Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/lib/client/project/save-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,58 @@ export class SaveHandler {
components: [],
systems: [],
});
private _readyToSync = true;
private _syncTimer?: Timer;
private _needSync: Writable<boolean> = writable(false);

constructor(project: Project) {
this._project = project;
}

async init() {
await this.fetchFromServer();
this._save.subscribe(() => {
this.syncToServer();
});
}

async fetchFromServer() {
this._save.set(await this._project.actions.save.get());
}

async syncToServer() {
Comment thread
Tchips46 marked this conversation as resolved.
Comment thread
Tchips46 marked this conversation as resolved.
if (this._readyToSync) {
this._readyToSync = false;

await this._project.actions.save.set({ save: get(this._save) });

this._syncTimer = setTimeout(() => {
this._readyToSync = true;
if (get(this._needSync)) {
this.syncToServer();
this._needSync.set(false);
}
}, 5000);
} else {
this._needSync.set(true);
}
}

async forceSyncToServer() {
this._readyToSync = true;
this._needSync.set(false);
clearTimeout(this._syncTimer);
await this.syncToServer();
}

get save(): Save {
return get(this._save);
}

get needSync(): Writable<boolean> {
return this._needSync;
}

addComponent(component: SaveComponent) {
get(this._save).components.push(component);
}
Expand Down
15 changes: 0 additions & 15 deletions src/lib/components/Widget/EditorGame/game.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
import { SvelteMap } from 'svelte/reactivity';
import { type Writable, writable } from 'svelte/store';

import type { Save } from '@utils/types';

export const mainModule: Writable<any> = writable(undefined);
export const env: Writable<Record<string, string>> = writable({});
export const files: Writable<Map<string, string>> = writable(new SvelteMap());
export const save: Writable<Save> = writable({
libraries: [],
entities: [],
components: [],
systems: [],
});

export enum GameState {
INIT_STATE = 0,
RELOAD_FROM_SERVER,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script lang="ts">
import { createDefaultComponent } from '$lib/components/Widget/EntityInspector/component-creator';
import {
selectedEntityId,
selectedEntity,
Expand All @@ -12,6 +11,7 @@

let openMap: { [key: string]: boolean } = $state({});
let openComponentSelector: boolean = $state(false);
let refresh: number = $state(0);

const { event, packages, save } = useProject();

Expand All @@ -22,106 +22,126 @@
});

async function addComponent(componentName: string) {
const [name, params] = await createDefaultComponent(componentName);
if ($selectedEntity) {
$selectedEntity.components[name] = params;
const componentManifest = packages.getComponentManifest(componentName);
if (!componentManifest) {
throw new Error(`Can't create ${componentName}: manifest not found`);
}
save.addComponentToEntity($selectedEntityId, componentName, componentManifest);
await save.forceSyncToServer();
refresh++;
}

async function removeComponent(componentName: string) {
if (!$selectedEntity) return;
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete $selectedEntity.components[componentName];
await save.forceSyncToServer();
refresh++;
}

function hotReloadAndSync() {
save.syncToServer();
event.emit(CoreEvents.HOT_RELOAD);
}
</script>

<div class="h-full w-full overflow-y-scroll bg-neutral-900 py-1 text-md">
{#if $selectedEntity}
<div class="mb-1">
<div
class="w-full flex items-center bg-neutral-800 px-2 py-1 font-semibold text-neutral-300 text-lg"
>
{$selectedEntity.id}
</div>
{#key refresh}
{#if $selectedEntity}
<div class="mb-1">
<div
class="w-full flex items-center bg-neutral-800 px-2 py-1 font-semibold text-neutral-300 text-lg"
>
{$selectedEntity.id}
<span
class="i-ic-round-refresh ml-auto text-2xl cursor-pointer"
aria-hidden="true"
onclick={(e) => {
e.stopPropagation();
refresh++;
}}
></span>
</div>

<div class="my-2">
{#each Object.keys($selectedEntity.components) as componentName (componentName)}
<button
class="text-neutral-200 text-md px-4 w-full flex cursor-pointer items-center gap-1 bg-neutral-800 px-2 py-1 font-semibold text-neutral-300 text-sm"
onclick={() => (openMap[componentName] = !openMap[componentName])}
>
<span
aria-hidden="true"
class="{!openMap[componentName]
? 'i-solar-alt-arrow-down-bold'
: 'i-solar-alt-arrow-right-bold'}
<div class="my-2">
{#each Object.keys($selectedEntity.components) as componentName (componentName)}
<button
class="text-neutral-200 text-md px-4 w-full flex cursor-pointer items-center gap-1 bg-neutral-800 px-2 py-1 font-semibold text-neutral-300 text-sm"
onclick={() => (openMap[componentName] = !openMap[componentName])}
>
<span
aria-hidden="true"
class="{!openMap[componentName]
? 'i-solar-alt-arrow-down-bold'
: 'i-solar-alt-arrow-right-bold'}
w-4 text-center select-none text-neutral-400 hover:text-neutral-200"
></span>
<span class="i-ic-baseline-token text-neutral-400"></span>
{componentName}
<span
class="i-solar-trash-bin-minimalistic-linear text-red-500 text-align-end"
aria-hidden="true"
onclick={(e) => {
e.stopPropagation();
removeComponent(componentName);
}}
></span>
</button>
{#if !openMap[componentName]}
{#each packages.getComponentManifest(componentName)?.params as param (param.name)}
<div class="grid grid-cols-[140px_1fr] m-2 mb-1 items-center gap-2">
<div class="text-neutral-200 text-sm">{param.name}</div>
></span>
<span class="i-ic-baseline-token text-neutral-400"></span>
{componentName}
<span
class="i-solar-trash-bin-minimalistic-linear text-red-500 text-align-end"
aria-hidden="true"
onclick={(e) => {
e.stopPropagation();
removeComponent(componentName);
}}
></span>
</button>
{#if !openMap[componentName]}
{#each packages.getComponentManifest(componentName)?.params as param (param.name)}
<div class="grid grid-cols-[140px_1fr] m-2 mb-1 items-center gap-2">
<div class="text-neutral-200 text-sm">{param.name}</div>

{#if param.type === 'string'}
<Input
type="text"
bind:value={$selectedEntity.components[componentName][param.name]}
onchange={() => event.emit(CoreEvents.HOT_RELOAD)}
/>
{:else if param.type === 'number'}
<Input
type="number"
bind:value={$selectedEntity.components[componentName][param.name]}
onchange={() => event.emit(CoreEvents.HOT_RELOAD)}
/>
{:else if param.type === 'boolean'}
<TristateSwitch
bind:value={$selectedEntity.components[componentName][param.name]}
onChange={() => event.emit(CoreEvents.HOT_RELOAD)}
/>
{/if}
</div>
{/each}
{/if}
{/each}
{#if param.type === 'string'}
<Input
type="text"
bind:value={$selectedEntity.components[componentName][param.name]}
onchange={hotReloadAndSync}
/>
{:else if param.type === 'number'}
<Input
type="number"
bind:value={$selectedEntity.components[componentName][param.name]}
onchange={hotReloadAndSync}
/>
{:else if param.type === 'boolean'}
<TristateSwitch
bind:value={$selectedEntity.components[componentName][param.name]}
onChange={hotReloadAndSync}
/>
{/if}
</div>
{/each}
{/if}
{/each}
</div>
</div>
</div>
<div class="mx-4 my-4 flex justify-center">
<button
class="h-10 w-full cursor-pointer rounded-md gap-2 bg-neutral-800 font-semibold hover:bg-neutral-700"
onclick={() => {
openComponentSelector = true;
<div class="mx-4 my-4 flex justify-center">
<button
class="h-10 w-full cursor-pointer rounded-md gap-2 bg-neutral-800 font-semibold hover:bg-neutral-700"
onclick={() => {
openComponentSelector = true;
}}
>
<span class="i-solar-add-circle-bold text-lg"> </span>
Add component
</button>
</div>
<ComponentSelector
availableComponents={save.save.components.filter(
(c) => !Object.keys($selectedEntity?.components ?? {}).includes(c.name),
)}
open={openComponentSelector}
onClose={() => (openComponentSelector = false)}
onSelect={(c) => {
addComponent(c.name);
openComponentSelector = false;
}}
>
<span class="i-solar-add-circle-bold text-lg"> </span>
Add component
</button>
</div>
<ComponentSelector
availableComponents={save.save.components.filter(
(c) => !Object.keys($selectedEntity?.components ?? {}).includes(c.name),
)}
open={openComponentSelector}
onClose={() => (openComponentSelector = false)}
onSelect={(c) => {
addComponent(c.name);
openComponentSelector = false;
}}
/>
{:else}
<div class="h-full w-full flex items-center justify-center text-2xl text-align-center">
Select an entity in the left panel
</div>
{/if}
/>
{:else}
<div class="h-full w-full flex items-center justify-center text-2xl text-align-center">
Select an entity in the left panel
</div>
{/if}
{/key}
</div>
26 changes: 0 additions & 26 deletions src/lib/components/Widget/EntityInspector/component-creator.ts

This file was deleted.

Loading