From book
Integrates Dojo worlds with JavaScript/TypeScript, Unity, Unreal, Rust, and C++ game clients. Generates typed bindings, SDK setup, and connection code for frontends and engines.
npx claudepluginhub dojoengine/bookThis skill is limited to using the following tools:
Connect your game client or frontend to your deployed Dojo world across multiple platforms.
Generates Cairo Dojo system contracts for implementing game logic, modifying model state, handling player actions, and emitting events in Starknet games.
Adds real-time or turn-based multiplayer to existing Phaser 3 or Three.js browser games using PartyKit on Cloudflare Durable Objects. Scaffolds room-based server, NetworkManager client, EventBus events, GameState fields, and extends render_game_to_text().
Guides UdonSharp C# scripting for VRChat world development, covering compile constraints, network synchronization, persistence, PhysBones, VRAM management, and event handling.
Share bugs, ideas, or general feedback.
Connect your game client or frontend to your deployed Dojo world across multiple platforms.
Handles client integration for:
| Platform | Language | Package |
|---|---|---|
| JavaScript/TypeScript | JS/TS | @dojoengine/sdk |
| Unity | C# | dojo.unity |
| Unreal Engine | C++ | dojo.unreal |
| Godot | GDScript | dojo.godot |
| Bevy | Rust | dojo.bevy |
| C/C++ | C/C++ | dojo.c |
Use the quickstart wizard:
pnpx @dojoengine/create-dojo start
# Essential packages
pnpm add @dojoengine/core @dojoengine/sdk @dojoengine/torii-client
# Controller + starknet-react (recommended)
pnpm add @cartridge/connector @cartridge/controller @starknet-react/core @starknet-react/chains starknet
# For state management
pnpm add @dojoengine/state zustand immer
# Build tools for WASM support
pnpm add -D vite-plugin-wasm vite-plugin-top-level-await
dojoConfig.ts:import { createDojoConfig } from "@dojoengine/core";
import manifest from "../path/to/manifest_dev.json";
export const dojoConfig = createDojoConfig({ manifest });
Important: The manifest must contain a valid world ABI with at least one type: "interface" entry.
If manifest.world.abi is missing or empty, starknet.js throws "Unable to determine Cairo version".
This is populated automatically by sozo build — if you see this error, rebuild your contracts.
DOJO_MANIFEST_PATH="../path/to/Scarb.toml" sozo build --typescript
import { init } from "@dojoengine/sdk";
import { DojoSdkProvider } from "@dojoengine/sdk/react";
import { dojoConfig } from "./dojoConfig.ts";
import { setupWorld } from "./bindings/typescript/contracts.gen.ts";
import type { SchemaType } from "./bindings/typescript/models.gen.ts";
async function main() {
const sdk = await init<SchemaType>({
client: {
worldAddress: dojoConfig.manifest.world.address,
toriiUrl: "http://localhost:8080",
relayUrl: "/ip4/127.0.0.1/tcp/9090",
},
domain: {
name: "MyDojoProject",
version: "1.0",
chainId: "KATANA",
revision: "1",
},
});
// Use in React
createRoot(document.getElementById("root")!).render(
<DojoSdkProvider sdk={sdk} dojoConfig={dojoConfig} clientFn={setupWorld}>
<StarknetProvider>
<App />
</StarknetProvider>
</DojoSdkProvider>
);
}
Cartridge Controller is the recommended way to handle account management in Dojo games. It provides session-based authentication via starknet-react.
Define policies and create the connector (outside React components):
import { ControllerConnector } from "@cartridge/connector";
import { SessionPolicies } from "@cartridge/controller";
const policies: SessionPolicies = {
contracts: {
[ACTIONS_CONTRACT_ADDRESS]: {
methods: [
{ name: "spawn", entrypoint: "spawn" },
{ name: "move", entrypoint: "move" },
],
},
},
};
const connector = new ControllerConnector({ policies });
Wrap your app with StarknetConfig:
import { StarknetConfig } from "@starknet-react/core";
import { sepolia, mainnet } from "@starknet-react/chains";
function StarknetProvider({ children }: { children: React.ReactNode }) {
return (
<StarknetConfig
autoConnect
chains={[mainnet, sepolia]}
connectors={[connector]}
provider={provider}
>
{children}
</StarknetConfig>
);
}
Connect/disconnect:
import { useConnect, useDisconnect, useAccount } from "@starknet-react/core";
function ConnectWallet() {
const { connect, connectors } = useConnect();
const { disconnect } = useDisconnect();
const { address } = useAccount();
return address ? (
<button onClick={() => disconnect()}>Disconnect</button>
) : (
<button onClick={() => connect({ connector: connectors[0] })}>
Connect
</button>
);
}
The useAccount() hook used in the "Executing Systems" section below returns the Controller session account once connected.
import { ToriiQueryBuilder, KeysClause, MemberClause } from "@dojoengine/sdk";
// Simple query: Find a specific player
const entities = await sdk.getEntities({
query: new ToriiQueryBuilder().withClause(
KeysClause(["dojo_starter-Player"], ["0xabcde..."], "FixedLen").build()
),
});
// Access the results
entities.items.forEach((entity) => {
const player = entity.models.dojo_starter.Player;
console.log(`Player: ${player?.name}, Score: ${player?.score}`);
});
const entities = await sdk.getEntities({
query: new ToriiQueryBuilder()
.withClause(
MemberClause("dojo_starter-Player", "score", "Gt", 0).build()
)
.withLimit(10)
.withOffset(0)
.withOrderBy([{ field: "score", direction: "Desc" }]),
});
const [initialEntities, subscription] = await sdk.subscribeEntityQuery({
query: new ToriiQueryBuilder()
.withClause(
MemberClause("dojo_starter-Player", "score", "Gt", 100).build()
)
.includeHashedKeys(),
callback: ({ data, error }) => {
if (data) {
data.forEach((entity) => {
const player = entity.models.dojo_starter.Player;
console.log(`Player ${player?.id}: ${player?.score} points`);
});
}
},
});
// Cancel later
// subscription.cancel();
import { useEntityQuery, useModels, useModel, useEntityId } from "@dojoengine/sdk/react";
function MyComponent() {
// Subscribe to entity changes
useEntityQuery(
new ToriiQueryBuilder()
.withClause(MemberClause("dojo_starter-Item", "durability", "Eq", 2).build())
.includeHashedKeys()
);
// Get all items from the store
const items = useModels("dojo_starter-Item");
// Get a single item by entity ID
const entityId = useEntityId(1);
const item = useModel(entityId, "dojo_starter-Item");
return (
<div>
{Object.entries(items).map(([id, item]) => (
<div key={id}>Item {id}: durability {item?.durability}</div>
))}
</div>
);
}
import { useDojoSDK } from "@dojoengine/sdk/react";
import { useAccount } from "@starknet-react/core";
function GameActions() {
const { client } = useDojoSDK();
const { account } = useAccount();
async function spawn() {
await client.actions.spawn({ account });
}
async function move(direction: number) {
await client.actions.move({ account, direction });
}
return (
<div>
<button onClick={spawn}>Spawn</button>
<button onClick={() => move(1)}>Move Right</button>
</div>
);
}
See dojo.unity documentation for:
See dojo.unreal documentation for:
See dojo.godot documentation for:
dojo-deploy skill)dojo-indexer skill)sozo build --typescript)dojo_starter-Position)After client integration: