dojo-client

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Dojo Client Integration

Dojo客户端集成

Connect your game client or frontend to your deployed Dojo world across multiple platforms.
跨多个平台将你的游戏客户端或前端连接到已部署的Dojo世界。

When to Use This Skill

何时使用该技能

  • "Set up JavaScript SDK for my Dojo game"
  • "Integrate Dojo with Unity"
  • "Generate TypeScript bindings"
  • "Connect React app to my world"
  • "为我的Dojo游戏设置JavaScript SDK"
  • "将Dojo与Unity集成"
  • "生成TypeScript绑定"
  • "将React应用连接到我的Dojo世界"

What This Skill Does

该技能的功能

Handles client integration for:
  • JavaScript/TypeScript SDK (primary)
  • Unity, Unreal, Godot, Bevy (game engines)
  • Typed binding generation
  • Query/subscription patterns
处理以下场景的客户端集成:
  • JavaScript/TypeScript SDK(主要支持)
  • Unity、Unreal、Godot、Bevy(游戏引擎)
  • 类型化绑定生成
  • 查询/订阅模式

Supported Platforms

支持的平台

PlatformLanguagePackage
JavaScript/TypeScriptJS/TS
@dojoengine/sdk
UnityC#
dojo.unity
Unreal EngineC++
dojo.unreal
GodotGDScript
dojo.godot
BevyRust
dojo.bevy
C/C++C/C++
dojo.c
平台语言
JavaScript/TypeScriptJS/TS
@dojoengine/sdk
UnityC#
dojo.unity
Unreal EngineC++
dojo.unreal
GodotGDScript
dojo.godot
BevyRust
dojo.bevy
C/C++C/C++
dojo.c

JavaScript/TypeScript Integration

JavaScript/TypeScript集成

Quick Start

快速开始

Use the quickstart wizard:
bash
pnpx @dojoengine/create-dojo start
使用快速启动向导:
bash
pnpx @dojoengine/create-dojo start

Manual Setup

手动设置

  1. Install dependencies:
bash
undefined
  1. 安装依赖:
bash
undefined

Essential packages

核心包

pnpm add @dojoengine/core @dojoengine/sdk @dojoengine/torii-client
pnpm add @dojoengine/core @dojoengine/sdk @dojoengine/torii-client

Controller + starknet-react (recommended)

控制器 + starknet-react(推荐)

pnpm add @cartridge/connector @cartridge/controller @starknet-react/core @starknet-react/chains starknet
pnpm add @cartridge/connector @cartridge/controller @starknet-react/core @starknet-react/chains starknet

For state management

状态管理相关

pnpm add @dojoengine/state zustand immer
pnpm add @dojoengine/state zustand immer

Build tools for WASM support

支持WASM的构建工具

pnpm add -D vite-plugin-wasm vite-plugin-top-level-await

2. **Create `dojoConfig.ts`:**

```typescript
import { createDojoConfig } from "@dojoengine/core";
import manifest from "../path/to/manifest_dev.json";

export const dojoConfig = createDojoConfig({ manifest });
  1. Generate TypeScript bindings:
bash
DOJO_MANIFEST_PATH="../path/to/Scarb.toml" sozo build --typescript
  1. Initialize the SDK:
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>
    );
}
pnpm add -D vite-plugin-wasm vite-plugin-top-level-await

2. **创建`dojoConfig.ts`文件:**

```typescript
import { createDojoConfig } from "@dojoengine/core";
import manifest from "../path/to/manifest_dev.json";

export const dojoConfig = createDojoConfig({ manifest });
  1. 生成TypeScript绑定:
bash
DOJO_MANIFEST_PATH="../path/to/Scarb.toml" sozo build --typescript
  1. 初始化SDK:
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",
        },
    });

    // 在React中使用
    createRoot(document.getElementById("root")!).render(
        <DojoSdkProvider sdk={sdk} dojoConfig={dojoConfig} clientFn={setupWorld}>
            <StarknetProvider>
                <App />
            </StarknetProvider>
        </DojoSdkProvider>
    );
}

Controller Integration (starknet-react)

控制器集成(starknet-react)

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):
typescript
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:
typescript
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:
typescript
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.
Cartridge Controller是在Dojo游戏中处理账户管理的推荐方式。它通过starknet-react提供基于会话的身份验证。
定义策略并创建连接器(在React组件外部):
typescript
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 });
使用StarknetConfig包裹你的应用:
typescript
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>
    );
}
连接/断开连接:
typescript
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()}>断开连接</button>
    ) : (
        <button onClick={() => connect({ connector: connectors[0] })}>
            连接钱包
        </button>
    );
}
在下面“执行系统”部分中使用的
useAccount()
钩子在连接后会返回Controller会话账户。

Querying Entities

查询实体

typescript
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}`);
});
typescript
import { ToriiQueryBuilder, KeysClause, MemberClause } from "@dojoengine/sdk";

// 简单查询:查找特定玩家
const entities = await sdk.getEntities({
    query: new ToriiQueryBuilder().withClause(
        KeysClause(["dojo_starter-Player"], ["0xabcde..."], "FixedLen").build()
    ),
});

// 访问结果
entities.items.forEach((entity) => {
    const player = entity.models.dojo_starter.Player;
    console.log(`玩家: ${player?.name}, 分数: ${player?.score}`);
});

Complex Queries

复杂查询

typescript
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" }]),
});
typescript
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" }]),
});

Subscribing to Changes

订阅变更

typescript
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();
typescript
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?.id}: ${player?.score}`);
            });
        }
    },
});

// 后续可取消订阅
// subscription.cancel();

React Hooks

React钩子

typescript
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>
    );
}
typescript
import { useEntityQuery, useModels, useModel, useEntityId } from "@dojoengine/sdk/react";

function MyComponent() {
    // 订阅实体变更
    useEntityQuery(
        new ToriiQueryBuilder()
            .withClause(MemberClause("dojo_starter-Item", "durability", "Eq", 2).build())
            .includeHashedKeys()
    );

    // 从存储中获取所有物品
    const items = useModels("dojo_starter-Item");

    // 通过实体ID获取单个物品
    const entityId = useEntityId(1);
    const item = useModel(entityId, "dojo_starter-Item");

    return (
        <div>
            {Object.entries(items).map(([id, item]) => (
                <div key={id}>物品 {id}: 耐久度 {item?.durability}</div>
            ))}
        </div>
    );
}

Executing Systems

执行系统

typescript
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>
    );
}
typescript
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}>生成角色</button>
            <button onClick={() => move(1)}>向右移动</button>
        </div>
    );
}

Game Engine Integration

游戏引擎集成

Unity

Unity

  • Package installation via Unity Package Manager
  • C# bindings generation
  • Entity synchronization patterns
  • Transaction handling
查看dojo.unity文档了解:
  • 通过Unity Package Manager安装包
  • 生成C#绑定
  • Entity同步模式
  • 事务处理

Unreal Engine

Unreal Engine

  • Plugin installation
  • Blueprint integration
  • C++ SDK usage
查看dojo.unreal文档了解:
  • 插件安装
  • 蓝图集成
  • C++ SDK使用方法

Godot

Godot

  • GDScript integration
  • Signal-based subscriptions
查看dojo.godot文档了解:
  • GDScript集成
  • 基于信号的订阅

Client Integration Checklist

客户端集成检查清单

Pre-Integration

集成前

  • World deployed (
    dojo-deploy
    skill)
  • Torii indexer running (
    dojo-indexer
    skill)
  • World address recorded
  • RPC endpoint accessible
  • 已部署Dojo世界(使用
    dojo-deploy
    技能)
  • Torii索引器正在运行(使用
    dojo-indexer
    技能)
  • 已记录世界地址
  • RPC端点可访问

Setup

设置阶段

  • SDK/package installed
  • Bindings generated (
    sozo build --typescript
    )
  • Manifest imported
  • SDK initialized
  • Test queries work
  • 已安装SDK/包
  • 已生成绑定(
    sozo build --typescript
  • 已导入Manifest
  • 已初始化SDK
  • 测试查询功能正常

Integration

集成阶段

  • Entity reads working
  • System executions working
  • Subscriptions configured
  • Error handling added
  • 实体读取功能正常
  • 系统执行功能正常
  • 已配置订阅
  • 已添加错误处理

Troubleshooting

故障排除

"Cannot connect to RPC"

“无法连接到RPC”

  • Verify RPC URL is correct
  • Check Katana/Torii is running
  • Verify network is reachable
  • 验证RPC URL是否正确
  • 检查Katana/Torii是否正在运行
  • 验证网络是否可达

"World not found"

“未找到世界”

  • Check world address is correct
  • Verify world is deployed
  • Check RPC is pointing to correct network
  • 检查世界地址是否正确
  • 验证世界是否已部署
  • 检查RPC是否指向正确的网络

"Model not found"

“未找到模型”

  • Ensure model is deployed
  • Check model name includes namespace (
    dojo_starter-Position
    )
  • Verify entity exists with that key
  • 确保模型已部署
  • 检查模型名称是否包含命名空间(如
    dojo_starter-Position
  • 验证存在对应键的实体

Next Steps

后续步骤

After client integration:
  1. Test end-to-end workflow
  2. Implement optimistic updates
  3. Add error handling
  4. Connect wallet for transactions
完成客户端集成后:
  1. 测试端到端工作流程
  2. 实现乐观更新
  3. 添加错误处理
  4. 连接钱包以处理交易

Related Skills

相关技能

  • dojo-deploy: Deploy world first
  • dojo-indexer: Set up Torii for queries
  • dojo-world: Configure permissions
  • dojo-migrate: Update client after migrations
  • dojo-deploy:先部署世界
  • dojo-indexer:设置Torii用于查询
  • dojo-world:配置权限
  • dojo-migrate:迁移后更新客户端