Skip to content

Commit

Permalink
add hook
Browse files Browse the repository at this point in the history
  • Loading branch information
dhvanipa committed Nov 7, 2023
1 parent aa54d1e commit 2024ef1
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 27 deletions.
8 changes: 6 additions & 2 deletions packages/recs/src/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ export function setComponent<S extends Schema, T = unknown>(
}
}
component.update$.next({ entity, value: [value, prevValue], component });
const updateValue = { entity, value: [value, prevValue], component };
return updateValue;
}

/**
Expand Down Expand Up @@ -139,9 +141,9 @@ export function updateComponent<S extends Schema, T = unknown>(
if (initialValue === undefined) {
throw new Error(`Can't update component ${getComponentName(component)} without a current value or initial value`);
}
setComponent(component, entity, { ...initialValue, ...value });
return setComponent(component, entity, { ...initialValue, ...value });
} else {
setComponent(component, entity, { ...currentValue, ...value });
return setComponent(component, entity, { ...currentValue, ...value });
}
}

Expand All @@ -161,6 +163,8 @@ export function removeComponent<S extends Schema, M extends Metadata, T = unknow
component.values[key].delete(entitySymbol);
}
component.update$.next({ entity, value: [undefined, prevValue], component });
const updateValue = { entity, value: [undefined, prevValue], component };
return updateValue;
}

/**
Expand Down
87 changes: 69 additions & 18 deletions packages/store-sync/src/recs/recsStorage.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
import { BlockLogsToStorageOptions } from "../blockLogsToStorage";
import { StoreConfig } from "@latticexyz/store";
import { debug } from "./debug";
import { tableIdToHex } from "@latticexyz/common";
import { isDefined } from "@latticexyz/common/utils";
import {
ComponentUpdate,
ComponentValue,
Entity,
Component as RecsComponent,
Schema as RecsSchema,
getComponentValue,
removeComponent,
setComponent,
updateComponent,
} from "@latticexyz/recs";
import { isDefined } from "@latticexyz/common/utils";
import { StoreConfig } from "@latticexyz/store";
import { BlockLogsToStorageOptions } from "../blockLogsToStorage";
import { BlockLogs } from "../common";
import { schemaToDefaults } from "../schemaToDefaults";
import { defineInternalComponents } from "./defineInternalComponents";
import { getTableEntity } from "./getTableEntity";
import { StoreComponentMetadata } from "./common";
import { tableIdToHex } from "@latticexyz/common";
import { debug } from "./debug";
import { defineInternalComponents } from "./defineInternalComponents";
import { encodeEntity } from "./encodeEntity";
import { getTableEntity } from "./getTableEntity";

export type ComponentEntityUpdates = Map<string, Map<Entity, ComponentUpdate[]>>;

export function recsStorage<TConfig extends StoreConfig = StoreConfig>({
components,
}: {
components: ReturnType<typeof defineInternalComponents> &
Record<string, RecsComponent<RecsSchema, StoreComponentMetadata>>;
config?: TConfig;
}): BlockLogsToStorageOptions<TConfig> {
export type RecsUpdatesHook = (
blockNumber: BlockLogs["blockNumber"],
componentEntityUpdates: ComponentEntityUpdates
) => Promise<void>;

export function recsStorage<TConfig extends StoreConfig = StoreConfig>(
{
components,
address,
}: {
components: ReturnType<typeof defineInternalComponents> &
Record<string, RecsComponent<RecsSchema, StoreComponentMetadata>>;
config?: TConfig;
address: `0x${string}` | undefined;
},
recsAllUpdatesHook?: RecsUpdatesHook
): BlockLogsToStorageOptions<TConfig> {
// TODO: do we need to store block number?

const componentsByTableId = Object.fromEntries(
Expand All @@ -44,7 +59,9 @@ export function recsStorage<TConfig extends StoreConfig = StoreConfig>({
.map((table) => getComponentValue(components.RegisteredTables, getTableEntity(table))?.table)
.filter(isDefined);
},
async storeOperations({ operations }) {
async storeOperations({ blockNumber, operations }) {
const componentEntityUpdates: ComponentEntityUpdates = new Map<string, Map<Entity, ComponentUpdate[]>>();
console.log("processing updates for world", blockNumber, address);
for (const operation of operations) {
const table = getComponentValue(
components.RegisteredTables,
Expand All @@ -68,24 +85,58 @@ export function recsStorage<TConfig extends StoreConfig = StoreConfig>({

const entity = encodeEntity(table.keySchema, operation.key);

let newUpdate = undefined;
if (operation.type === "SetRecord") {
debug("setting component", tableId, entity, operation.value);
setComponent(component, entity, operation.value as ComponentValue);
newUpdate = setComponent(component, entity, operation.value as ComponentValue);
} else if (operation.type === "SetField") {
debug("updating component", tableId, entity, {
[operation.fieldName]: operation.fieldValue,
});
updateComponent(
newUpdate = updateComponent(
component,
entity,
{ [operation.fieldName]: operation.fieldValue } as ComponentValue,
schemaToDefaults(table.valueSchema) as ComponentValue
);
} else if (operation.type === "DeleteRecord") {
debug("deleting component", tableId, entity);
removeComponent(component, entity);
newUpdate = removeComponent(component, entity);
}

if (newUpdate) {
// console.log("component updates");
if (componentEntityUpdates.has(newUpdate.component.id)) {
// JS reference so don't need to set again
if (componentEntityUpdates.get(newUpdate.component.id)?.has(newUpdate.entity)) {
componentEntityUpdates.get(newUpdate.component.id)?.get(newUpdate.entity)?.push(newUpdate);
} else {
componentEntityUpdates.get(newUpdate.component.id)?.set(newUpdate.entity, [newUpdate]);
}
} else {
const entityUpdates = new Map<Entity, ComponentUpdate[]>();
entityUpdates.set(newUpdate.entity, [newUpdate]);
componentEntityUpdates.set(newUpdate.component.id, entityUpdates);
}
}
}

// console.log("end of tx");
// console.log("blockNumber", blockNumber);
// componentEntityUpdates.forEach((entityUpdates, component) => {
// console.log('component', component);
// entityUpdates.forEach((updates, entity) => {
// console.log('entity', entity);
// updates.forEach((update) => {
// console.log(update.value);
// });
// });
// console.log("done");
// });
if (recsAllUpdatesHook !== undefined) {
console.log("calling hook for world", blockNumber, address);
await recsAllUpdatesHook(blockNumber, componentEntityUpdates);
}
},
} as BlockLogsToStorageOptions<TConfig>;
}
18 changes: 11 additions & 7 deletions packages/store-sync/src/recs/syncToRecs.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { StoreConfig } from "@latticexyz/store";
import { World as RecsWorld, getComponentValue, setComponent } from "@latticexyz/recs";
import { StoreConfig } from "@latticexyz/store";
import storeConfig from "@latticexyz/store/mud.config.js";
import worldConfig from "@latticexyz/world/mud.config.js";
import { SyncStep } from "../SyncStep";
import { SyncOptions, SyncResult } from "../common";
import { recsStorage } from "./recsStorage";
import { defineInternalComponents } from "./defineInternalComponents";
import { createStoreSync } from "../createStoreSync";
import { ConfigToRecsComponents } from "./common";
import storeConfig from "@latticexyz/store/mud.config.js";
import worldConfig from "@latticexyz/world/mud.config.js";
import { configToRecsComponents } from "./configToRecsComponents";
import { defineInternalComponents } from "./defineInternalComponents";
import { RecsUpdatesHook, recsStorage } from "./recsStorage";
import { singletonEntity } from "./singletonEntity";
import { SyncStep } from "../SyncStep";

type SyncToRecsOptions<TConfig extends StoreConfig = StoreConfig> = SyncOptions<TConfig> & {
world: RecsWorld;
config: TConfig;
startSync?: boolean;
recsAllUpdatesHook?: RecsUpdatesHook;
};

type SyncToRecsResult<TConfig extends StoreConfig = StoreConfig> = SyncResult<TConfig> & {
Expand All @@ -34,6 +35,7 @@ export async function syncToRecs<TConfig extends StoreConfig = StoreConfig>({
maxBlockRange,
initialState,
indexerUrl,
recsAllUpdatesHook,
startSync = true,
}: SyncToRecsOptions<TConfig>): Promise<SyncToRecsResult<TConfig>> {
const components = {
Expand All @@ -45,8 +47,10 @@ export async function syncToRecs<TConfig extends StoreConfig = StoreConfig>({

world.registerEntity({ id: singletonEntity });

console.log("syncWithRecs", startBlock, address);

const storeSync = await createStoreSync({
storageAdapter: recsStorage({ components, config }),
storageAdapter: recsStorage({ components, config, address }, recsAllUpdatesHook),
config,
address,
publicClient,
Expand Down

0 comments on commit 2024ef1

Please sign in to comment.