dojo-client
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDojo 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
支持的平台
| Platform | Language | Package |
|---|---|---|
| JavaScript/TypeScript | JS/TS | |
| Unity | C# | |
| Unreal Engine | C++ | |
| Godot | GDScript | |
| Bevy | Rust | |
| C/C++ | C/C++ | |
| 平台 | 语言 | 包 |
|---|---|---|
| JavaScript/TypeScript | JS/TS | |
| Unity | C# | |
| Unreal Engine | C++ | |
| Godot | GDScript | |
| Bevy | Rust | |
| C/C++ | C/C++ | |
JavaScript/TypeScript Integration
JavaScript/TypeScript集成
Quick Start
快速开始
Use the quickstart wizard:
bash
pnpx @dojoengine/create-dojo start使用快速启动向导:
bash
pnpx @dojoengine/create-dojo startManual Setup
手动设置
- Install dependencies:
bash
undefined- 安装依赖:
bash
undefinedEssential 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 });- Generate TypeScript bindings:
bash
DOJO_MANIFEST_PATH="../path/to/Scarb.toml" sozo build --typescript- 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 });- 生成TypeScript绑定:
bash
DOJO_MANIFEST_PATH="../path/to/Scarb.toml" sozo build --typescript- 初始化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 hook used in the "Executing Systems" section below returns the Controller session account once connected.
useAccount()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>
);
}在下面“执行系统”部分中使用的钩子在连接后会返回Controller会话账户。
useAccount()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
See dojo.unity documentation for:
- 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
See dojo.unreal documentation for:
- Plugin installation
- Blueprint integration
- C++ SDK usage
查看dojo.unreal文档了解:
- 插件安装
- 蓝图集成
- C++ SDK使用方法
Godot
Godot
See dojo.godot documentation for:
- GDScript integration
- Signal-based subscriptions
查看dojo.godot文档了解:
- GDScript集成
- 基于信号的订阅
Client Integration Checklist
客户端集成检查清单
Pre-Integration
集成前
- World deployed (skill)
dojo-deploy - Torii indexer running (skill)
dojo-indexer - 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:
- Test end-to-end workflow
- Implement optimistic updates
- Add error handling
- Connect wallet for transactions
完成客户端集成后:
- 测试端到端工作流程
- 实现乐观更新
- 添加错误处理
- 连接钱包以处理交易
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:迁移后更新客户端