Skip to content

Commit

Permalink
feat(recs,store-sync): skip update stream when hydrating from initial…
Browse files Browse the repository at this point in the history
… snapshot
  • Loading branch information
holic committed Sep 29, 2023
1 parent d382a59 commit 64d3dea
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 12 deletions.
26 changes: 19 additions & 7 deletions packages/recs/src/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import {
import { isFullComponentValue, isIndexer } from "./utils";
import { getEntityString, getEntitySymbol } from "./Entity";

export type ComponentMutationOptions = {
/** Skip publishing this mutation to the component's update stream. Mostly used internally during initial hydration. */
skipUpdateStream?: boolean;
};

function getComponentName(component: Component<any, any, any>) {
return (
component.metadata?.componentName ??
Expand Down Expand Up @@ -80,7 +85,8 @@ export function defineComponent<S extends Schema, M extends Metadata, T = unknow
export function setComponent<S extends Schema, T = unknown>(
component: Component<S, Metadata, T>,
entity: Entity,
value: ComponentValue<S, T>
value: ComponentValue<S, T>,
options: ComponentMutationOptions = {}
) {
const entitySymbol = getEntitySymbol(entity);
const prevValue = getComponentValue(component, entity);
Expand Down Expand Up @@ -109,7 +115,9 @@ export function setComponent<S extends Schema, T = unknown>(
}
}
}
component.update$.next({ entity, value: [value, prevValue], component });
if (!options.skipUpdateStream) {
component.update$.next({ entity, value: [value, prevValue], component });
}
}

/**
Expand All @@ -132,16 +140,17 @@ export function updateComponent<S extends Schema, T = unknown>(
component: Component<S, Metadata, T>,
entity: Entity,
value: Partial<ComponentValue<S, T>>,
initialValue?: ComponentValue<S, T>
initialValue?: ComponentValue<S, T>,
options: ComponentMutationOptions = {}
) {
const currentValue = getComponentValue(component, entity);
if (currentValue === undefined) {
if (initialValue === undefined) {
throw new Error(`Can't update component ${getComponentName(component)} without a current value or initial value`);
}
setComponent(component, entity, { ...initialValue, ...value });
setComponent(component, entity, { ...initialValue, ...value }, options);
} else {
setComponent(component, entity, { ...currentValue, ...value });
setComponent(component, entity, { ...currentValue, ...value }, options);
}
}

Expand All @@ -153,14 +162,17 @@ export function updateComponent<S extends Schema, T = unknown>(
*/
export function removeComponent<S extends Schema, M extends Metadata, T = unknown>(
component: Component<S, M, T>,
entity: Entity
entity: Entity,
options: ComponentMutationOptions = {}
) {
const entitySymbol = getEntitySymbol(entity);
const prevValue = getComponentValue(component, entity);
for (const key of Object.keys(component.values)) {
component.values[key].delete(entitySymbol);
}
component.update$.next({ entity, value: [undefined, prevValue], component });
if (!options.skipUpdateStream) {
component.update$.next({ entity, value: [undefined, prevValue], component });
}
}

/**
Expand Down
18 changes: 14 additions & 4 deletions packages/store-sync/src/recs/recsStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import worldConfig from "@latticexyz/world/mud.config";
export type RecsStorageOptions<TConfig extends StoreConfig = StoreConfig> = {
world: RecsWorld;
config: TConfig;
shouldSkipUpdateStream?: () => boolean;
};

export type RecsStorageAdapter<TConfig extends StoreConfig = StoreConfig> = {
Expand All @@ -38,6 +39,7 @@ export type RecsStorageAdapter<TConfig extends StoreConfig = StoreConfig> = {
export function recsStorage<TConfig extends StoreConfig = StoreConfig>({
world,
config,
shouldSkipUpdateStream,
}: RecsStorageOptions<TConfig>): RecsStorageAdapter<TConfig> {
world.registerEntity({ id: singletonEntity });

Expand All @@ -52,7 +54,12 @@ export function recsStorage<TConfig extends StoreConfig = StoreConfig>({
async registerTables({ tables }) {
for (const table of tables) {
// TODO: check if table exists already and skip/warn?
setComponent(components.RegisteredTables, getTableEntity(table), { table });
setComponent(
components.RegisteredTables,
getTableEntity(table),
{ table },
{ skipUpdateStream: shouldSkipUpdateStream?.() }
);
}
},
async getTables({ tables }) {
Expand Down Expand Up @@ -87,7 +94,9 @@ export function recsStorage<TConfig extends StoreConfig = StoreConfig>({

if (operation.type === "SetRecord") {
debug("setting component", tableId, entity, operation.value);
setComponent(component, entity, operation.value as ComponentValue);
setComponent(component, entity, operation.value as ComponentValue, {
skipUpdateStream: shouldSkipUpdateStream?.(),
});
} else if (operation.type === "SetField") {
debug("updating component", tableId, entity, {
[operation.fieldName]: operation.fieldValue,
Expand All @@ -96,11 +105,12 @@ export function recsStorage<TConfig extends StoreConfig = StoreConfig>({
component,
entity,
{ [operation.fieldName]: operation.fieldValue } as ComponentValue,
schemaToDefaults(table.valueSchema) as ComponentValue
schemaToDefaults(table.valueSchema) as ComponentValue,
{ skipUpdateStream: shouldSkipUpdateStream?.() }
);
} else if (operation.type === "DeleteRecord") {
debug("deleting component", tableId, entity);
removeComponent(component, entity);
removeComponent(component, entity, { skipUpdateStream: shouldSkipUpdateStream?.() });
}
}
},
Expand Down
7 changes: 6 additions & 1 deletion packages/store-sync/src/recs/syncToRecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ export async function syncToRecs<TConfig extends StoreConfig = StoreConfig>({
tableIds,
matchId,
}: SyncToRecsOptions<TConfig>): Promise<SyncToRecsResult<TConfig>> {
const { storageAdapter, components } = recsStorage({ world, config });
const { storageAdapter, components } = recsStorage({
world,
config,
shouldSkipUpdateStream: (): boolean =>
getComponentValue(components.SyncProgress, singletonEntity)?.step !== SyncStep.LIVE,
});

const storeSync = await createStoreSync({
storageAdapter,
Expand Down

0 comments on commit 64d3dea

Please sign in to comment.