Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip store sync cleanup #3489

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions .changeset/hip-pugs-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-mud": patch
---

Updated React ECS template with EntryKit for wallet support and a cleaned up app structure.
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { defineTable } from "@latticexyz/store/internal";
import { SyncStep } from "../SyncStep";
import { getSchemaPrimitives, getValueSchema } from "@latticexyz/protocol-parser/internal";

export enum SyncStep {
INITIALIZE = "initialize",
SNAPSHOT = "snapshot",
RPC = "rpc",
LIVE = "live",
}

export const SyncProgress = defineTable({
namespaceLabel: "syncToStash",
label: "SyncProgress",
Expand Down
2 changes: 1 addition & 1 deletion packages/store-sync/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from "./common";
export * from "./configToTables";
export * from "./createStoreSync";
export * from "./SyncStep";
export * from "./SyncProgress";
export * from "./isTableRegistrationLog";
export * from "./logToTable";
export * from "./tablesWithRecordsToLogs";
Expand Down
3 changes: 3 additions & 0 deletions packages/store-sync/src/recs/createSyncAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export function createSyncAdapter<const config extends StoreConfig>({
latestBlockNumber,
lastBlockNumberProcessed,
message,
__staticData: undefined,
__encodedLengths: undefined,
__dynamicData: undefined,
});

// when we switch to live, trigger update for all entities in all components
Expand Down
26 changes: 10 additions & 16 deletions packages/store-sync/src/recs/defineInternalComponents.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { World, defineComponent, Type, Component, Schema, Metadata } from "@latticexyz/recs";
import { World } from "@latticexyz/recs";
import { tablesToComponents } from "./tablesToComponents";
import { SyncProgress } from "../SyncProgress";

export type InternalComponents = ReturnType<typeof defineInternalComponents>;
export type InternalComponents = tablesToComponents<{
SyncProgress: typeof SyncProgress;
}>;

export function defineInternalComponents(world: World) {
return {
SyncProgress: defineComponent(
world,
{
step: Type.String,
message: Type.String,
percentage: Type.Number,
latestBlockNumber: Type.BigInt,
lastBlockNumberProcessed: Type.BigInt,
},
{ metadata: { componentName: "SyncProgress" } },
),
} as const satisfies Record<string, Component<Schema, Metadata>>;
export function defineInternalComponents(world: World): InternalComponents {
return tablesToComponents(world, {
SyncProgress,
});
}
3 changes: 3 additions & 0 deletions packages/store-sync/src/recs/syncToRecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export async function syncToRecs<const config extends StoreConfig, const extraTa
latestBlockNumber,
lastBlockNumberProcessed,
message,
__staticData: undefined,
__encodedLengths: undefined,
__dynamicData: undefined,
});

// when we switch to live, trigger update for all entities in all components
Expand Down
3 changes: 1 addition & 2 deletions packages/store-sync/src/stash/createSyncAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { getRecord, setRecord, registerTable, Stash } from "@latticexyz/stash/internal";
import { createStorageAdapter } from "./createStorageAdapter";
import { SyncStep } from "../SyncStep";
import { SyncAdapter } from "../common";
import { createStoreSync } from "../createStoreSync";
import { SyncProgress } from "./common";
import { SyncProgress, SyncStep } from "../SyncProgress";

export type CreateSyncAdapterOptions = { stash: Stash };

Expand Down
19 changes: 14 additions & 5 deletions templates/react-ecs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
.DS_Store
logs
*.log

node_modules

# mud artifacts
.mud
# sqlite indexer data
*.db
*.db-journal
.env.*

# foundry
cache
broadcast
out/*
!out/IWorld.sol
out/IWorld.sol/*
!out/IWorld.sol/IWorld.abi.json
!out/IWorld.sol/IWorld.abi.d.json.ts
10 changes: 9 additions & 1 deletion templates/react-ecs/mprocs.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
scrollback: 10000
procs:
client:
cwd: packages/client
shell: pnpm run dev
contracts:
cwd: packages/contracts
shell: pnpm mud dev-contracts --rpc http://127.0.0.1:8545
deploy-prereqs:
cwd: packages/contracts
shell: pnpm deploy-local-prereqs
env:
DEBUG: "mud:*"
# Anvil default account (0x70997970C51812dc3A010C7d01b50e0d17dc79C8)
PRIVATE_KEY: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
anvil:
cwd: packages/contracts
shell: anvil --base-fee 0 --block-time 2
shell: anvil --block-time 2
explorer:
cwd: packages/contracts
shell: pnpm explorer
10 changes: 8 additions & 2 deletions templates/react-ecs/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "mud-template-react-ecs",
"name": "mud-template-react",
"private": true,
"scripts": {
"build": "pnpm recursive run build",
Expand All @@ -17,7 +17,6 @@
"@latticexyz/explorer": "link:../../packages/explorer",
"@latticexyz/store-indexer": "link:../../packages/store-indexer",
"@types/debug": "4.1.7",
"@types/node": "^18",
"@typescript-eslint/eslint-plugin": "7.1.1",
"@typescript-eslint/parser": "7.1.1",
"eslint": "8.57.0",
Expand All @@ -28,5 +27,12 @@
"engines": {
"node": "^18",
"pnpm": "^8 || ^9"
},
"pnpm": {
"overrides": {
"@tanstack/react-query": "link:../../packages/entrykit/node_modules/@tanstack/react-query",
"@types/react": "link:../../packages/entrykit/node_modules/@types/react",
"wagmi": "link:../../packages/entrykit/node_modules/wagmi"
}
}
}
2 changes: 0 additions & 2 deletions templates/react-ecs/packages/client/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
node_modules
dist
.DS_Store
4 changes: 2 additions & 2 deletions templates/react-ecs/packages/client/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>a minimal MUD client</title>
<title>a MUD app</title>
</head>
<body>
<div id="react-root"></div>
Expand Down
25 changes: 16 additions & 9 deletions templates/react-ecs/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,39 @@
"type": "module",
"scripts": {
"build": "vite build",
"dev": "wait-port localhost:8545 && vite",
"dev": "vite",
"preview": "vite preview",
"test": "tsc --noEmit"
},
"dependencies": {
"@latticexyz/common": "link:../../../../packages/common",
"@latticexyz/dev-tools": "link:../../../../packages/dev-tools",
"@latticexyz/entrykit": "link:../../../../packages/entrykit",
"@latticexyz/explorer": "link:../../../../packages/explorer",
"@latticexyz/react": "link:../../../../packages/react",
"@latticexyz/recs": "link:../../../../packages/recs",
"@latticexyz/schema-type": "link:../../../../packages/schema-type",
"@latticexyz/store-sync": "link:../../../../packages/store-sync",
"@latticexyz/utils": "link:../../../../packages/utils",
"@latticexyz/world": "link:../../../../packages/world",
"@tanstack/react-query": "^5.63.0",
"contracts": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rxjs": "7.5.5",
"viem": "2.21.19"
"react": "18.2.0",
"react-dom": "18.2.0",
"react-error-boundary": "5.0.0",
"tailwind-merge": "^2.6.0",
"viem": "2.21.19",
"wagmi": "2.12.11"
},
"devDependencies": {
"@types/react": "18.2.22",
"@types/react-dom": "18.2.7",
"@vitejs/plugin-react": "^3.1.0",
"@vitejs/plugin-react": "^4.3.4",
"autoprefixer": "^10.4.20",
"eslint-plugin-react": "7.31.11",
"eslint-plugin-react-hooks": "4.6.0",
"vite": "^4.2.1",
"wait-port": "^1.0.4"
"postcss": "^8.4.49",
"tailwindcss": "^3.4.17",
"vite": "^6.0.7",
"vite-plugin-mud": "link:../../../../packages/vite-plugin-mud"
}
}
6 changes: 6 additions & 0 deletions templates/react-ecs/packages/client/postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
87 changes: 66 additions & 21 deletions templates/react-ecs/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,74 @@
import { useComponentValue } from "@latticexyz/react";
import { useMUD } from "./MUDContext";
import { singletonEntity } from "@latticexyz/store-sync/recs";
import { AccountButton } from "@latticexyz/entrykit/internal";
import { Direction, Entity } from "./common";
import mudConfig from "contracts/mud.config";
import { useMemo } from "react";
import { GameMap } from "./game/GameMap";
import { useWorldContract } from "./mud/useWorldContract";
import { Synced } from "./mud/Synced";
import { useSync } from "@latticexyz/store-sync/react";
import { components } from "./mud/recs";
import { useEntityQuery } from "@latticexyz/react";
import { Has, getComponentValueStrict } from "@latticexyz/recs";
import { Address } from "viem";

export const App = () => {
const {
components: { Counter },
systemCalls: { increment },
} = useMUD();
export function App() {
const playerEntities = useEntityQuery([Has(components.Owner), Has(components.Position)]);
const players = useMemo(
() =>
playerEntities.map((entity) => {
const owner = getComponentValueStrict(components.Owner, entity);
const position = getComponentValueStrict(components.Position, entity);
return {
entity: entity as Entity,
owner: owner.owner as Address,
x: position.x,
y: position.y,
};
}),
[playerEntities],
);

const sync = useSync();
const worldContract = useWorldContract();

const onMove = useMemo(
() =>
sync.data && worldContract
? async (entity: Entity, direction: Direction) => {
const tx = await worldContract.write.app__move([entity, mudConfig.enums.Direction.indexOf(direction)]);
await sync.data.waitForTransaction(tx);
}
: undefined,
[sync.data, worldContract],
);

const counter = useComponentValue(Counter, singletonEntity);
const onSpawn = useMemo(
() =>
sync.data && worldContract
? async () => {
const tx = await worldContract.write.app__spawn();
await sync.data.waitForTransaction(tx);
}
: undefined,
[sync.data, worldContract],
);

return (
<>
<div>
Counter: <span>{counter?.value ?? "??"}</span>
<div className="fixed inset-0 grid place-items-center p-4">
<Synced
fallback={({ message, percentage }) => (
<div className="tabular-nums">
{message} ({percentage.toFixed(1)}%)…
</div>
)}
>
<GameMap players={players} onMove={onMove} onSpawn={onSpawn} />
</Synced>
</div>
<div className="fixed top-2 right-2">
<AccountButton />
</div>
<button
type="button"
onClick={async (event) => {
event.preventDefault();
console.log("new counter value:", await increment());
}}
>
Increment
</button>
</>
);
};
}
21 changes: 0 additions & 21 deletions templates/react-ecs/packages/client/src/MUDContext.tsx

This file was deleted.

29 changes: 29 additions & 0 deletions templates/react-ecs/packages/client/src/Providers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { WagmiProvider } from "wagmi";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
import { ReactNode } from "react";
import { SyncProvider } from "@latticexyz/store-sync/react";
import { defineConfig, EntryKitProvider } from "@latticexyz/entrykit/internal";
import { wagmiConfig } from "./wagmiConfig";
import { chainId, getWorldAddress, startBlock } from "./common";
import { syncAdapter } from "./mud/recs";

const queryClient = new QueryClient();

export type Props = {
children: ReactNode;
};

export function Providers({ children }: Props) {
const worldAddress = getWorldAddress();
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<EntryKitProvider config={defineConfig({ chainId, worldAddress })}>
<SyncProvider chainId={chainId} address={worldAddress} startBlock={startBlock} adapter={syncAdapter}>
{children}
</SyncProvider>
</EntryKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
Loading
Loading