diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ba94db3c..70770b67 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,7 +20,7 @@ jobs: run: git submodule update --init --recursive - run: curl -L https://install.dojoengine.org | bash - - run: /home/runner/.config/.dojo/bin/dojoup -v v1.0.0-alpha.6 + - run: /home/runner/.config/.dojo/bin/dojoup -v v1.0.0-alpha.9 - run: | cd examples/dojo/dojo-starter /home/runner/.config/.dojo/bin/sozo build diff --git a/examples/clients/react/react-app/src/App.tsx b/examples/clients/react/react-app/src/App.tsx index 2b302ff0..07802e5d 100644 --- a/examples/clients/react/react-app/src/App.tsx +++ b/examples/clients/react/react-app/src/App.tsx @@ -2,7 +2,6 @@ import "./App.css"; import { useComponentValue, useQuerySync } from "@dojoengine/react"; import { Entity } from "@dojoengine/recs"; import { useEffect, useState } from "react"; -import { Direction } from "./utils"; import { getEntityIdFromKeys } from "@dojoengine/utils"; import { useDojo } from "./dojo/useDojo"; @@ -17,6 +16,7 @@ function App() { account, } = useDojo(); + // sync entities useQuerySync(toriiClient, contractComponents as any, []); const [clipboardStatus, setClipboardStatus] = useState({ @@ -139,7 +139,7 @@ function App() {
diff --git a/examples/clients/react/react-app/src/dojo/createSystemCalls.ts b/examples/clients/react/react-app/src/dojo/createSystemCalls.ts index bd72864c..46f699b4 100644 --- a/examples/clients/react/react-app/src/dojo/createSystemCalls.ts +++ b/examples/clients/react/react-app/src/dojo/createSystemCalls.ts @@ -1,4 +1,4 @@ -import { Account, AccountInterface } from "starknet"; +import { Account } from "starknet"; import { Entity, Has, @@ -9,9 +9,9 @@ import { } from "@dojoengine/recs"; import { uuid } from "@latticexyz/utils"; import { ClientComponents } from "./createClientComponents"; -import { Direction, updatePositionWithDirection } from "../utils"; import { getEntityIdFromKeys } from "@dojoengine/utils"; import type { IWorld } from "./typescript/contracts.gen"; +import { Direction } from "./typescript/models.gen"; export type SystemCalls = ReturnType; @@ -77,38 +77,10 @@ export function createSystemCalls( }; const move = async (account: Account, direction: Direction) => { - const entityId = getEntityIdFromKeys([ - BigInt(account.address), - ]) as Entity; - - // Update the state before the transaction - // const positionId = uuid(); - // Position.addOverride(positionId, { - // entity: entityId, - // value: { - // player: BigInt(entityId), - // vec: updatePositionWithDirection( - // direction, - // getComponentValue(Position, entityId) as any - // ).vec, - // }, - // }); - - // // Update the state before the transaction - // const movesId = uuid(); - // Moves.addOverride(movesId, { - // entity: entityId, - // value: { - // player: BigInt(entityId), - // remaining: - // (getComponentValue(Moves, entityId)?.remaining || 0) - 1, - // }, - // }); - try { await client.actions.move({ account, - direction: { type: "Left" }, + direction, }); // Wait for the indexer to update the entity @@ -127,11 +99,6 @@ export function createSystemCalls( }); } catch (e) { console.log(e); - // Position.removeOverride(positionId); - // Moves.removeOverride(movesId); - } finally { - // Position.removeOverride(positionId); - // Moves.removeOverride(movesId); } }; diff --git a/examples/clients/react/react-app/src/dojo/setup.ts b/examples/clients/react/react-app/src/dojo/setup.ts index e545428d..0ad7139c 100644 --- a/examples/clients/react/react-app/src/dojo/setup.ts +++ b/examples/clients/react/react-app/src/dojo/setup.ts @@ -7,7 +7,7 @@ import { world } from "./world"; import { setupWorld } from "./typescript/contracts.gen"; import { Account, ArraySignatureType } from "starknet"; import { BurnerManager } from "@dojoengine/create-burner"; -import { getSyncEvents } from "@dojoengine/state"; +import { getSyncEvents, getSyncEntities } from "@dojoengine/state"; export type SetupResult = Awaited>; @@ -29,6 +29,7 @@ export async function setup({ ...config }: DojoConfig) { // create dojo provider const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); + // Sync all events const eventSync = getSyncEvents( toriiClient, contractComponents as any, @@ -36,6 +37,13 @@ export async function setup({ ...config }: DojoConfig) { [] ); + // Sync all entities + const sync = await getSyncEntities( + toriiClient, + contractComponents as any, + [] + ); + // setup world const client = await setupWorld(dojoProvider); @@ -75,5 +83,6 @@ export async function setup({ ...config }: DojoConfig) { burnerManager, toriiClient, eventSync, + sync, }; } diff --git a/examples/clients/react/react-app/src/dojo/typescript/contracts.gen.ts b/examples/clients/react/react-app/src/dojo/typescript/contracts.gen.ts index 0f7fac11..411d0500 100644 --- a/examples/clients/react/react-app/src/dojo/typescript/contracts.gen.ts +++ b/examples/clients/react/react-app/src/dojo/typescript/contracts.gen.ts @@ -1,7 +1,7 @@ // Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. // Import the necessary types from the recs SDK // generate again with `sozo build --typescript` -import { Account, byteArray } from "starknet"; +import { Account } from "starknet"; import { DojoProvider } from "@dojoengine/core"; import * as models from "./models.gen"; diff --git a/examples/clients/react/react-app/tsconfig.json b/examples/clients/react/react-app/tsconfig.json index 7f1be29a..1da7add7 100644 --- a/examples/clients/react/react-app/tsconfig.json +++ b/examples/clients/react/react-app/tsconfig.json @@ -1,21 +1,7 @@ { + "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "node", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "strict": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "allowSyntheticDefaultImports": true + "jsx": "react-jsx" }, "include": ["src", "dojoConfig.ts"], "references": [ diff --git a/examples/clients/react/react-phaser-example/src/dojo/contractComponents.ts b/examples/clients/react/react-phaser-example/src/dojo/contractComponents.ts deleted file mode 100644 index 12430e19..00000000 --- a/examples/clients/react/react-phaser-example/src/dojo/contractComponents.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ - -import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; - -export function defineContractComponents(world: World) { - return { - Moves: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - remaining: RecsType.Number, - last_direction: RecsType.Number, - }, - { - metadata: { - name: "Moves", - types: ["contractaddress", "u8", "enum"], - customTypes: ["Direction"], - }, - } - ); - })(), - Position: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - vec: { x: RecsType.Number, y: RecsType.Number }, - }, - { - metadata: { - name: "Position", - types: ["contractaddress", "u32", "u32"], - customTypes: ["Vec2"], - }, - } - ); - })(), - }; -} diff --git a/examples/clients/react/react-phaser-example/src/dojo/createClientComponents.ts b/examples/clients/react/react-phaser-example/src/dojo/createClientComponents.ts index 550ad13b..7b5bb860 100644 --- a/examples/clients/react/react-phaser-example/src/dojo/createClientComponents.ts +++ b/examples/clients/react/react-phaser-example/src/dojo/createClientComponents.ts @@ -1,5 +1,5 @@ import { overridableComponent } from "@dojoengine/recs"; -import { ContractComponents } from "./generated/contractComponents"; +import { ContractComponents } from "./typescript/models.gen"; export type ClientComponents = ReturnType; diff --git a/examples/clients/react/react-phaser-example/src/dojo/createNetworkLayer.ts b/examples/clients/react/react-phaser-example/src/dojo/createNetworkLayer.ts index 3ad74434..1290ade5 100644 --- a/examples/clients/react/react-phaser-example/src/dojo/createNetworkLayer.ts +++ b/examples/clients/react/react-phaser-example/src/dojo/createNetworkLayer.ts @@ -1,8 +1,9 @@ import { world as recsWorld } from "./world"; -import { setup } from "./generated/setup"; + import { dojoConfig } from "../../dojoConfig"; import { createBurner } from "./createBurner"; import { Account } from "starknet"; +import { setup } from "./setup"; export type NetworkLayer = Awaited>; diff --git a/examples/clients/react/react-phaser-example/src/dojo/createSystemCalls.ts b/examples/clients/react/react-phaser-example/src/dojo/createSystemCalls.ts index b1f71a0a..38475f37 100644 --- a/examples/clients/react/react-phaser-example/src/dojo/createSystemCalls.ts +++ b/examples/clients/react/react-phaser-example/src/dojo/createSystemCalls.ts @@ -1,32 +1,102 @@ -import { AccountInterface } from "starknet"; +import { Account, AccountInterface } from "starknet"; +import { + Entity, + Has, + HasValue, + World, + defineSystem, + getComponentValue, +} from "@dojoengine/recs"; +import { uuid } from "@latticexyz/utils"; import { ClientComponents } from "./createClientComponents"; -import { Direction } from "./utils"; -import { ContractComponents } from "./generated/contractComponents"; -import type { IWorld } from "./generated/generated"; +import { getEntityIdFromKeys } from "@dojoengine/utils"; +import type { IWorld } from "./typescript/contracts.gen"; +import { Direction } from "./typescript/models.gen"; export type SystemCalls = ReturnType; export function createSystemCalls( { client }: { client: IWorld }, - contractComponents: ContractComponents, - { Position, Moves }: ClientComponents + { Position, Moves }: ClientComponents, + world: World ) { - const spawn = async (account: AccountInterface) => { + const spawn = async (account: Account) => { + const entityId = getEntityIdFromKeys([ + BigInt(account.address), + ]) as Entity; + + const movesId = uuid(); + Moves.addOverride(movesId, { + entity: entityId, + value: { + player: BigInt(entityId), + remaining: + (getComponentValue(Moves, entityId)?.remaining || 0) + 100, + }, + }); + + const positionId = uuid(); + Position.addOverride(positionId, { + entity: entityId, + value: { + player: BigInt(entityId), + vec: { + x: 10 + (getComponentValue(Position, entityId)?.vec.x || 0), + y: 10 + (getComponentValue(Position, entityId)?.vec.y || 0), + }, + }, + }); + try { await client.actions.spawn({ account, }); + + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); + Position.removeOverride(positionId); + Moves.removeOverride(movesId); + } finally { + Position.removeOverride(positionId); + Moves.removeOverride(movesId); } }; - const move = async (account: AccountInterface, direction: Direction) => { + const move = async (account: Account, direction: Direction) => { try { await client.actions.move({ account, direction, }); + + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); } diff --git a/examples/clients/react/react-phaser-example/src/dojo/generated/contractComponents.ts b/examples/clients/react/react-phaser-example/src/dojo/generated/contractComponents.ts deleted file mode 100644 index 596871c8..00000000 --- a/examples/clients/react/react-phaser-example/src/dojo/generated/contractComponents.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Autogenerated file. Do not edit manually. - * Generated using @dojoengine/core - * Command: npx @dojoengine/core - */ - -import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; - -export type ContractComponents = Awaited< - ReturnType ->; - -export function defineContractComponents(world: World) { - return { - DirectionsAvailable: (() => { - return defineComponent( - world, - { player: RecsType.BigInt, directions: RecsType.StringArray }, - { - metadata: { - name: "dojo_starter-DirectionsAvailable", - types: ["contractaddress"], - customTypes: ["Direction"], - }, - } - ); - })(), - Moves: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - remaining: RecsType.Number, - last_direction: RecsType.Number, - can_move: RecsType.Boolean, - }, - { - metadata: { - name: "dojo_starter-Moves", - types: ["contractaddress", "u8", "enum", "bool"], - customTypes: ["Direction"], - }, - } - ); - })(), - Position: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - vec: { x: RecsType.Number, y: RecsType.Number }, - }, - { - metadata: { - name: "dojo_starter-Position", - types: ["contractaddress", "u32", "u32"], - customTypes: ["Vec2"], - }, - } - ); - })(), - }; -} diff --git a/examples/clients/react/react-phaser-example/src/dojo/generated/generated.ts b/examples/clients/react/react-phaser-example/src/dojo/generated/generated.ts deleted file mode 100644 index fe9bbe9d..00000000 --- a/examples/clients/react/react-phaser-example/src/dojo/generated/generated.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Account, AccountInterface } from "starknet"; -import { DojoProvider } from "@dojoengine/core"; -import { Direction } from "../utils"; - -const NAMESPACE = "dojo_starter"; - -export interface IWorld { - actions: { - spawn: (props: { account: AccountInterface }) => Promise; - move: (props: MoveProps) => Promise; - }; -} - -export interface MoveProps { - account: Account | AccountInterface; - direction: Direction; -} - -const handleError = (action: string, error: unknown) => { - console.error(`Error executing ${action}:`, error); - throw error; -}; - -export const setupWorld = async (provider: DojoProvider): Promise => { - const actions = () => ({ - spawn: async ({ account }: { account: AccountInterface }) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "spawn", - calldata: [], - }, - NAMESPACE - ); - } catch (error) { - handleError("spawn", error); - } - }, - - move: async ({ account, direction }: MoveProps) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "move", - calldata: [direction], - }, - NAMESPACE - ); - } catch (error) { - handleError("move", error); - } - }, - }); - - return { actions: actions() }; -}; diff --git a/examples/clients/react/react-phaser-example/src/dojo/generated/setup.ts b/examples/clients/react/react-phaser-example/src/dojo/generated/setup.ts deleted file mode 100644 index 00090229..00000000 --- a/examples/clients/react/react-phaser-example/src/dojo/generated/setup.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { getSyncEntities } from "@dojoengine/state"; -import * as torii from "@dojoengine/torii-client"; -import { createClientComponents } from "../createClientComponents"; -import { createSystemCalls } from "../createSystemCalls"; -import { defineContractComponents } from "./contractComponents"; -import { world } from "./world"; -import { setupWorld } from "./generated"; -import { - DojoConfig, - DojoProvider, - createModelTypedData, -} from "@dojoengine/core"; -import { - ArraySignatureType, - TypedData, - WeierstrassSignatureType, -} from "starknet"; - -export type SetupResult = Awaited>; - -export async function setup({ ...config }: DojoConfig) { - // torii client - const toriiClient = await torii.createClient({ - rpcUrl: config.rpcUrl, - toriiUrl: config.toriiUrl, - relayUrl: config.relayUrl, - worldAddress: config.manifest.world.address || "", - }); - - // create contract components - const contractComponents = defineContractComponents(world); - - // create client components - const clientComponents = createClientComponents({ contractComponents }); - - // fetch all existing entities from torii - const sync = await getSyncEntities( - toriiClient, - contractComponents as any, - [] - ); - - const client = await setupWorld( - new DojoProvider(config.manifest, config.rpcUrl) - ); - - return { - client, - clientComponents, - contractComponents, - systemCalls: createSystemCalls( - { client }, - contractComponents, - clientComponents - ), - publish: (typedData: string, signature: ArraySignatureType) => { - toriiClient.publishMessage(typedData, signature); - }, - config, - sync, - }; -} diff --git a/examples/clients/react/react-pwa-app/src/dojo/generated/setup.ts b/examples/clients/react/react-phaser-example/src/dojo/setup.ts similarity index 71% rename from examples/clients/react/react-pwa-app/src/dojo/generated/setup.ts rename to examples/clients/react/react-phaser-example/src/dojo/setup.ts index 9cd7fada..0ad7139c 100644 --- a/examples/clients/react/react-pwa-app/src/dojo/generated/setup.ts +++ b/examples/clients/react/react-phaser-example/src/dojo/setup.ts @@ -1,22 +1,13 @@ -import { getSyncEntities } from "@dojoengine/state"; -import { - DojoConfig, - DojoProvider, - createModelTypedData, -} from "@dojoengine/core"; +import { DojoConfig, DojoProvider } from "@dojoengine/core"; import * as torii from "@dojoengine/torii-client"; -import { createClientComponents } from "../createClientComponents"; -import { createSystemCalls } from "../createSystemCalls"; -import { defineContractComponents } from "./contractComponents"; +import { createClientComponents } from "./createClientComponents"; +import { createSystemCalls } from "./createSystemCalls"; +import { defineContractComponents } from "./typescript/models.gen"; import { world } from "./world"; -import { setupWorld } from "./generated"; -import { - Account, - ArraySignatureType, - TypedData, - WeierstrassSignatureType, -} from "starknet"; +import { setupWorld } from "./typescript/contracts.gen"; +import { Account, ArraySignatureType } from "starknet"; import { BurnerManager } from "@dojoengine/create-burner"; +import { getSyncEvents, getSyncEntities } from "@dojoengine/state"; export type SetupResult = Awaited>; @@ -25,7 +16,7 @@ export async function setup({ ...config }: DojoConfig) { const toriiClient = await torii.createClient({ rpcUrl: config.rpcUrl, toriiUrl: config.toriiUrl, - relayUrl: config.relayUrl, + relayUrl: "", worldAddress: config.manifest.world.address || "", }); @@ -35,15 +26,23 @@ export async function setup({ ...config }: DojoConfig) { // create client components const clientComponents = createClientComponents({ contractComponents }); - // fetch all existing entities from torii - const sync = await getSyncEntities( + // create dojo provider + const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); + + // Sync all events + const eventSync = getSyncEvents( toriiClient, contractComponents as any, + undefined, [] ); - // create dojo provider - const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); + // Sync all entities + const sync = await getSyncEntities( + toriiClient, + contractComponents as any, + [] + ); // setup world const client = await setupWorld(dojoProvider); @@ -75,17 +74,15 @@ export async function setup({ ...config }: DojoConfig) { client, clientComponents, contractComponents, - systemCalls: createSystemCalls( - { client }, - contractComponents, - clientComponents - ), + systemCalls: createSystemCalls({ client }, clientComponents, world), publish: (typedData: string, signature: ArraySignatureType) => { toriiClient.publishMessage(typedData, signature); }, config, dojoProvider, burnerManager, + toriiClient, + eventSync, sync, }; } diff --git a/examples/clients/react/react-phaser-example/src/dojo/typescript/contracts.gen.ts b/examples/clients/react/react-phaser-example/src/dojo/typescript/contracts.gen.ts new file mode 100644 index 00000000..0f7fac11 --- /dev/null +++ b/examples/clients/react/react-phaser-example/src/dojo/typescript/contracts.gen.ts @@ -0,0 +1,86 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { Account, byteArray } from "starknet"; +import { DojoProvider } from "@dojoengine/core"; +import * as models from "./models.gen"; + +export type IWorld = Awaited>; + +export async function setupWorld(provider: DojoProvider) { + // System definitions for `dojo_starter-actions` contract + function actions() { + const contract_name = "actions"; + + // Call the `spawn` system with the specified Account and calldata + const spawn = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "spawn", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `move` system with the specified Account and calldata + const move = async (props: { + account: Account; + direction: models.Direction; + }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "move", + calldata: [ + ["None", "Left", "Right", "Up", "Down"].indexOf( + props.direction.type + ), + ], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `world` system with the specified Account and calldata + const world = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "world", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + return { + spawn, + move, + world, + }; + } + + return { + actions: actions(), + }; +} diff --git a/examples/clients/react/react-phaser-example/src/dojo/typescript/models.gen.ts b/examples/clients/react/react-phaser-example/src/dojo/typescript/models.gen.ts new file mode 100644 index 00000000..051689eb --- /dev/null +++ b/examples/clients/react/react-phaser-example/src/dojo/typescript/models.gen.ts @@ -0,0 +1,193 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; + +export type ContractComponents = Awaited< + ReturnType +>; + +// Type definition for `dojo_starter::models::Direction` enum +export type Direction = + | { type: "None" } + | { type: "Left" } + | { type: "Right" } + | { type: "Up" } + | { type: "Down" }; + +export const DirectionDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `dojo::model::layout::Layout` enum +export type Layout = + | { type: "Fixed"; value: RecsType.NumberArray } + | { type: "Struct"; value: RecsType.StringArray } + | { type: "Tuple"; value: RecsType.StringArray } + | { type: "Array"; value: RecsType.StringArray } + | { type: "ByteArray" } + | { type: "Enum"; value: RecsType.StringArray }; + +export const LayoutDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `core::byte_array::ByteArray` struct +export interface ByteArray { + data: String[]; + pending_word: BigInt; + pending_word_len: Number; +} +export const ByteArrayDefinition = { + data: RecsType.StringArray, + pending_word: RecsType.BigInt, + pending_word_len: RecsType.Number, +}; + +// Type definition for `dojo::model::layout::FieldLayout` struct +export interface FieldLayout { + selector: BigInt; + layout: Layout; +} +export const FieldLayoutDefinition = { + selector: RecsType.BigInt, + layout: LayoutDefinition, +}; + +// Type definition for `dojo_starter::models::Moves` struct +export interface Moves { + player: BigInt; + remaining: Number; + last_direction: Direction; + can_move: Boolean; +} +export const MovesDefinition = { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: DirectionDefinition, + can_move: RecsType.Boolean, +}; + +// Type definition for `dojo_starter::models::DirectionsAvailable` struct +export interface DirectionsAvailable { + player: BigInt; + directions: String[]; +} +export const DirectionsAvailableDefinition = { + player: RecsType.BigInt, + directions: RecsType.StringArray, +}; + +// Type definition for `dojo_starter::systems::actions::actions::Moved` struct +export interface Moved { + player: BigInt; + direction: Direction; +} +export const MovedDefinition = { + player: RecsType.BigInt, + direction: DirectionDefinition, +}; + +// Type definition for `dojo_starter::models::Vec2` struct +export interface Vec2 { + x: Number; + y: Number; +} +export const Vec2Definition = { + x: RecsType.Number, + y: RecsType.Number, +}; + +// Type definition for `dojo_starter::models::Position` struct +export interface Position { + player: BigInt; + vec: Vec2; +} +export const PositionDefinition = { + player: RecsType.BigInt, + vec: Vec2Definition, +}; + +export function defineContractComponents(world: World) { + return { + // Model definition for `dojo_starter::models::Moves` model + Moves: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: RecsType.String, + can_move: RecsType.Boolean, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moves", + types: ["ContractAddress", "u8", "Direction", "bool"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::DirectionsAvailable` model + DirectionsAvailable: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + directions: RecsType.StringArray, + }, + { + metadata: { + namespace: "dojo_starter", + name: "DirectionsAvailable", + types: ["ContractAddress", "array"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::systems::actions::actions::Moved` model + Moved: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + direction: RecsType.String, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moved", + types: ["ContractAddress", "Direction"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::Position` model + Position: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + vec: Vec2Definition, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Position", + types: ["ContractAddress"], + customTypes: ["Vec2"], + }, + } + ); + })(), + }; +} diff --git a/examples/clients/react/react-phaser-example/src/phaser/config/constants.ts b/examples/clients/react/react-phaser-example/src/phaser/config/constants.ts index 18d3c87c..8bafc664 100644 --- a/examples/clients/react/react-phaser-example/src/phaser/config/constants.ts +++ b/examples/clients/react/react-phaser-example/src/phaser/config/constants.ts @@ -35,14 +35,6 @@ export enum Assets { Tileset = "Tileset", } -export enum Direction { - Unknown, - Up, - Down, - Left, - Right, -} - export const TILE_HEIGHT = 32; export const TILE_WIDTH = 32; diff --git a/examples/clients/react/react-phaser-example/src/phaser/systems/controls.ts b/examples/clients/react/react-phaser-example/src/phaser/systems/controls.ts index d9260b9b..9c3a295e 100644 --- a/examples/clients/react/react-phaser-example/src/phaser/systems/controls.ts +++ b/examples/clients/react/react-phaser-example/src/phaser/systems/controls.ts @@ -1,5 +1,5 @@ +import { DirectionDefinition } from "../../dojo/typescript/models.gen"; import { PhaserLayer } from "../createPhaserLayer"; -import { Direction } from "../../dojo/utils"; export const controls = (layer: PhaserLayer) => { const { @@ -15,56 +15,56 @@ export const controls = (layer: PhaserLayer) => { input.onKeyPress( (keys) => keys.has("W"), () => { - move(account, Direction.Up); + move(account, { type: "Up" }); } ); input.onKeyPress( (keys) => keys.has("A"), () => { - move(account, Direction.Left); + move(account, { type: "Left" }); } ); input.onKeyPress( (keys) => keys.has("S"), () => { - move(account, Direction.Down); + move(account, { type: "Down" }); } ); input.onKeyPress( (keys) => keys.has("D"), () => { - move(account, Direction.Right); + move(account, { type: "Right" }); } ); input.onKeyPress( (keys) => keys.has("UP"), () => { - move(account, Direction.Up); + move(account, { type: "Up" }); } ); input.onKeyPress( (keys) => keys.has("LEFT"), () => { - move(account, Direction.Left); + move(account, { type: "Left" }); } ); input.onKeyPress( (keys) => keys.has("DOWN"), () => { - move(account, Direction.Down); + move(account, { type: "Down" }); } ); input.onKeyPress( (keys) => keys.has("RIGHT"), () => { - move(account, Direction.Right); + move(account, { type: "Right" }); } ); }; diff --git a/examples/clients/react/react-pwa-app/src/App.tsx b/examples/clients/react/react-pwa-app/src/App.tsx index a171c899..55ff5426 100644 --- a/examples/clients/react/react-pwa-app/src/App.tsx +++ b/examples/clients/react/react-pwa-app/src/App.tsx @@ -2,7 +2,6 @@ import { useComponentValue } from "@dojoengine/react"; import { Entity } from "@dojoengine/recs"; import { useEffect, useState } from "react"; import "./App.css"; -import { Direction } from "./utils"; import { getEntityIdFromKeys } from "@dojoengine/utils"; import { useDojo } from "./dojo/useDojo"; @@ -119,7 +118,7 @@ function App() {
diff --git a/examples/clients/react/react-pwa-app/src/dojo/DojoContext.tsx b/examples/clients/react/react-pwa-app/src/dojo/DojoContext.tsx index cebee9dc..ee5ecd6c 100644 --- a/examples/clients/react/react-pwa-app/src/dojo/DojoContext.tsx +++ b/examples/clients/react/react-pwa-app/src/dojo/DojoContext.tsx @@ -1,7 +1,7 @@ import { BurnerAccount, useBurnerManager } from "@dojoengine/create-burner"; import { ReactNode, createContext, useContext, useMemo } from "react"; import { Account } from "starknet"; -import { SetupResult } from "./generated/setup"; +import { SetupResult } from "./setup"; interface DojoContextType extends SetupResult { masterAccount: Account; diff --git a/examples/clients/react/react-pwa-app/src/dojo/createClientComponents.ts b/examples/clients/react/react-pwa-app/src/dojo/createClientComponents.ts index 550ad13b..7b5bb860 100644 --- a/examples/clients/react/react-pwa-app/src/dojo/createClientComponents.ts +++ b/examples/clients/react/react-pwa-app/src/dojo/createClientComponents.ts @@ -1,5 +1,5 @@ import { overridableComponent } from "@dojoengine/recs"; -import { ContractComponents } from "./generated/contractComponents"; +import { ContractComponents } from "./typescript/models.gen"; export type ClientComponents = ReturnType; diff --git a/examples/clients/react/react-pwa-app/src/dojo/createSystemCalls.ts b/examples/clients/react/react-pwa-app/src/dojo/createSystemCalls.ts index 94a204df..46f699b4 100644 --- a/examples/clients/react/react-pwa-app/src/dojo/createSystemCalls.ts +++ b/examples/clients/react/react-pwa-app/src/dojo/createSystemCalls.ts @@ -1,57 +1,71 @@ -import { Account, AccountInterface } from "starknet"; -import { Entity, getComponentValue } from "@dojoengine/recs"; +import { Account } from "starknet"; +import { + Entity, + Has, + HasValue, + World, + defineSystem, + getComponentValue, +} from "@dojoengine/recs"; import { uuid } from "@latticexyz/utils"; import { ClientComponents } from "./createClientComponents"; -import { Direction, updatePositionWithDirection } from "../utils"; -import { - getEntityIdFromKeys, - getEvents, - setComponentsFromEvents, -} from "@dojoengine/utils"; -import { ContractComponents } from "./generated/contractComponents"; -import type { IWorld } from "./generated/generated"; +import { getEntityIdFromKeys } from "@dojoengine/utils"; +import type { IWorld } from "./typescript/contracts.gen"; +import { Direction } from "./typescript/models.gen"; export type SystemCalls = ReturnType; export function createSystemCalls( { client }: { client: IWorld }, - contractComponents: ContractComponents, - { Position, Moves }: ClientComponents + { Position, Moves }: ClientComponents, + world: World ) { - const spawn = async (account: AccountInterface) => { + const spawn = async (account: Account) => { const entityId = getEntityIdFromKeys([ BigInt(account.address), ]) as Entity; - const positionId = uuid(); - Position.addOverride(positionId, { + const movesId = uuid(); + Moves.addOverride(movesId, { entity: entityId, - value: { player: BigInt(entityId), vec: { x: 10, y: 10 } }, + value: { + player: BigInt(entityId), + remaining: + (getComponentValue(Moves, entityId)?.remaining || 0) + 100, + }, }); - const movesId = uuid(); - Moves.addOverride(movesId, { + const positionId = uuid(); + Position.addOverride(positionId, { entity: entityId, value: { player: BigInt(entityId), - remaining: 100, - last_direction: 0, + vec: { + x: 10 + (getComponentValue(Position, entityId)?.vec.x || 0), + y: 10 + (getComponentValue(Position, entityId)?.vec.y || 0), + }, }, }); try { - const { transaction_hash } = await client.actions.spawn({ + await client.actions.spawn({ account, }); - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); Position.removeOverride(positionId); @@ -62,54 +76,29 @@ export function createSystemCalls( } }; - const move = async (account: AccountInterface, direction: Direction) => { - const entityId = getEntityIdFromKeys([ - BigInt(account.address), - ]) as Entity; - - const positionId = uuid(); - Position.addOverride(positionId, { - entity: entityId, - value: { - player: BigInt(entityId), - vec: updatePositionWithDirection( - direction, - getComponentValue(Position, entityId) as any - ).vec, - }, - }); - - const movesId = uuid(); - Moves.addOverride(movesId, { - entity: entityId, - value: { - player: BigInt(entityId), - remaining: - (getComponentValue(Moves, entityId)?.remaining || 0) - 1, - }, - }); - + const move = async (account: Account, direction: Direction) => { try { - const { transaction_hash } = await client.actions.move({ + await client.actions.move({ account, direction, }); - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); - Position.removeOverride(positionId); - Moves.removeOverride(movesId); - } finally { - Position.removeOverride(positionId); - Moves.removeOverride(movesId); } }; diff --git a/examples/clients/react/react-pwa-app/src/dojo/generated/contractComponents.ts b/examples/clients/react/react-pwa-app/src/dojo/generated/contractComponents.ts deleted file mode 100644 index 596871c8..00000000 --- a/examples/clients/react/react-pwa-app/src/dojo/generated/contractComponents.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Autogenerated file. Do not edit manually. - * Generated using @dojoengine/core - * Command: npx @dojoengine/core - */ - -import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; - -export type ContractComponents = Awaited< - ReturnType ->; - -export function defineContractComponents(world: World) { - return { - DirectionsAvailable: (() => { - return defineComponent( - world, - { player: RecsType.BigInt, directions: RecsType.StringArray }, - { - metadata: { - name: "dojo_starter-DirectionsAvailable", - types: ["contractaddress"], - customTypes: ["Direction"], - }, - } - ); - })(), - Moves: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - remaining: RecsType.Number, - last_direction: RecsType.Number, - can_move: RecsType.Boolean, - }, - { - metadata: { - name: "dojo_starter-Moves", - types: ["contractaddress", "u8", "enum", "bool"], - customTypes: ["Direction"], - }, - } - ); - })(), - Position: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - vec: { x: RecsType.Number, y: RecsType.Number }, - }, - { - metadata: { - name: "dojo_starter-Position", - types: ["contractaddress", "u32", "u32"], - customTypes: ["Vec2"], - }, - } - ); - })(), - }; -} diff --git a/examples/clients/react/react-pwa-app/src/dojo/generated/generated.ts b/examples/clients/react/react-pwa-app/src/dojo/generated/generated.ts deleted file mode 100644 index b10d2654..00000000 --- a/examples/clients/react/react-pwa-app/src/dojo/generated/generated.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Account, AccountInterface } from "starknet"; -import { DojoProvider } from "@dojoengine/core"; -import { Direction } from "../../utils"; - -const NAMESPACE = "dojo_starter"; - -export interface IWorld { - actions: { - spawn: (props: { account: AccountInterface }) => Promise; - move: (props: MoveProps) => Promise; - }; -} - -export interface MoveProps { - account: Account | AccountInterface; - direction: Direction; -} - -const handleError = (action: string, error: unknown) => { - console.error(`Error executing ${action}:`, error); - throw error; -}; - -export const setupWorld = async (provider: DojoProvider): Promise => { - const actions = () => ({ - spawn: async ({ account }: { account: AccountInterface }) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "spawn", - calldata: [], - }, - NAMESPACE - ); - } catch (error) { - handleError("spawn", error); - } - }, - - move: async ({ account, direction }: MoveProps) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "move", - calldata: [direction], - }, - NAMESPACE - ); - } catch (error) { - handleError("move", error); - } - }, - }); - - return { actions: actions() }; -}; diff --git a/examples/clients/react/react-pwa-app/src/dojo/setup.ts b/examples/clients/react/react-pwa-app/src/dojo/setup.ts new file mode 100644 index 00000000..0ad7139c --- /dev/null +++ b/examples/clients/react/react-pwa-app/src/dojo/setup.ts @@ -0,0 +1,88 @@ +import { DojoConfig, DojoProvider } from "@dojoengine/core"; +import * as torii from "@dojoengine/torii-client"; +import { createClientComponents } from "./createClientComponents"; +import { createSystemCalls } from "./createSystemCalls"; +import { defineContractComponents } from "./typescript/models.gen"; +import { world } from "./world"; +import { setupWorld } from "./typescript/contracts.gen"; +import { Account, ArraySignatureType } from "starknet"; +import { BurnerManager } from "@dojoengine/create-burner"; +import { getSyncEvents, getSyncEntities } from "@dojoengine/state"; + +export type SetupResult = Awaited>; + +export async function setup({ ...config }: DojoConfig) { + // torii client + const toriiClient = await torii.createClient({ + rpcUrl: config.rpcUrl, + toriiUrl: config.toriiUrl, + relayUrl: "", + worldAddress: config.manifest.world.address || "", + }); + + // create contract components + const contractComponents = defineContractComponents(world); + + // create client components + const clientComponents = createClientComponents({ contractComponents }); + + // create dojo provider + const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); + + // Sync all events + const eventSync = getSyncEvents( + toriiClient, + contractComponents as any, + undefined, + [] + ); + + // Sync all entities + const sync = await getSyncEntities( + toriiClient, + contractComponents as any, + [] + ); + + // setup world + const client = await setupWorld(dojoProvider); + + // create burner manager + const burnerManager = new BurnerManager({ + masterAccount: new Account( + { + nodeUrl: config.rpcUrl, + }, + config.masterAddress, + config.masterPrivateKey + ), + accountClassHash: config.accountClassHash, + rpcProvider: dojoProvider.provider, + feeTokenAddress: config.feeTokenAddress, + }); + + try { + await burnerManager.init(); + if (burnerManager.list().length === 0) { + await burnerManager.create(); + } + } catch (e) { + console.error(e); + } + + return { + client, + clientComponents, + contractComponents, + systemCalls: createSystemCalls({ client }, clientComponents, world), + publish: (typedData: string, signature: ArraySignatureType) => { + toriiClient.publishMessage(typedData, signature); + }, + config, + dojoProvider, + burnerManager, + toriiClient, + eventSync, + sync, + }; +} diff --git a/examples/clients/react/react-pwa-app/src/dojo/typescript/contracts.gen.ts b/examples/clients/react/react-pwa-app/src/dojo/typescript/contracts.gen.ts new file mode 100644 index 00000000..411d0500 --- /dev/null +++ b/examples/clients/react/react-pwa-app/src/dojo/typescript/contracts.gen.ts @@ -0,0 +1,86 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { Account } from "starknet"; +import { DojoProvider } from "@dojoengine/core"; +import * as models from "./models.gen"; + +export type IWorld = Awaited>; + +export async function setupWorld(provider: DojoProvider) { + // System definitions for `dojo_starter-actions` contract + function actions() { + const contract_name = "actions"; + + // Call the `spawn` system with the specified Account and calldata + const spawn = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "spawn", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `move` system with the specified Account and calldata + const move = async (props: { + account: Account; + direction: models.Direction; + }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "move", + calldata: [ + ["None", "Left", "Right", "Up", "Down"].indexOf( + props.direction.type + ), + ], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `world` system with the specified Account and calldata + const world = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "world", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + return { + spawn, + move, + world, + }; + } + + return { + actions: actions(), + }; +} diff --git a/examples/clients/react/react-pwa-app/src/dojo/typescript/models.gen.ts b/examples/clients/react/react-pwa-app/src/dojo/typescript/models.gen.ts new file mode 100644 index 00000000..051689eb --- /dev/null +++ b/examples/clients/react/react-pwa-app/src/dojo/typescript/models.gen.ts @@ -0,0 +1,193 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; + +export type ContractComponents = Awaited< + ReturnType +>; + +// Type definition for `dojo_starter::models::Direction` enum +export type Direction = + | { type: "None" } + | { type: "Left" } + | { type: "Right" } + | { type: "Up" } + | { type: "Down" }; + +export const DirectionDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `dojo::model::layout::Layout` enum +export type Layout = + | { type: "Fixed"; value: RecsType.NumberArray } + | { type: "Struct"; value: RecsType.StringArray } + | { type: "Tuple"; value: RecsType.StringArray } + | { type: "Array"; value: RecsType.StringArray } + | { type: "ByteArray" } + | { type: "Enum"; value: RecsType.StringArray }; + +export const LayoutDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `core::byte_array::ByteArray` struct +export interface ByteArray { + data: String[]; + pending_word: BigInt; + pending_word_len: Number; +} +export const ByteArrayDefinition = { + data: RecsType.StringArray, + pending_word: RecsType.BigInt, + pending_word_len: RecsType.Number, +}; + +// Type definition for `dojo::model::layout::FieldLayout` struct +export interface FieldLayout { + selector: BigInt; + layout: Layout; +} +export const FieldLayoutDefinition = { + selector: RecsType.BigInt, + layout: LayoutDefinition, +}; + +// Type definition for `dojo_starter::models::Moves` struct +export interface Moves { + player: BigInt; + remaining: Number; + last_direction: Direction; + can_move: Boolean; +} +export const MovesDefinition = { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: DirectionDefinition, + can_move: RecsType.Boolean, +}; + +// Type definition for `dojo_starter::models::DirectionsAvailable` struct +export interface DirectionsAvailable { + player: BigInt; + directions: String[]; +} +export const DirectionsAvailableDefinition = { + player: RecsType.BigInt, + directions: RecsType.StringArray, +}; + +// Type definition for `dojo_starter::systems::actions::actions::Moved` struct +export interface Moved { + player: BigInt; + direction: Direction; +} +export const MovedDefinition = { + player: RecsType.BigInt, + direction: DirectionDefinition, +}; + +// Type definition for `dojo_starter::models::Vec2` struct +export interface Vec2 { + x: Number; + y: Number; +} +export const Vec2Definition = { + x: RecsType.Number, + y: RecsType.Number, +}; + +// Type definition for `dojo_starter::models::Position` struct +export interface Position { + player: BigInt; + vec: Vec2; +} +export const PositionDefinition = { + player: RecsType.BigInt, + vec: Vec2Definition, +}; + +export function defineContractComponents(world: World) { + return { + // Model definition for `dojo_starter::models::Moves` model + Moves: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: RecsType.String, + can_move: RecsType.Boolean, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moves", + types: ["ContractAddress", "u8", "Direction", "bool"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::DirectionsAvailable` model + DirectionsAvailable: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + directions: RecsType.StringArray, + }, + { + metadata: { + namespace: "dojo_starter", + name: "DirectionsAvailable", + types: ["ContractAddress", "array"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::systems::actions::actions::Moved` model + Moved: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + direction: RecsType.String, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moved", + types: ["ContractAddress", "Direction"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::Position` model + Position: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + vec: Vec2Definition, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Position", + types: ["ContractAddress"], + customTypes: ["Vec2"], + }, + } + ); + })(), + }; +} diff --git a/examples/clients/react/react-phaser-example/src/dojo/generated/world.ts b/examples/clients/react/react-pwa-app/src/dojo/world.ts similarity index 100% rename from examples/clients/react/react-phaser-example/src/dojo/generated/world.ts rename to examples/clients/react/react-pwa-app/src/dojo/world.ts diff --git a/examples/clients/react/react-pwa-app/src/main.tsx b/examples/clients/react/react-pwa-app/src/main.tsx index 5af1fa50..5ae7a19c 100644 --- a/examples/clients/react/react-pwa-app/src/main.tsx +++ b/examples/clients/react/react-pwa-app/src/main.tsx @@ -2,9 +2,9 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; -import { setup } from "./dojo/generated/setup.ts"; import { DojoProvider } from "./dojo/DojoContext.tsx"; import { dojoConfig } from "../dojoConfig.ts"; +import { setup } from "./dojo/setup.ts"; async function init() { const rootElement = document.getElementById("root"); diff --git a/examples/clients/react/react-pwa-app/tsconfig.json b/examples/clients/react/react-pwa-app/tsconfig.json index 7f1be29a..1da7add7 100644 --- a/examples/clients/react/react-pwa-app/tsconfig.json +++ b/examples/clients/react/react-pwa-app/tsconfig.json @@ -1,21 +1,7 @@ { + "extends": "../../../../tsconfig.base.json", "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "node", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "strict": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "allowSyntheticDefaultImports": true + "jsx": "react-jsx" }, "include": ["src", "dojoConfig.ts"], "references": [ diff --git a/examples/clients/react/react-threejs/src/dojo/DojoContext.tsx b/examples/clients/react/react-threejs/src/dojo/DojoContext.tsx index 8c82a8f2..ee5ecd6c 100644 --- a/examples/clients/react/react-threejs/src/dojo/DojoContext.tsx +++ b/examples/clients/react/react-threejs/src/dojo/DojoContext.tsx @@ -1,7 +1,7 @@ import { BurnerAccount, useBurnerManager } from "@dojoengine/create-burner"; import { ReactNode, createContext, useContext, useMemo } from "react"; -import { Account, RpcProvider } from "starknet"; -import { SetupResult } from "./generated/setup"; +import { Account } from "starknet"; +import { SetupResult } from "./setup"; interface DojoContextType extends SetupResult { masterAccount: Account; @@ -21,21 +21,20 @@ export const DojoProvider = ({ if (currentValue) throw new Error("DojoProvider can only be used once"); const { - config: { rpcUrl, masterAddress, masterPrivateKey }, + config: { masterAddress, masterPrivateKey }, burnerManager, + dojoProvider, } = value; - const rpcProvider = useMemo( - () => - new RpcProvider({ - nodeUrl: rpcUrl, - }), - [rpcUrl] - ); - const masterAccount = useMemo( - () => new Account(rpcProvider, masterAddress, masterPrivateKey), - [rpcProvider, masterAddress, masterPrivateKey] + () => + new Account( + dojoProvider.provider, + masterAddress, + masterPrivateKey, + "1" + ), + [masterAddress, masterPrivateKey, dojoProvider.provider] ); const { diff --git a/examples/clients/react/react-threejs/src/dojo/createClientComponents.ts b/examples/clients/react/react-threejs/src/dojo/createClientComponents.ts index 550ad13b..7b5bb860 100644 --- a/examples/clients/react/react-threejs/src/dojo/createClientComponents.ts +++ b/examples/clients/react/react-threejs/src/dojo/createClientComponents.ts @@ -1,5 +1,5 @@ import { overridableComponent } from "@dojoengine/recs"; -import { ContractComponents } from "./generated/contractComponents"; +import { ContractComponents } from "./typescript/models.gen"; export type ClientComponents = ReturnType; diff --git a/examples/clients/react/react-threejs/src/dojo/createSystemCalls.ts b/examples/clients/react/react-threejs/src/dojo/createSystemCalls.ts index 55041b11..46f699b4 100644 --- a/examples/clients/react/react-threejs/src/dojo/createSystemCalls.ts +++ b/examples/clients/react/react-threejs/src/dojo/createSystemCalls.ts @@ -1,57 +1,71 @@ -import { AccountInterface } from "starknet"; -import { Entity, getComponentValue } from "@dojoengine/recs"; +import { Account } from "starknet"; +import { + Entity, + Has, + HasValue, + World, + defineSystem, + getComponentValue, +} from "@dojoengine/recs"; import { uuid } from "@latticexyz/utils"; import { ClientComponents } from "./createClientComponents"; -import { Direction, updatePositionWithDirection } from "../utils"; -import { - getEntityIdFromKeys, - getEvents, - setComponentsFromEvents, -} from "@dojoengine/utils"; -import { ContractComponents } from "./generated/contractComponents"; -import type { IWorld } from "./generated/generated"; +import { getEntityIdFromKeys } from "@dojoengine/utils"; +import type { IWorld } from "./typescript/contracts.gen"; +import { Direction } from "./typescript/models.gen"; export type SystemCalls = ReturnType; export function createSystemCalls( { client }: { client: IWorld }, - contractComponents: ContractComponents, - { Position, Moves }: ClientComponents + { Position, Moves }: ClientComponents, + world: World ) { - const spawn = async (account: AccountInterface) => { + const spawn = async (account: Account) => { const entityId = getEntityIdFromKeys([ BigInt(account.address), ]) as Entity; - const positionId = uuid(); - Position.addOverride(positionId, { + const movesId = uuid(); + Moves.addOverride(movesId, { entity: entityId, - value: { player: BigInt(entityId), vec: { x: 10, y: 10 } }, + value: { + player: BigInt(entityId), + remaining: + (getComponentValue(Moves, entityId)?.remaining || 0) + 100, + }, }); - const movesId = uuid(); - Moves.addOverride(movesId, { + const positionId = uuid(); + Position.addOverride(positionId, { entity: entityId, value: { player: BigInt(entityId), - remaining: 100, - last_direction: 0, + vec: { + x: 10 + (getComponentValue(Position, entityId)?.vec.x || 0), + y: 10 + (getComponentValue(Position, entityId)?.vec.y || 0), + }, }, }); try { - const { transaction_hash } = await client.actions.spawn({ + await client.actions.spawn({ account, }); - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); Position.removeOverride(positionId); @@ -62,54 +76,29 @@ export function createSystemCalls( } }; - const move = async (account: AccountInterface, direction: Direction) => { - const entityId = getEntityIdFromKeys([ - BigInt(account.address), - ]) as Entity; - - const positionId = uuid(); - Position.addOverride(positionId, { - entity: entityId, - value: { - player: BigInt(entityId), - vec: updatePositionWithDirection( - direction, - getComponentValue(Position, entityId) as any - ).vec, - }, - }); - - const movesId = uuid(); - Moves.addOverride(movesId, { - entity: entityId, - value: { - player: BigInt(entityId), - remaining: - (getComponentValue(Moves, entityId)?.remaining || 0) - 1, - }, - }); - + const move = async (account: Account, direction: Direction) => { try { - const { transaction_hash } = await client.actions.move({ + await client.actions.move({ account, direction, }); - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); - Position.removeOverride(positionId); - Moves.removeOverride(movesId); - } finally { - Position.removeOverride(positionId); - Moves.removeOverride(movesId); } }; diff --git a/examples/clients/react/react-threejs/src/dojo/generated/contractComponents.ts b/examples/clients/react/react-threejs/src/dojo/generated/contractComponents.ts deleted file mode 100644 index 596871c8..00000000 --- a/examples/clients/react/react-threejs/src/dojo/generated/contractComponents.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Autogenerated file. Do not edit manually. - * Generated using @dojoengine/core - * Command: npx @dojoengine/core - */ - -import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; - -export type ContractComponents = Awaited< - ReturnType ->; - -export function defineContractComponents(world: World) { - return { - DirectionsAvailable: (() => { - return defineComponent( - world, - { player: RecsType.BigInt, directions: RecsType.StringArray }, - { - metadata: { - name: "dojo_starter-DirectionsAvailable", - types: ["contractaddress"], - customTypes: ["Direction"], - }, - } - ); - })(), - Moves: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - remaining: RecsType.Number, - last_direction: RecsType.Number, - can_move: RecsType.Boolean, - }, - { - metadata: { - name: "dojo_starter-Moves", - types: ["contractaddress", "u8", "enum", "bool"], - customTypes: ["Direction"], - }, - } - ); - })(), - Position: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - vec: { x: RecsType.Number, y: RecsType.Number }, - }, - { - metadata: { - name: "dojo_starter-Position", - types: ["contractaddress", "u32", "u32"], - customTypes: ["Vec2"], - }, - } - ); - })(), - }; -} diff --git a/examples/clients/react/react-threejs/src/dojo/generated/generated.ts b/examples/clients/react/react-threejs/src/dojo/generated/generated.ts deleted file mode 100644 index b10d2654..00000000 --- a/examples/clients/react/react-threejs/src/dojo/generated/generated.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Account, AccountInterface } from "starknet"; -import { DojoProvider } from "@dojoengine/core"; -import { Direction } from "../../utils"; - -const NAMESPACE = "dojo_starter"; - -export interface IWorld { - actions: { - spawn: (props: { account: AccountInterface }) => Promise; - move: (props: MoveProps) => Promise; - }; -} - -export interface MoveProps { - account: Account | AccountInterface; - direction: Direction; -} - -const handleError = (action: string, error: unknown) => { - console.error(`Error executing ${action}:`, error); - throw error; -}; - -export const setupWorld = async (provider: DojoProvider): Promise => { - const actions = () => ({ - spawn: async ({ account }: { account: AccountInterface }) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "spawn", - calldata: [], - }, - NAMESPACE - ); - } catch (error) { - handleError("spawn", error); - } - }, - - move: async ({ account, direction }: MoveProps) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "move", - calldata: [direction], - }, - NAMESPACE - ); - } catch (error) { - handleError("move", error); - } - }, - }); - - return { actions: actions() }; -}; diff --git a/examples/clients/react/react-threejs/src/dojo/generated/setup.ts b/examples/clients/react/react-threejs/src/dojo/setup.ts similarity index 66% rename from examples/clients/react/react-threejs/src/dojo/generated/setup.ts rename to examples/clients/react/react-threejs/src/dojo/setup.ts index 2111ab7a..de26fca9 100644 --- a/examples/clients/react/react-threejs/src/dojo/generated/setup.ts +++ b/examples/clients/react/react-threejs/src/dojo/setup.ts @@ -1,24 +1,13 @@ -import { getSyncEntities } from "@dojoengine/state"; -import { - DojoProvider, - DojoConfig, - createModelTypedData, -} from "@dojoengine/core"; +import { DojoConfig, DojoProvider } from "@dojoengine/core"; import * as torii from "@dojoengine/torii-client"; -import { createClientComponents } from "../createClientComponents"; -import { createSystemCalls } from "../createSystemCalls"; -import { defineContractComponents } from "./contractComponents"; +import { createClientComponents } from "./createClientComponents"; +import { createSystemCalls } from "./createSystemCalls"; +import { defineContractComponents } from "./typescript/models.gen"; import { world } from "./world"; -import { setupWorld } from "./generated"; -import { - Account, - ArraySignatureType, - RpcProvider, - Signature, - TypedData, - WeierstrassSignatureType, -} from "starknet"; +import { setupWorld } from "./typescript/contracts.gen"; +import { Account, ArraySignatureType } from "starknet"; import { BurnerManager } from "@dojoengine/create-burner"; +import { getSyncEvents, getSyncEntities } from "@dojoengine/state"; export type SetupResult = Awaited>; @@ -27,7 +16,7 @@ export async function setup({ ...config }: DojoConfig) { const toriiClient = await torii.createClient({ rpcUrl: config.rpcUrl, toriiUrl: config.toriiUrl, - relayUrl: config.relayUrl, + relayUrl: "", worldAddress: config.manifest.world.address || "", }); @@ -37,20 +26,28 @@ export async function setup({ ...config }: DojoConfig) { // create client components const clientComponents = createClientComponents({ contractComponents }); - // fetch all existing entities from torii - // fetch all existing entities from torii - const sync = await getSyncEntities( + // create dojo provider + const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); + + // Sync all events + const eventSync = getSyncEvents( toriiClient, contractComponents as any, + undefined, [] ); - const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); - - const client = await setupWorld( - new DojoProvider(config.manifest, config.rpcUrl) + // Sync all entities + const sync = await getSyncEntities( + toriiClient, + contractComponents as any, + [] ); + // setup world + const client = await setupWorld(dojoProvider); + + // create burner manager const burnerManager = new BurnerManager({ masterAccount: new Account( { @@ -77,17 +74,16 @@ export async function setup({ ...config }: DojoConfig) { client, clientComponents, contractComponents, - systemCalls: createSystemCalls( - { client }, - contractComponents, - clientComponents - ), + systemCalls: createSystemCalls({ client }, clientComponents, world), publish: (typedData: string, signature: ArraySignatureType) => { toriiClient.publishMessage(typedData, signature); }, config, - world, + dojoProvider, burnerManager, + toriiClient, + eventSync, sync, + world, }; } diff --git a/examples/clients/react/react-threejs/src/dojo/typescript/contracts.gen.ts b/examples/clients/react/react-threejs/src/dojo/typescript/contracts.gen.ts new file mode 100644 index 00000000..411d0500 --- /dev/null +++ b/examples/clients/react/react-threejs/src/dojo/typescript/contracts.gen.ts @@ -0,0 +1,86 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { Account } from "starknet"; +import { DojoProvider } from "@dojoengine/core"; +import * as models from "./models.gen"; + +export type IWorld = Awaited>; + +export async function setupWorld(provider: DojoProvider) { + // System definitions for `dojo_starter-actions` contract + function actions() { + const contract_name = "actions"; + + // Call the `spawn` system with the specified Account and calldata + const spawn = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "spawn", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `move` system with the specified Account and calldata + const move = async (props: { + account: Account; + direction: models.Direction; + }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "move", + calldata: [ + ["None", "Left", "Right", "Up", "Down"].indexOf( + props.direction.type + ), + ], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `world` system with the specified Account and calldata + const world = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "world", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + return { + spawn, + move, + world, + }; + } + + return { + actions: actions(), + }; +} diff --git a/examples/clients/react/react-threejs/src/dojo/typescript/models.gen.ts b/examples/clients/react/react-threejs/src/dojo/typescript/models.gen.ts new file mode 100644 index 00000000..051689eb --- /dev/null +++ b/examples/clients/react/react-threejs/src/dojo/typescript/models.gen.ts @@ -0,0 +1,193 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; + +export type ContractComponents = Awaited< + ReturnType +>; + +// Type definition for `dojo_starter::models::Direction` enum +export type Direction = + | { type: "None" } + | { type: "Left" } + | { type: "Right" } + | { type: "Up" } + | { type: "Down" }; + +export const DirectionDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `dojo::model::layout::Layout` enum +export type Layout = + | { type: "Fixed"; value: RecsType.NumberArray } + | { type: "Struct"; value: RecsType.StringArray } + | { type: "Tuple"; value: RecsType.StringArray } + | { type: "Array"; value: RecsType.StringArray } + | { type: "ByteArray" } + | { type: "Enum"; value: RecsType.StringArray }; + +export const LayoutDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `core::byte_array::ByteArray` struct +export interface ByteArray { + data: String[]; + pending_word: BigInt; + pending_word_len: Number; +} +export const ByteArrayDefinition = { + data: RecsType.StringArray, + pending_word: RecsType.BigInt, + pending_word_len: RecsType.Number, +}; + +// Type definition for `dojo::model::layout::FieldLayout` struct +export interface FieldLayout { + selector: BigInt; + layout: Layout; +} +export const FieldLayoutDefinition = { + selector: RecsType.BigInt, + layout: LayoutDefinition, +}; + +// Type definition for `dojo_starter::models::Moves` struct +export interface Moves { + player: BigInt; + remaining: Number; + last_direction: Direction; + can_move: Boolean; +} +export const MovesDefinition = { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: DirectionDefinition, + can_move: RecsType.Boolean, +}; + +// Type definition for `dojo_starter::models::DirectionsAvailable` struct +export interface DirectionsAvailable { + player: BigInt; + directions: String[]; +} +export const DirectionsAvailableDefinition = { + player: RecsType.BigInt, + directions: RecsType.StringArray, +}; + +// Type definition for `dojo_starter::systems::actions::actions::Moved` struct +export interface Moved { + player: BigInt; + direction: Direction; +} +export const MovedDefinition = { + player: RecsType.BigInt, + direction: DirectionDefinition, +}; + +// Type definition for `dojo_starter::models::Vec2` struct +export interface Vec2 { + x: Number; + y: Number; +} +export const Vec2Definition = { + x: RecsType.Number, + y: RecsType.Number, +}; + +// Type definition for `dojo_starter::models::Position` struct +export interface Position { + player: BigInt; + vec: Vec2; +} +export const PositionDefinition = { + player: RecsType.BigInt, + vec: Vec2Definition, +}; + +export function defineContractComponents(world: World) { + return { + // Model definition for `dojo_starter::models::Moves` model + Moves: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: RecsType.String, + can_move: RecsType.Boolean, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moves", + types: ["ContractAddress", "u8", "Direction", "bool"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::DirectionsAvailable` model + DirectionsAvailable: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + directions: RecsType.StringArray, + }, + { + metadata: { + namespace: "dojo_starter", + name: "DirectionsAvailable", + types: ["ContractAddress", "array"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::systems::actions::actions::Moved` model + Moved: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + direction: RecsType.String, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moved", + types: ["ContractAddress", "Direction"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::Position` model + Position: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + vec: Vec2Definition, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Position", + types: ["ContractAddress"], + customTypes: ["Vec2"], + }, + } + ); + })(), + }; +} diff --git a/examples/clients/react/react-pwa-app/src/dojo/generated/world.ts b/examples/clients/react/react-threejs/src/dojo/world.ts similarity index 100% rename from examples/clients/react/react-pwa-app/src/dojo/generated/world.ts rename to examples/clients/react/react-threejs/src/dojo/world.ts diff --git a/examples/clients/react/react-threejs/src/gameComponents/Player.tsx b/examples/clients/react/react-threejs/src/gameComponents/Player.tsx index e954c2bf..f990fde3 100644 --- a/examples/clients/react/react-threejs/src/gameComponents/Player.tsx +++ b/examples/clients/react/react-threejs/src/gameComponents/Player.tsx @@ -3,10 +3,11 @@ import { useDojo } from "@/dojo/useDojo"; import { useComponentValue } from "@dojoengine/react"; import { Cone } from "@react-three/drei"; import { getEntityIdFromKeys } from "@dojoengine/utils"; -import { Direction } from "@/utils"; + import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame } from "@react-three/fiber"; import { MAP_SCALE } from "@/config"; +import { Direction } from "@/dojo/typescript/models.gen"; export const Player = (props: any) => { const { @@ -17,9 +18,7 @@ export const Player = (props: any) => { }, } = useDojo(); - const [hoveredTile, setHoveredTile] = useState( - undefined - ); + const [hoveredTile, setHoveredTile] = useState({ type: "None" }); const [startPosition, setStartPosition] = useState< THREE.Vector3 | undefined >(); @@ -49,9 +48,14 @@ export const Player = (props: any) => { [MAP_SCALE] ); - const blueCellsAroundPlayer = [ + interface BlueCell { + direction: Direction; + position: THREE.Vector3; + } + + const blueCellsAroundPlayer: BlueCell[] = [ { - direction: Direction.Up, + direction: { type: "Up" }, position: new THREE.Vector3( (vec.y - 1) * MAP_SCALE, -MAP_SCALE + 0.1, @@ -59,7 +63,7 @@ export const Player = (props: any) => { ), }, { - direction: Direction.Down, + direction: { type: "Down" }, position: new THREE.Vector3( (vec.y + 1) * MAP_SCALE, -MAP_SCALE + 0.1, @@ -67,7 +71,7 @@ export const Player = (props: any) => { ), }, { - direction: Direction.Right, + direction: { type: "Right" }, position: new THREE.Vector3( vec.y * MAP_SCALE, -MAP_SCALE + 0.1, @@ -75,7 +79,7 @@ export const Player = (props: any) => { ), }, { - direction: Direction.Left, + direction: { type: "Left" }, position: new THREE.Vector3( vec.y * MAP_SCALE, -MAP_SCALE + 0.1, @@ -130,7 +134,7 @@ export const Player = (props: any) => { lerpProgress.current = 0; // Reset Hovered Tile - setHoveredTile(undefined); + setHoveredTile({ type: "None" }); }, [coneRef, player, vec.x, vec.y]); useEffect(() => { @@ -178,7 +182,7 @@ export const Player = (props: any) => { // Stop propagation to avoid selecting other cells // onPointerLeave does not stop at the first cell encountered by default e.stopPropagation(); - setHoveredTile(undefined); + setHoveredTile({ type: "None" }); }} > ); diff --git a/examples/clients/react/react-threejs/src/gameComponents/Players.tsx b/examples/clients/react/react-threejs/src/gameComponents/Players.tsx index e3d5341c..68991bdb 100644 --- a/examples/clients/react/react-threejs/src/gameComponents/Players.tsx +++ b/examples/clients/react/react-threejs/src/gameComponents/Players.tsx @@ -1,10 +1,10 @@ import { useDojo } from "@/dojo/useDojo"; import { Has, defineSystem } from "@dojoengine/recs"; import { Player } from "./Player"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { useElementStore } from "@/store"; -export const Players = (props: any) => { +export const Players = (_props: any) => { const { setup: { clientComponents: { Position }, diff --git a/examples/clients/react/react-threejs/src/main.tsx b/examples/clients/react/react-threejs/src/main.tsx index 5af1fa50..5ae7a19c 100644 --- a/examples/clients/react/react-threejs/src/main.tsx +++ b/examples/clients/react/react-threejs/src/main.tsx @@ -2,9 +2,9 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App.tsx"; import "./index.css"; -import { setup } from "./dojo/generated/setup.ts"; import { DojoProvider } from "./dojo/DojoContext.tsx"; import { dojoConfig } from "../dojoConfig.ts"; +import { setup } from "./dojo/setup.ts"; async function init() { const rootElement = document.getElementById("root"); diff --git a/examples/clients/react/react-threejs/tsconfig.json b/examples/clients/react/react-threejs/tsconfig.json index cfc2ec48..42bff674 100644 --- a/examples/clients/react/react-threejs/tsconfig.json +++ b/examples/clients/react/react-threejs/tsconfig.json @@ -1,21 +1,8 @@ { + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "node", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, "jsx": "react-jsx", - "strict": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, - "allowSyntheticDefaultImports": true, "baseUrl": ".", "paths": { "@/*": ["./src/*"] diff --git a/examples/clients/vanilla/phaser/src/dojo/createClientComponent.ts b/examples/clients/vanilla/phaser/src/dojo/createClientComponents.ts similarity index 87% rename from examples/clients/vanilla/phaser/src/dojo/createClientComponent.ts rename to examples/clients/vanilla/phaser/src/dojo/createClientComponents.ts index 5555bcf8..7b5bb860 100644 --- a/examples/clients/vanilla/phaser/src/dojo/createClientComponent.ts +++ b/examples/clients/vanilla/phaser/src/dojo/createClientComponents.ts @@ -1,5 +1,5 @@ import { overridableComponent } from "@dojoengine/recs"; -import { ContractComponents } from "./defineContractComponents"; +import { ContractComponents } from "./typescript/models.gen"; export type ClientComponents = ReturnType; diff --git a/examples/clients/vanilla/phaser/src/dojo/createSystemCalls.ts b/examples/clients/vanilla/phaser/src/dojo/createSystemCalls.ts new file mode 100644 index 00000000..46f699b4 --- /dev/null +++ b/examples/clients/vanilla/phaser/src/dojo/createSystemCalls.ts @@ -0,0 +1,109 @@ +import { Account } from "starknet"; +import { + Entity, + Has, + HasValue, + World, + defineSystem, + getComponentValue, +} from "@dojoengine/recs"; +import { uuid } from "@latticexyz/utils"; +import { ClientComponents } from "./createClientComponents"; +import { getEntityIdFromKeys } from "@dojoengine/utils"; +import type { IWorld } from "./typescript/contracts.gen"; +import { Direction } from "./typescript/models.gen"; + +export type SystemCalls = ReturnType; + +export function createSystemCalls( + { client }: { client: IWorld }, + { Position, Moves }: ClientComponents, + world: World +) { + const spawn = async (account: Account) => { + const entityId = getEntityIdFromKeys([ + BigInt(account.address), + ]) as Entity; + + const movesId = uuid(); + Moves.addOverride(movesId, { + entity: entityId, + value: { + player: BigInt(entityId), + remaining: + (getComponentValue(Moves, entityId)?.remaining || 0) + 100, + }, + }); + + const positionId = uuid(); + Position.addOverride(positionId, { + entity: entityId, + value: { + player: BigInt(entityId), + vec: { + x: 10 + (getComponentValue(Position, entityId)?.vec.x || 0), + y: 10 + (getComponentValue(Position, entityId)?.vec.y || 0), + }, + }, + }); + + try { + await client.actions.spawn({ + account, + }); + + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); + } catch (e) { + console.log(e); + Position.removeOverride(positionId); + Moves.removeOverride(movesId); + } finally { + Position.removeOverride(positionId); + Moves.removeOverride(movesId); + } + }; + + const move = async (account: Account, direction: Direction) => { + try { + await client.actions.move({ + account, + direction, + }); + + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); + } catch (e) { + console.log(e); + } + }; + + return { + spawn, + move, + }; +} diff --git a/examples/clients/vanilla/phaser/src/dojo/defineContractComponents.ts b/examples/clients/vanilla/phaser/src/dojo/defineContractComponents.ts deleted file mode 100644 index fd566b51..00000000 --- a/examples/clients/vanilla/phaser/src/dojo/defineContractComponents.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* Autogenerated file. Do not edit manually. */ - -import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; - -export type ContractComponents = Awaited< - ReturnType ->; - -export function defineContractComponents(world: World) { - return { - DirectionsAvailable: (() => { - return defineComponent( - world, - { player: RecsType.BigInt, directions: RecsType.StringArray }, - { - metadata: { - name: "dojo_starter-DirectionsAvailable", - types: ["contractaddress"], - customTypes: [], - }, - } - ); - })(), - Moved: (() => { - return defineComponent( - world, - { player: RecsType.BigInt, direction: RecsType.Number }, - { - metadata: { - name: "dojo_starter-Moved", - types: ["contractaddress", "enum"], - customTypes: ["Direction"], - }, - } - ); - })(), - Moves: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - remaining: RecsType.Number, - last_direction: RecsType.Number, - can_move: RecsType.Boolean, - }, - { - metadata: { - name: "dojo_starter-Moves", - types: ["contractaddress", "u8", "enum", "bool"], - customTypes: ["Direction"], - }, - } - ); - })(), - Position: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - vec: { x: RecsType.Number, y: RecsType.Number }, - }, - { - metadata: { - name: "dojo_starter-Position", - types: ["contractaddress", "u32", "u32"], - customTypes: ["Vec2"], - }, - } - ); - })(), - }; -} diff --git a/examples/clients/vanilla/phaser/src/dojo/defineContractSystems.ts b/examples/clients/vanilla/phaser/src/dojo/defineContractSystems.ts deleted file mode 100644 index 41066cf9..00000000 --- a/examples/clients/vanilla/phaser/src/dojo/defineContractSystems.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Account, AccountInterface } from "starknet"; -import { DojoProvider } from "@dojoengine/core"; -import { Config } from "../../dojoConfig.ts"; -import { Direction } from "./utils.ts"; -const NAMESPACE = "dojo_starter"; - -export interface MoveProps { - account: Account | AccountInterface; - direction: Direction; -} - -const handleError = (action: string, error: unknown) => { - console.error(`Error executing ${action}:`, error); - throw error; -}; - -export type IWorld = Awaited>; - -export async function setupWorld(provider: DojoProvider, _config: Config) { - const actions = () => ({ - spawn: async ({ account }: { account: AccountInterface }) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "spawn", - calldata: [], - }, - NAMESPACE - ); - } catch (error) { - handleError("spawn", error); - } - }, - - move: async ({ account, direction }: MoveProps) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "move", - calldata: [direction], - }, - NAMESPACE - ); - } catch (error) { - handleError("move", error); - } - }, - }); - - return { actions: actions() }; -} diff --git a/examples/clients/vanilla/phaser/src/dojo/models.ts b/examples/clients/vanilla/phaser/src/dojo/models.ts deleted file mode 100644 index 3ca498dc..00000000 --- a/examples/clients/vanilla/phaser/src/dojo/models.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ContractComponents } from "./defineContractComponents"; - -export type ClientModels = ReturnType; - -export function models({ - contractModels, -}: { - contractModels: ContractComponents; -}) { - return { - models: { - ...contractModels, - }, - }; -} diff --git a/examples/clients/vanilla/phaser/src/dojo/setup.ts b/examples/clients/vanilla/phaser/src/dojo/setup.ts index 217617b9..0ad7139c 100644 --- a/examples/clients/vanilla/phaser/src/dojo/setup.ts +++ b/examples/clients/vanilla/phaser/src/dojo/setup.ts @@ -1,99 +1,65 @@ -import { getSyncEntities } from "@dojoengine/state"; +import { DojoConfig, DojoProvider } from "@dojoengine/core"; import * as torii from "@dojoengine/torii-client"; - -import { models } from "./models.ts"; -import { systems } from "./systems.ts"; -import { defineContractComponents } from "./defineContractComponents.ts"; -import { world } from "./world.ts"; -import { Config } from "../../dojoConfig.ts"; -import { setupWorld } from "./defineContractSystems.ts"; - -import { DojoProvider } from "@dojoengine/core"; +import { createClientComponents } from "./createClientComponents"; +import { createSystemCalls } from "./createSystemCalls"; +import { defineContractComponents } from "./typescript/models.gen"; +import { world } from "./world"; +import { setupWorld } from "./typescript/contracts.gen"; +import { Account, ArraySignatureType } from "starknet"; import { BurnerManager } from "@dojoengine/create-burner"; -import { Account, RpcProvider } from "starknet"; -import { - ClientComponents, - createClientComponents, -} from "./createClientComponent.ts"; +import { getSyncEvents, getSyncEntities } from "@dojoengine/state"; export type SetupResult = Awaited>; -export type IDojo = Awaited>; -export async function setup({ ...config }: Config) { +export async function setup({ ...config }: DojoConfig) { // torii client - let toriiClient = null; - try { - toriiClient = await torii.createClient({ - rpcUrl: config.rpcUrl, - toriiUrl: config.toriiUrl, - relayUrl: "", - worldAddress: config.manifest.world.address || "", - }); - } catch (e) { - console.error("Failed to create torii client:", e); - throw e; - } + const toriiClient = await torii.createClient({ + rpcUrl: config.rpcUrl, + toriiUrl: config.toriiUrl, + relayUrl: "", + worldAddress: config.manifest.world.address || "", + }); // create contract components - let contractModels = null; - try { - contractModels = createClientComponents({ - contractComponents: defineContractComponents(world), - }); - } catch (e) { - console.error("Failed to create contract components:", e); - throw e; - } + const contractComponents = defineContractComponents(world); // create client components - const { models: clientModels } = models({ contractModels }); + const clientComponents = createClientComponents({ contractComponents }); - // fetch all existing entities from torii - let sync = null; - try { - sync = await getSyncEntities( - toriiClient, - contractModels as any, - [], - 1000 - ); - } catch (e) { - console.error("Failed to fetch sync:", e); - throw e; - } + // create dojo provider + const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); - let client = null; - try { - client = await setupWorld( - new DojoProvider(config.manifest, config.rpcUrl), - config - ); - } catch (e) { - console.error("Failed to create client:", e); - throw e; - } + // Sync all events + const eventSync = getSyncEvents( + toriiClient, + contractComponents as any, + undefined, + [] + ); - const rpcProvider = new RpcProvider({ - nodeUrl: config.rpcUrl, - }); + // Sync all entities + const sync = await getSyncEntities( + toriiClient, + contractComponents as any, + [] + ); - let burnerManager = null; - try { - burnerManager = new BurnerManager({ - masterAccount: new Account( - rpcProvider, - config.masterAddress, - config.masterPrivateKey - ), - feeTokenAddress: config.feeTokenAddress, - accountClassHash: config.accountClassHash, + // setup world + const client = await setupWorld(dojoProvider); - rpcProvider, - }); - } catch (e) { - console.log("Failed to create burner manager:", e); - throw e; - } + // create burner manager + const burnerManager = new BurnerManager({ + masterAccount: new Account( + { + nodeUrl: config.rpcUrl, + }, + config.masterAddress, + config.masterPrivateKey + ), + accountClassHash: config.accountClassHash, + rpcProvider: dojoProvider.provider, + feeTokenAddress: config.feeTokenAddress, + }); try { await burnerManager.init(); @@ -103,26 +69,20 @@ export async function setup({ ...config }: Config) { } catch (e) { console.error(e); } - const actions = systems({ - client, - clientModels: clientModels as ClientComponents, - contractComponents: contractModels, - }); - const account = burnerManager.getActiveAccount(); - if (null === account || undefined === account) { - throw new Error("failed to get active account"); - } return { client, - clientModels, - contractComponents: clientModels, - systemCalls: actions.actions, + clientComponents, + contractComponents, + systemCalls: createSystemCalls({ client }, clientComponents, world), + publish: (typedData: string, signature: ArraySignatureType) => { + toriiClient.publishMessage(typedData, signature); + }, config, - world, + dojoProvider, burnerManager, - rpcProvider, + toriiClient, + eventSync, sync, - account, }; } diff --git a/examples/clients/vanilla/phaser/src/dojo/systems.ts b/examples/clients/vanilla/phaser/src/dojo/systems.ts deleted file mode 100644 index 973dce19..00000000 --- a/examples/clients/vanilla/phaser/src/dojo/systems.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { AccountInterface } from "starknet"; -import { Entity, getComponentValue } from "@dojoengine/recs"; -import { uuid } from "@latticexyz/utils"; -import { ClientComponents } from "./createClientComponent"; -import { Direction, updatePositionWithDirection } from "./utils"; -import { - getEntityIdFromKeys, - getEvents, - setComponentsFromEvents, -} from "@dojoengine/utils"; -import type { IWorld } from "./defineContractSystems"; -import { ContractComponents } from "./defineContractComponents"; - -export type SystemCalls = ReturnType; - -export function systems({ - client, - clientModels: { Position, Moves }, - contractComponents, -}: { - client: IWorld; - clientModels: ClientComponents; - contractComponents: ContractComponents; -}) { - function actions() { - const spawn = async (account: AccountInterface) => { - const entityId = getEntityIdFromKeys([ - BigInt(account.address), - ]) as Entity; - - const positionId = uuid(); - Position.addOverride(positionId, { - entity: entityId, - value: { player: BigInt(entityId), vec: { x: 10, y: 10 } }, - }); - - const movesId = uuid(); - Moves.addOverride(movesId, { - entity: entityId, - value: { - player: BigInt(entityId), - remaining: 100, - last_direction: 0, - }, - }); - - try { - const { transaction_hash } = (await client.actions.spawn({ - account, - })) as { transaction_hash: string }; - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); - } catch (e) { - console.error(e); - Position.removeOverride(positionId); - Moves.removeOverride(movesId); - } - }; - - const move = async ( - account: AccountInterface, - direction: Direction - ) => { - const entityId = getEntityIdFromKeys([ - BigInt(account.address), - ]) as Entity; - - const positionId = uuid(); - Position.addOverride(positionId, { - entity: entityId, - value: { - player: BigInt(entityId), - vec: updatePositionWithDirection( - direction, - getComponentValue(Position, entityId) as any - ).vec, - }, - }); - - const movesId = uuid(); - Moves.addOverride(movesId, { - entity: entityId, - value: { - player: BigInt(entityId), - remaining: - (getComponentValue(Moves, entityId)?.remaining || 0) - - 1, - }, - }); - - try { - const { transaction_hash } = (await client.actions.move({ - account, - direction, - })) as { transaction_hash: string }; - - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); - } catch (e) { - console.error(e); - Position.removeOverride(positionId); - Moves.removeOverride(movesId); - } - }; - return { spawn, move }; - } - - return { - actions: actions(), - }; -} diff --git a/examples/clients/vanilla/phaser/src/dojo/typescript/contracts.gen.ts b/examples/clients/vanilla/phaser/src/dojo/typescript/contracts.gen.ts new file mode 100644 index 00000000..411d0500 --- /dev/null +++ b/examples/clients/vanilla/phaser/src/dojo/typescript/contracts.gen.ts @@ -0,0 +1,86 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { Account } from "starknet"; +import { DojoProvider } from "@dojoengine/core"; +import * as models from "./models.gen"; + +export type IWorld = Awaited>; + +export async function setupWorld(provider: DojoProvider) { + // System definitions for `dojo_starter-actions` contract + function actions() { + const contract_name = "actions"; + + // Call the `spawn` system with the specified Account and calldata + const spawn = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "spawn", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `move` system with the specified Account and calldata + const move = async (props: { + account: Account; + direction: models.Direction; + }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "move", + calldata: [ + ["None", "Left", "Right", "Up", "Down"].indexOf( + props.direction.type + ), + ], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `world` system with the specified Account and calldata + const world = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "world", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + return { + spawn, + move, + world, + }; + } + + return { + actions: actions(), + }; +} diff --git a/examples/clients/vanilla/phaser/src/dojo/typescript/models.gen.ts b/examples/clients/vanilla/phaser/src/dojo/typescript/models.gen.ts new file mode 100644 index 00000000..051689eb --- /dev/null +++ b/examples/clients/vanilla/phaser/src/dojo/typescript/models.gen.ts @@ -0,0 +1,193 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; + +export type ContractComponents = Awaited< + ReturnType +>; + +// Type definition for `dojo_starter::models::Direction` enum +export type Direction = + | { type: "None" } + | { type: "Left" } + | { type: "Right" } + | { type: "Up" } + | { type: "Down" }; + +export const DirectionDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `dojo::model::layout::Layout` enum +export type Layout = + | { type: "Fixed"; value: RecsType.NumberArray } + | { type: "Struct"; value: RecsType.StringArray } + | { type: "Tuple"; value: RecsType.StringArray } + | { type: "Array"; value: RecsType.StringArray } + | { type: "ByteArray" } + | { type: "Enum"; value: RecsType.StringArray }; + +export const LayoutDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `core::byte_array::ByteArray` struct +export interface ByteArray { + data: String[]; + pending_word: BigInt; + pending_word_len: Number; +} +export const ByteArrayDefinition = { + data: RecsType.StringArray, + pending_word: RecsType.BigInt, + pending_word_len: RecsType.Number, +}; + +// Type definition for `dojo::model::layout::FieldLayout` struct +export interface FieldLayout { + selector: BigInt; + layout: Layout; +} +export const FieldLayoutDefinition = { + selector: RecsType.BigInt, + layout: LayoutDefinition, +}; + +// Type definition for `dojo_starter::models::Moves` struct +export interface Moves { + player: BigInt; + remaining: Number; + last_direction: Direction; + can_move: Boolean; +} +export const MovesDefinition = { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: DirectionDefinition, + can_move: RecsType.Boolean, +}; + +// Type definition for `dojo_starter::models::DirectionsAvailable` struct +export interface DirectionsAvailable { + player: BigInt; + directions: String[]; +} +export const DirectionsAvailableDefinition = { + player: RecsType.BigInt, + directions: RecsType.StringArray, +}; + +// Type definition for `dojo_starter::systems::actions::actions::Moved` struct +export interface Moved { + player: BigInt; + direction: Direction; +} +export const MovedDefinition = { + player: RecsType.BigInt, + direction: DirectionDefinition, +}; + +// Type definition for `dojo_starter::models::Vec2` struct +export interface Vec2 { + x: Number; + y: Number; +} +export const Vec2Definition = { + x: RecsType.Number, + y: RecsType.Number, +}; + +// Type definition for `dojo_starter::models::Position` struct +export interface Position { + player: BigInt; + vec: Vec2; +} +export const PositionDefinition = { + player: RecsType.BigInt, + vec: Vec2Definition, +}; + +export function defineContractComponents(world: World) { + return { + // Model definition for `dojo_starter::models::Moves` model + Moves: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: RecsType.String, + can_move: RecsType.Boolean, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moves", + types: ["ContractAddress", "u8", "Direction", "bool"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::DirectionsAvailable` model + DirectionsAvailable: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + directions: RecsType.StringArray, + }, + { + metadata: { + namespace: "dojo_starter", + name: "DirectionsAvailable", + types: ["ContractAddress", "array"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::systems::actions::actions::Moved` model + Moved: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + direction: RecsType.String, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moved", + types: ["ContractAddress", "Direction"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::Position` model + Position: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + vec: Vec2Definition, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Position", + types: ["ContractAddress"], + customTypes: ["Vec2"], + }, + } + ); + })(), + }; +} diff --git a/examples/clients/vanilla/phaser/src/dojo/utils.ts b/examples/clients/vanilla/phaser/src/dojo/utils.ts deleted file mode 100644 index 1ec8b000..00000000 --- a/examples/clients/vanilla/phaser/src/dojo/utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -export enum Direction { - Left = 1, - Right = 2, - Up = 3, - Down = 4, -} - -export function updatePositionWithDirection( - direction: Direction, - value: { vec: { x: number; y: number } } -) { - switch (direction) { - case Direction.Left: - value.vec.x--; - break; - case Direction.Right: - value.vec.x++; - break; - case Direction.Up: - value.vec.y--; - break; - case Direction.Down: - value.vec.y++; - break; - default: - throw new Error("Invalid direction provided"); - } - return value; -} diff --git a/examples/clients/vanilla/phaser/src/scenes/scene-main.ts b/examples/clients/vanilla/phaser/src/scenes/scene-main.ts index 51d8d6fa..c5078d4e 100644 --- a/examples/clients/vanilla/phaser/src/scenes/scene-main.ts +++ b/examples/clients/vanilla/phaser/src/scenes/scene-main.ts @@ -1,9 +1,10 @@ import { Scene } from "phaser"; import { Chunk } from "../entities"; -import { IDojo } from "../dojo/setup"; -import { Direction } from "../dojo/utils"; + +import { SetupResult } from "../dojo/setup"; +import { Account } from "starknet"; export default class SceneMain extends Scene { - dojo: IDojo; + dojo: SetupResult; chunkSize: number; tileSize: number; cameraSpeed: number; @@ -14,7 +15,7 @@ export default class SceneMain extends Scene { keyA: Phaser.Input.Keyboard.Key | null; keyD: Phaser.Input.Keyboard.Key | null; - constructor(dojo: IDojo) { + constructor(dojo: SetupResult) { super({ key: "MainScene" }); this.dojo = dojo; @@ -72,7 +73,7 @@ export default class SceneMain extends Scene { Phaser.Input.Keyboard.KeyCodes.D ); - this.dojo.systemCalls.spawn(this.dojo.account); + this.dojo.systemCalls.spawn(this.dojo.burnerManager.account as Account); } getChunk(x: number, y: number) { @@ -133,25 +134,37 @@ export default class SceneMain extends Scene { if (null !== this.keyW && this.keyW.isDown) { this.followPoint.y -= this.cameraSpeed; this.cameras.main.centerOn(this.followPoint.x, this.followPoint.y); - this.dojo.systemCalls.move(this.dojo.account, Direction.Up); + this.dojo.systemCalls.move( + this.dojo.burnerManager.account as Account, + { type: "Up" } + ); return; } if (null !== this.keyS && this.keyS.isDown) { this.followPoint.y += this.cameraSpeed; this.cameras.main.centerOn(this.followPoint.x, this.followPoint.y); - this.dojo.systemCalls.move(this.dojo.account, Direction.Down); + this.dojo.systemCalls.move( + this.dojo.burnerManager.account as Account, + { type: "Down" } + ); return; } if (null !== this.keyA && this.keyA.isDown) { this.followPoint.x -= this.cameraSpeed; this.cameras.main.centerOn(this.followPoint.x, this.followPoint.y); - this.dojo.systemCalls.move(this.dojo.account, Direction.Left); + this.dojo.systemCalls.move( + this.dojo.burnerManager.account as Account, + { type: "Left" } + ); return; } if (null !== this.keyD && this.keyD.isDown) { this.followPoint.x += this.cameraSpeed; this.cameras.main.centerOn(this.followPoint.x, this.followPoint.y); - this.dojo.systemCalls.move(this.dojo.account, Direction.Right); + this.dojo.systemCalls.move( + this.dojo.burnerManager.account as Account, + { type: "Right" } + ); return; } diff --git a/examples/clients/vue/vue-app/src/App.vue b/examples/clients/vue/vue-app/src/App.vue index 27ae8529..3bd7b59c 100644 --- a/examples/clients/vue/vue-app/src/App.vue +++ b/examples/clients/vue/vue-app/src/App.vue @@ -1,15 +1,17 @@ - - - - diff --git a/examples/clients/vue/vue-app/src/dojo/createClientComponents.ts b/examples/clients/vue/vue-app/src/dojo/createClientComponents.ts index 550ad13b..7b5bb860 100644 --- a/examples/clients/vue/vue-app/src/dojo/createClientComponents.ts +++ b/examples/clients/vue/vue-app/src/dojo/createClientComponents.ts @@ -1,5 +1,5 @@ import { overridableComponent } from "@dojoengine/recs"; -import { ContractComponents } from "./generated/contractComponents"; +import { ContractComponents } from "./typescript/models.gen"; export type ClientComponents = ReturnType; diff --git a/examples/clients/vue/vue-app/src/dojo/createSystemCalls.ts b/examples/clients/vue/vue-app/src/dojo/createSystemCalls.ts index 6c35e7e8..46f699b4 100644 --- a/examples/clients/vue/vue-app/src/dojo/createSystemCalls.ts +++ b/examples/clients/vue/vue-app/src/dojo/createSystemCalls.ts @@ -1,57 +1,71 @@ import { Account } from "starknet"; -import { Entity, getComponentValue } from "@dojoengine/recs"; +import { + Entity, + Has, + HasValue, + World, + defineSystem, + getComponentValue, +} from "@dojoengine/recs"; import { uuid } from "@latticexyz/utils"; import { ClientComponents } from "./createClientComponents"; -import { Direction, updatePositionWithDirection } from "../utils"; -import { - getEntityIdFromKeys, - getEvents, - setComponentsFromEvents, -} from "@dojoengine/utils"; -import { ContractComponents } from "./generated/contractComponents"; -import type { IWorld } from "./generated/generated"; +import { getEntityIdFromKeys } from "@dojoengine/utils"; +import type { IWorld } from "./typescript/contracts.gen"; +import { Direction } from "./typescript/models.gen"; export type SystemCalls = ReturnType; export function createSystemCalls( { client }: { client: IWorld }, - contractComponents: ContractComponents, - { Position, Moves }: ClientComponents + { Position, Moves }: ClientComponents, + world: World ) { const spawn = async (account: Account) => { const entityId = getEntityIdFromKeys([ BigInt(account.address), ]) as Entity; - const positionId = uuid(); - Position.addOverride(positionId, { + const movesId = uuid(); + Moves.addOverride(movesId, { entity: entityId, - value: { player: BigInt(entityId), vec: { x: 10, y: 10 } }, + value: { + player: BigInt(entityId), + remaining: + (getComponentValue(Moves, entityId)?.remaining || 0) + 100, + }, }); - const movesId = uuid(); - Moves.addOverride(movesId, { + const positionId = uuid(); + Position.addOverride(positionId, { entity: entityId, value: { player: BigInt(entityId), - remaining: 100, - last_direction: 0, + vec: { + x: 10 + (getComponentValue(Position, entityId)?.vec.x || 0), + y: 10 + (getComponentValue(Position, entityId)?.vec.y || 0), + }, }, }); try { - const { transaction_hash } = await client.actions.spawn({ + await client.actions.spawn({ account, }); - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); Position.removeOverride(positionId); @@ -63,53 +77,28 @@ export function createSystemCalls( }; const move = async (account: Account, direction: Direction) => { - const entityId = getEntityIdFromKeys([ - BigInt(account.address), - ]) as Entity; - - const positionId = uuid(); - Position.addOverride(positionId, { - entity: entityId, - value: { - player: BigInt(entityId), - vec: updatePositionWithDirection( - direction, - getComponentValue(Position, entityId) as any - ).vec, - }, - }); - - const movesId = uuid(); - Moves.addOverride(movesId, { - entity: entityId, - value: { - player: BigInt(entityId), - remaining: - (getComponentValue(Moves, entityId)?.remaining || 0) - 1, - }, - }); - try { - const { transaction_hash } = await client.actions.move({ + await client.actions.move({ account, direction, }); - setComponentsFromEvents( - contractComponents, - getEvents( - await account.waitForTransaction(transaction_hash, { - retryInterval: 100, - }) - ) - ); + // Wait for the indexer to update the entity + // By doing this we keep the optimistic UI in sync with the actual state + await new Promise((resolve) => { + defineSystem( + world, + [ + Has(Moves), + HasValue(Moves, { player: BigInt(account.address) }), + ], + () => { + resolve(); + } + ); + }); } catch (e) { console.log(e); - Position.removeOverride(positionId); - Moves.removeOverride(movesId); - } finally { - Position.removeOverride(positionId); - Moves.removeOverride(movesId); } }; diff --git a/examples/clients/vue/vue-app/src/dojo/generated/contractComponents.ts b/examples/clients/vue/vue-app/src/dojo/generated/contractComponents.ts deleted file mode 100644 index 596871c8..00000000 --- a/examples/clients/vue/vue-app/src/dojo/generated/contractComponents.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Autogenerated file. Do not edit manually. - * Generated using @dojoengine/core - * Command: npx @dojoengine/core - */ - -import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; - -export type ContractComponents = Awaited< - ReturnType ->; - -export function defineContractComponents(world: World) { - return { - DirectionsAvailable: (() => { - return defineComponent( - world, - { player: RecsType.BigInt, directions: RecsType.StringArray }, - { - metadata: { - name: "dojo_starter-DirectionsAvailable", - types: ["contractaddress"], - customTypes: ["Direction"], - }, - } - ); - })(), - Moves: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - remaining: RecsType.Number, - last_direction: RecsType.Number, - can_move: RecsType.Boolean, - }, - { - metadata: { - name: "dojo_starter-Moves", - types: ["contractaddress", "u8", "enum", "bool"], - customTypes: ["Direction"], - }, - } - ); - })(), - Position: (() => { - return defineComponent( - world, - { - player: RecsType.BigInt, - vec: { x: RecsType.Number, y: RecsType.Number }, - }, - { - metadata: { - name: "dojo_starter-Position", - types: ["contractaddress", "u32", "u32"], - customTypes: ["Vec2"], - }, - } - ); - })(), - }; -} diff --git a/examples/clients/vue/vue-app/src/dojo/generated/generated.ts b/examples/clients/vue/vue-app/src/dojo/generated/generated.ts deleted file mode 100644 index b10d2654..00000000 --- a/examples/clients/vue/vue-app/src/dojo/generated/generated.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Account, AccountInterface } from "starknet"; -import { DojoProvider } from "@dojoengine/core"; -import { Direction } from "../../utils"; - -const NAMESPACE = "dojo_starter"; - -export interface IWorld { - actions: { - spawn: (props: { account: AccountInterface }) => Promise; - move: (props: MoveProps) => Promise; - }; -} - -export interface MoveProps { - account: Account | AccountInterface; - direction: Direction; -} - -const handleError = (action: string, error: unknown) => { - console.error(`Error executing ${action}:`, error); - throw error; -}; - -export const setupWorld = async (provider: DojoProvider): Promise => { - const actions = () => ({ - spawn: async ({ account }: { account: AccountInterface }) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "spawn", - calldata: [], - }, - NAMESPACE - ); - } catch (error) { - handleError("spawn", error); - } - }, - - move: async ({ account, direction }: MoveProps) => { - try { - return await provider.execute( - account, - { - contractName: "actions", - entrypoint: "move", - calldata: [direction], - }, - NAMESPACE - ); - } catch (error) { - handleError("move", error); - } - }, - }); - - return { actions: actions() }; -}; diff --git a/examples/clients/vue/vue-app/src/dojo/generated/setup.ts b/examples/clients/vue/vue-app/src/dojo/generated/setup.ts deleted file mode 100644 index 5edebeba..00000000 --- a/examples/clients/vue/vue-app/src/dojo/generated/setup.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { getSyncEntities } from "@dojoengine/state"; -import { - DojoConfig, - DojoProvider, - createModelTypedData, -} from "@dojoengine/core"; -import * as torii from "@dojoengine/torii-client"; -import { createClientComponents } from "../createClientComponents"; -import { createSystemCalls } from "../createSystemCalls"; -import { defineContractComponents } from "./contractComponents"; -import { world } from "./world"; -import { setupWorld } from "./generated"; -import { - ArraySignatureType, - TypedData, - WeierstrassSignatureType, -} from "starknet"; - -export type SetupResult = Awaited>; - -export async function setup({ ...config }: DojoConfig) { - // torii client - const toriiClient = await torii.createClient({ - rpcUrl: config.rpcUrl, - toriiUrl: config.toriiUrl, - relayUrl: "", - worldAddress: config.manifest.world.address || "", - }); - - // create contract components - const contractComponents = defineContractComponents(world); - - // create client components - const clientComponents = createClientComponents({ contractComponents }); - - // fetch all existing entities from torii - const sync = await getSyncEntities( - toriiClient, - contractComponents as any, - [] - ); - - // create dojo provider - const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); - - // setup world - const client = await setupWorld(dojoProvider); - - return { - client, - clientComponents, - contractComponents, - systemCalls: createSystemCalls( - { client }, - contractComponents, - clientComponents - ), - publish: (typedData: string, signature: ArraySignatureType) => { - toriiClient.publishMessage(typedData, signature); - }, - config, - dojoProvider, - sync, - }; -} diff --git a/examples/clients/vue/vue-app/src/dojo/generated/world.ts b/examples/clients/vue/vue-app/src/dojo/generated/world.ts deleted file mode 100644 index 960676e5..00000000 --- a/examples/clients/vue/vue-app/src/dojo/generated/world.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createWorld } from "@dojoengine/recs"; - -export const world = createWorld(); diff --git a/examples/clients/vue/vue-app/src/dojo/setup.ts b/examples/clients/vue/vue-app/src/dojo/setup.ts new file mode 100644 index 00000000..f1ba6ded --- /dev/null +++ b/examples/clients/vue/vue-app/src/dojo/setup.ts @@ -0,0 +1,88 @@ +import { DojoConfig, DojoProvider } from "@dojoengine/core"; +import * as torii from "@dojoengine/torii-client"; +import { createClientComponents } from "./createClientComponents"; +import { createSystemCalls } from "./createSystemCalls"; +import { defineContractComponents } from "./typescript/models.gen"; +import { world } from "./world"; +import { setupWorld } from "./typescript/contracts.gen"; +import { Account, ArraySignatureType } from "starknet"; +import { BurnerManager } from "@dojoengine/create-burner"; +import { getSyncEvents, getSyncEntities } from "@dojoengine/state"; + +export type SetupResult = Awaited>; + +export async function setup({ ...config }: DojoConfig) { + // torii client + const toriiClient = await torii.createClient({ + rpcUrl: config.rpcUrl, + toriiUrl: config.toriiUrl, + relayUrl: "", + worldAddress: config.manifest.world.address || "", + }); + + // create contract components + const contractComponents = defineContractComponents(world); + + // create client components + const clientComponents = createClientComponents({ contractComponents }); + + // create dojo provider + const dojoProvider = new DojoProvider(config.manifest, config.rpcUrl); + + // Sync all events + const eventSync = getSyncEvents( + toriiClient, + contractComponents as any, + undefined, + [] + ); + + // Sync all entities + const sync = await getSyncEntities( + toriiClient, + contractComponents as any, + [] + ); + + // setup world + const client = await setupWorld(dojoProvider); + + // create burner manager + const burnerManager = new BurnerManager({ + masterAccount: new Account( + { + nodeUrl: config.rpcUrl, + }, + config.masterAddress, + config.masterPrivateKey + ), + accountClassHash: config.accountClassHash, + rpcProvider: dojoProvider.provider, + feeTokenAddress: config.feeTokenAddress, + }); + + try { + await burnerManager.init(); + if (burnerManager.list().length === 0) { + await burnerManager.create(); + } + } catch (e) { + console.error(e); + } + + return { + client, + clientComponents, + contractComponents, + systemCalls: createSystemCalls({ client }, clientComponents, world), + publish: (typedData: string, signature: ArraySignatureType) => { + toriiClient.publishMessage(typedData, signature); + }, + config, + dojoProvider, + account: burnerManager.account as Account, + toriiClient, + eventSync, + sync, + }; +} diff --git a/examples/clients/vue/vue-app/src/dojo/typescript/contracts.gen.ts b/examples/clients/vue/vue-app/src/dojo/typescript/contracts.gen.ts new file mode 100644 index 00000000..0f7fac11 --- /dev/null +++ b/examples/clients/vue/vue-app/src/dojo/typescript/contracts.gen.ts @@ -0,0 +1,86 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { Account, byteArray } from "starknet"; +import { DojoProvider } from "@dojoengine/core"; +import * as models from "./models.gen"; + +export type IWorld = Awaited>; + +export async function setupWorld(provider: DojoProvider) { + // System definitions for `dojo_starter-actions` contract + function actions() { + const contract_name = "actions"; + + // Call the `spawn` system with the specified Account and calldata + const spawn = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "spawn", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `move` system with the specified Account and calldata + const move = async (props: { + account: Account; + direction: models.Direction; + }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "move", + calldata: [ + ["None", "Left", "Right", "Up", "Down"].indexOf( + props.direction.type + ), + ], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + // Call the `world` system with the specified Account and calldata + const world = async (props: { account: Account }) => { + try { + return await provider.execute( + props.account, + { + contractName: contract_name, + entrypoint: "world", + calldata: [], + }, + "dojo_starter" + ); + } catch (error) { + console.error("Error executing spawn:", error); + throw error; + } + }; + + return { + spawn, + move, + world, + }; + } + + return { + actions: actions(), + }; +} diff --git a/examples/clients/vue/vue-app/src/dojo/typescript/models.gen.ts b/examples/clients/vue/vue-app/src/dojo/typescript/models.gen.ts new file mode 100644 index 00000000..051689eb --- /dev/null +++ b/examples/clients/vue/vue-app/src/dojo/typescript/models.gen.ts @@ -0,0 +1,193 @@ +// Generated by dojo-bindgen on Thu, 22 Aug 2024 20:04:33 +0000. Do not modify this file manually. +// Import the necessary types from the recs SDK +// generate again with `sozo build --typescript` +import { defineComponent, Type as RecsType, World } from "@dojoengine/recs"; + +export type ContractComponents = Awaited< + ReturnType +>; + +// Type definition for `dojo_starter::models::Direction` enum +export type Direction = + | { type: "None" } + | { type: "Left" } + | { type: "Right" } + | { type: "Up" } + | { type: "Down" }; + +export const DirectionDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `dojo::model::layout::Layout` enum +export type Layout = + | { type: "Fixed"; value: RecsType.NumberArray } + | { type: "Struct"; value: RecsType.StringArray } + | { type: "Tuple"; value: RecsType.StringArray } + | { type: "Array"; value: RecsType.StringArray } + | { type: "ByteArray" } + | { type: "Enum"; value: RecsType.StringArray }; + +export const LayoutDefinition = { + type: RecsType.String, + value: RecsType.String, +}; + +// Type definition for `core::byte_array::ByteArray` struct +export interface ByteArray { + data: String[]; + pending_word: BigInt; + pending_word_len: Number; +} +export const ByteArrayDefinition = { + data: RecsType.StringArray, + pending_word: RecsType.BigInt, + pending_word_len: RecsType.Number, +}; + +// Type definition for `dojo::model::layout::FieldLayout` struct +export interface FieldLayout { + selector: BigInt; + layout: Layout; +} +export const FieldLayoutDefinition = { + selector: RecsType.BigInt, + layout: LayoutDefinition, +}; + +// Type definition for `dojo_starter::models::Moves` struct +export interface Moves { + player: BigInt; + remaining: Number; + last_direction: Direction; + can_move: Boolean; +} +export const MovesDefinition = { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: DirectionDefinition, + can_move: RecsType.Boolean, +}; + +// Type definition for `dojo_starter::models::DirectionsAvailable` struct +export interface DirectionsAvailable { + player: BigInt; + directions: String[]; +} +export const DirectionsAvailableDefinition = { + player: RecsType.BigInt, + directions: RecsType.StringArray, +}; + +// Type definition for `dojo_starter::systems::actions::actions::Moved` struct +export interface Moved { + player: BigInt; + direction: Direction; +} +export const MovedDefinition = { + player: RecsType.BigInt, + direction: DirectionDefinition, +}; + +// Type definition for `dojo_starter::models::Vec2` struct +export interface Vec2 { + x: Number; + y: Number; +} +export const Vec2Definition = { + x: RecsType.Number, + y: RecsType.Number, +}; + +// Type definition for `dojo_starter::models::Position` struct +export interface Position { + player: BigInt; + vec: Vec2; +} +export const PositionDefinition = { + player: RecsType.BigInt, + vec: Vec2Definition, +}; + +export function defineContractComponents(world: World) { + return { + // Model definition for `dojo_starter::models::Moves` model + Moves: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + remaining: RecsType.Number, + last_direction: RecsType.String, + can_move: RecsType.Boolean, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moves", + types: ["ContractAddress", "u8", "Direction", "bool"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::DirectionsAvailable` model + DirectionsAvailable: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + directions: RecsType.StringArray, + }, + { + metadata: { + namespace: "dojo_starter", + name: "DirectionsAvailable", + types: ["ContractAddress", "array"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::systems::actions::actions::Moved` model + Moved: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + direction: RecsType.String, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Moved", + types: ["ContractAddress", "Direction"], + customTypes: [], + }, + } + ); + })(), + + // Model definition for `dojo_starter::models::Position` model + Position: (() => { + return defineComponent( + world, + { + player: RecsType.BigInt, + vec: Vec2Definition, + }, + { + metadata: { + namespace: "dojo_starter", + name: "Position", + types: ["ContractAddress"], + customTypes: ["Vec2"], + }, + } + ); + })(), + }; +} diff --git a/examples/clients/react/react-threejs/src/dojo/generated/world.ts b/examples/clients/vue/vue-app/src/dojo/world.ts similarity index 100% rename from examples/clients/react/react-threejs/src/dojo/generated/world.ts rename to examples/clients/vue/vue-app/src/dojo/world.ts diff --git a/examples/clients/vue/vue-app/tsconfig.json b/examples/clients/vue/vue-app/tsconfig.json index 122d09c0..f5be5491 100644 --- a/examples/clients/vue/vue-app/tsconfig.json +++ b/examples/clients/vue/vue-app/tsconfig.json @@ -6,16 +6,19 @@ "module": "ESNext", "skipLibCheck": true, "moduleResolution": "node", + "esModuleInterop": true, "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "preserve", "strict": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } }, "include": ["src", "dojoConfig.ts"], "exclude": ["node_modules"], diff --git a/examples/clients/vue/vue-app/tsconfig.node.json b/examples/clients/vue/vue-app/tsconfig.node.json index 26063d85..e2214750 100644 --- a/examples/clients/vue/vue-app/tsconfig.node.json +++ b/examples/clients/vue/vue-app/tsconfig.node.json @@ -3,7 +3,6 @@ "composite": true, "skipLibCheck": true, "module": "ESNext", - "moduleResolution": "bundler", "allowSyntheticDefaultImports": true }, "include": ["vite.config.ts"] diff --git a/examples/dojo/dojo-starter/.github/workflows/test.yaml b/examples/dojo/dojo-starter/.github/workflows/test.yaml index 101b7433..c4f4bcda 100644 --- a/examples/dojo/dojo-starter/.github/workflows/test.yaml +++ b/examples/dojo/dojo-starter/.github/workflows/test.yaml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v3 - run: curl -L https://install.dojoengine.org | bash - - run: /home/runner/.config/.dojo/bin/dojoup -v v1.0.0-alpha.6 + - run: /home/runner/.config/.dojo/bin/dojoup -v v1.0.0-alpha.9 - run: | /home/runner/.config/.dojo/bin/sozo build /home/runner/.config/.dojo/bin/sozo test diff --git a/examples/dojo/dojo-starter/README.md b/examples/dojo/dojo-starter/README.md index 5979ee0d..05061c94 100644 --- a/examples/dojo/dojo-starter/README.md +++ b/examples/dojo/dojo-starter/README.md @@ -41,7 +41,7 @@ sozo build sozo migrate apply # Start Torii -torii --world 0x403b5f047b8c4797139e30801e310473d99ca6877d19e0f27506f353f8f70f7 --allowed-origins "*" +torii --world 0x3b34889efbdf01f707d5d7421f112e8fb85a42fb6f2e5422c75ce3253148b0e --allowed-origins "*" ``` --- diff --git a/examples/dojo/dojo-starter/Scarb.lock b/examples/dojo/dojo-starter/Scarb.lock index 33297f7b..e994b92e 100644 --- a/examples/dojo/dojo-starter/Scarb.lock +++ b/examples/dojo/dojo-starter/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "dojo" version = "1.0.0-alpha.4" -source = "git+https://github.com/dojoengine/dojo?tag=v1.0.0-alpha.6#ede7930735c708572af8a87f81bb76354a401b3a" +source = "git+https://github.com/dojoengine/dojo?tag=v1.0.0-alpha.9#e42ce0c220a59d75c5b08e87a81de12cfdc27a55" dependencies = [ "dojo_plugin", ] diff --git a/examples/dojo/dojo-starter/Scarb.toml b/examples/dojo/dojo-starter/Scarb.toml index b0bc4a64..1a551d7a 100644 --- a/examples/dojo/dojo-starter/Scarb.toml +++ b/examples/dojo/dojo-starter/Scarb.toml @@ -12,6 +12,6 @@ spawn = "./scripts/spawn.sh" move = "./scripts/move.sh" [dependencies] -dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.6" } +dojo = { git = "https://github.com/dojoengine/dojo", tag = "v1.0.0-alpha.9" } [[target.dojo]] diff --git a/examples/dojo/dojo-starter/dojo_dev.toml b/examples/dojo/dojo-starter/dojo_dev.toml index f73c0596..a4e1ba87 100644 --- a/examples/dojo/dojo-starter/dojo_dev.toml +++ b/examples/dojo/dojo-starter/dojo_dev.toml @@ -20,4 +20,4 @@ rpc_url = "http://localhost:5050/" # Default account for katana with seed = 0 account_address = "0xb3ff441a68610b30fd5e2abbf3a1548eb6ba6f3559f2862bf2dc757e5828ca" private_key = "0x2bbf4f9fd0bbb2e60b0316c1fe0b76cf7a4d0198bd493ced9b8df2a3a24d68a" -world_address = "0x403b5f047b8c4797139e30801e310473d99ca6877d19e0f27506f353f8f70f7" +world_address = "0x3b34889efbdf01f707d5d7421f112e8fb85a42fb6f2e5422c75ce3253148b0e" diff --git a/examples/dojo/dojo-starter/manifests/dev/base/contracts/dojo_starter-actions-7a1c7102.toml b/examples/dojo/dojo-starter/manifests/dev/base/contracts/dojo_starter-actions-7a1c7102.toml index dd755415..9b53c897 100644 --- a/examples/dojo/dojo-starter/manifests/dev/base/contracts/dojo_starter-actions-7a1c7102.toml +++ b/examples/dojo/dojo-starter/manifests/dev/base/contracts/dojo_starter-actions-7a1c7102.toml @@ -1,6 +1,6 @@ kind = "DojoContract" -class_hash = "0x4f10da1ba615523c1a4b9e2ad40df134edbcf2d971a1074efbd9772c86c2173" -original_class_hash = "0x4f10da1ba615523c1a4b9e2ad40df134edbcf2d971a1074efbd9772c86c2173" +class_hash = "0x7304354bfea03508e0a8beaca98c9580c852a0e350b74dcafb55192c62fa3f4" +original_class_hash = "0x7304354bfea03508e0a8beaca98c9580c852a0e350b74dcafb55192c62fa3f4" base_class_hash = "0x0" abi = "manifests/dev/base/abis/contracts/dojo_starter-actions-7a1c7102.json" reads = [] diff --git a/examples/dojo/dojo-starter/manifests/dev/base/dojo-world.toml b/examples/dojo/dojo-starter/manifests/dev/base/dojo-world.toml index 6f56ceed..2b3b1409 100644 --- a/examples/dojo/dojo-starter/manifests/dev/base/dojo-world.toml +++ b/examples/dojo/dojo-starter/manifests/dev/base/dojo-world.toml @@ -1,6 +1,6 @@ kind = "Class" -class_hash = "0x458d0ce5b14a4844092bdb62050f462d53362304a13febbac6d973691d61be2" -original_class_hash = "0x458d0ce5b14a4844092bdb62050f462d53362304a13febbac6d973691d61be2" +class_hash = "0xa349b743d361ce4567361475a89b84a386bb383448c6926954e5fe0b525597" +original_class_hash = "0xa349b743d361ce4567361475a89b84a386bb383448c6926954e5fe0b525597" abi = "manifests/dev/base/abis/dojo-world.json" tag = "dojo-world" manifest_name = "dojo-world" diff --git a/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.json b/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.json index b3c92df3..463cbc47 100644 --- a/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.json +++ b/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.json @@ -1,8 +1,8 @@ { "world": { "kind": "WorldContract", - "class_hash": "0x458d0ce5b14a4844092bdb62050f462d53362304a13febbac6d973691d61be2", - "original_class_hash": "0x458d0ce5b14a4844092bdb62050f462d53362304a13febbac6d973691d61be2", + "class_hash": "0xa349b743d361ce4567361475a89b84a386bb383448c6926954e5fe0b525597", + "original_class_hash": "0xa349b743d361ce4567361475a89b84a386bb383448c6926954e5fe0b525597", "abi": [ { "type": "impl", @@ -1229,8 +1229,8 @@ ] } ], - "address": "0x403b5f047b8c4797139e30801e310473d99ca6877d19e0f27506f353f8f70f7", - "transaction_hash": "0x12df6c5a6c89d2c128c96026395f1f8917c36a7864aca26f0c7b9e2ea152bca", + "address": "0x3b34889efbdf01f707d5d7421f112e8fb85a42fb6f2e5422c75ce3253148b0e", + "transaction_hash": "0x4729b83525f0dddbfce0afd73e0b6a39303de51690efd756691356c21c30067", "block_number": 3, "seed": "dojo_starter", "metadata": { @@ -1250,9 +1250,9 @@ "contracts": [ { "kind": "DojoContract", - "address": "0x36e4506b35e6dfb301d437c95f74b3e1f4f82da5d8841bec894bb8de29ec13", - "class_hash": "0x4f10da1ba615523c1a4b9e2ad40df134edbcf2d971a1074efbd9772c86c2173", - "original_class_hash": "0x4f10da1ba615523c1a4b9e2ad40df134edbcf2d971a1074efbd9772c86c2173", + "address": "0x5e8df8a235031ba8e66e590a7289d27d7d54c026bcdbda62b791c0a69055225", + "class_hash": "0x7304354bfea03508e0a8beaca98c9580c852a0e350b74dcafb55192c62fa3f4", + "original_class_hash": "0x7304354bfea03508e0a8beaca98c9580c852a0e350b74dcafb55192c62fa3f4", "base_class_hash": "0x2427dd10a58850ac9a5ca6ce04b7771b05330fd18f2e481831ad903b969e6b2", "abi": [ { diff --git a/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.toml b/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.toml index bca467c3..41d787c8 100644 --- a/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.toml +++ b/examples/dojo/dojo-starter/manifests/dev/deployment/manifest.toml @@ -1,10 +1,10 @@ [world] kind = "WorldContract" -class_hash = "0x458d0ce5b14a4844092bdb62050f462d53362304a13febbac6d973691d61be2" -original_class_hash = "0x458d0ce5b14a4844092bdb62050f462d53362304a13febbac6d973691d61be2" +class_hash = "0xa349b743d361ce4567361475a89b84a386bb383448c6926954e5fe0b525597" +original_class_hash = "0xa349b743d361ce4567361475a89b84a386bb383448c6926954e5fe0b525597" abi = "manifests/dev/deployment/abis/dojo-world.json" -address = "0x403b5f047b8c4797139e30801e310473d99ca6877d19e0f27506f353f8f70f7" -transaction_hash = "0x12df6c5a6c89d2c128c96026395f1f8917c36a7864aca26f0c7b9e2ea152bca" +address = "0x3b34889efbdf01f707d5d7421f112e8fb85a42fb6f2e5422c75ce3253148b0e" +transaction_hash = "0x4729b83525f0dddbfce0afd73e0b6a39303de51690efd756691356c21c30067" block_number = 3 seed = "dojo_starter" manifest_name = "dojo-world" @@ -23,9 +23,9 @@ manifest_name = "dojo-base" [[contracts]] kind = "DojoContract" -address = "0x36e4506b35e6dfb301d437c95f74b3e1f4f82da5d8841bec894bb8de29ec13" -class_hash = "0x4f10da1ba615523c1a4b9e2ad40df134edbcf2d971a1074efbd9772c86c2173" -original_class_hash = "0x4f10da1ba615523c1a4b9e2ad40df134edbcf2d971a1074efbd9772c86c2173" +address = "0x5e8df8a235031ba8e66e590a7289d27d7d54c026bcdbda62b791c0a69055225" +class_hash = "0x7304354bfea03508e0a8beaca98c9580c852a0e350b74dcafb55192c62fa3f4" +original_class_hash = "0x7304354bfea03508e0a8beaca98c9580c852a0e350b74dcafb55192c62fa3f4" base_class_hash = "0x2427dd10a58850ac9a5ca6ce04b7771b05330fd18f2e481831ad903b969e6b2" abi = "manifests/dev/deployment/abis/contracts/dojo_starter-actions-7a1c7102.json" reads = [] diff --git a/packages/create-dojo/src/index.ts b/packages/create-dojo/src/index.ts index 9bc3874e..15d29edf 100644 --- a/packages/create-dojo/src/index.ts +++ b/packages/create-dojo/src/index.ts @@ -46,7 +46,7 @@ async function run() { console.log(`Downloading ${template} into client directory...`); spawn.sync("npx", [ "degit", - `dojoengine/dojo.js/examples/react/${template}`, + `dojoengine/dojo.js/examples/clients/react/${template}`, clientPath, // Cloning directly into the client directory ]); @@ -67,7 +67,7 @@ async function run() { console.log("You can then build the starter and run the client.\n"); console.log("For detailed instructions, follow the README here:\n"); - console.log('https://book.dojoengine.org/cairo/hello-dojo'); + console.log('https://book.dojoengine.org'); } catch (e) { console.error(`Error: ${e}`); diff --git a/packages/state/src/recs/index.ts b/packages/state/src/recs/index.ts index bd1776fc..c74a3174 100644 --- a/packages/state/src/recs/index.ts +++ b/packages/state/src/recs/index.ts @@ -217,13 +217,13 @@ export const getEntitiesQuery = async ( } : null; - const fetchedEntities = await client.getEntities({ - limit, - offset: cursor, - clause: clause || undefined, - }); - while (continueFetching) { + const fetchedEntities = await client.getEntities({ + limit, + offset: cursor, + clause: clause || undefined, + }); + setEntities(fetchedEntities, components); if (Object.keys(fetchedEntities).length < limit) { @@ -279,8 +279,6 @@ export const syncEvents = async ( return await client.onEventMessageUpdated( entityKeyClause, (fetchedEntities: any, data: any) => { - // Log the fetched entities and data for debugging purposes - console.log("fetchedEntities", data); // Update the local state with the fetched entities and their data setEntities({ [fetchedEntities]: data }, components); } diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..0c8cb70f --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "allowJs": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + } +}