sui-game-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +163 -0
  2. package/dist/bin/cli.d.ts +2 -0
  3. package/dist/bin/cli.js +79 -0
  4. package/dist/index.d.ts +3 -0
  5. package/dist/index.js +19 -0
  6. package/dist/session.d.ts +23 -0
  7. package/dist/session.js +47 -0
  8. package/dist/spawner.d.ts +19 -0
  9. package/dist/spawner.js +37 -0
  10. package/dist/sync.d.ts +20 -0
  11. package/dist/sync.js +55 -0
  12. package/package.json +46 -0
  13. package/starter/README.md +57 -0
  14. package/starter/client/index.ts +79 -0
  15. package/starter/move/Move.toml +9 -0
  16. package/starter/move/sources/hero.move +109 -0
  17. package/starter/scripts/deploy.ts +37 -0
  18. package/starters/godot/README.md +71 -0
  19. package/starters/godot/deploy.js +39 -0
  20. package/starters/godot/move/Move.toml +9 -0
  21. package/starters/godot/move/sources/hero.move +109 -0
  22. package/starters/godot/project.godot +29 -0
  23. package/starters/godot/scenes/main.tscn +22 -0
  24. package/starters/godot/scripts/game_manager.gd +14 -0
  25. package/starters/godot/scripts/player.gd +10 -0
  26. package/starters/godot/scripts/sui/hero_controller.gd +56 -0
  27. package/starters/godot/scripts/sui/sui_service.gd +62 -0
  28. package/starters/nextjs/.env.local.example +2 -0
  29. package/starters/nextjs/README.md +110 -0
  30. package/starters/nextjs/app/globals.css +3 -0
  31. package/starters/nextjs/app/layout.tsx +22 -0
  32. package/starters/nextjs/app/page.tsx +5 -0
  33. package/starters/nextjs/components/HeroGame.tsx +161 -0
  34. package/starters/nextjs/lib/sui-client.ts +22 -0
  35. package/starters/nextjs/move/Move.toml +9 -0
  36. package/starters/nextjs/move/sources/hero.move +109 -0
  37. package/starters/nextjs/next.config.js +4 -0
  38. package/starters/nextjs/package.json +30 -0
  39. package/starters/nextjs/postcss.config.js +6 -0
  40. package/starters/nextjs/scripts/deploy.ts +42 -0
  41. package/starters/nextjs/tailwind.config.js +12 -0
  42. package/starters/nextjs/tsconfig.json +39 -0
  43. package/starters/unity/Assets/Scenes/Main.unity +197 -0
  44. package/starters/unity/Assets/Scripts/Core/GameManager.cs +38 -0
  45. package/starters/unity/Assets/Scripts/Core/PlayerController.cs +37 -0
  46. package/starters/unity/Assets/Scripts/Sui/HeroManager.cs +99 -0
  47. package/starters/unity/Assets/Scripts/Sui/SuiManager.cs +94 -0
  48. package/starters/unity/README.md +72 -0
  49. package/starters/unity/deploy.js +39 -0
  50. package/starters/unity/move/Move.toml +9 -0
  51. package/starters/unity/move/sources/hero.move +109 -0
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # SUI Game SDK
2
+
3
+ The ultimate SDK for building games on SUI. Includes Session Keys, Asset Spawning, and Game State Synchronization.
4
+
5
+ ## Features
6
+
7
+ ### 1. Session Manager
8
+ Eliminate wallet popups during gameplay.
9
+ - Generate ephemeral Ed25519 keys locally
10
+ - Export/Import session state
11
+ - Sign transactions seamlessly
12
+
13
+ ### 2. Asset Spawner
14
+ Simplified API for minting game objects using Move calls.
15
+ - Fluent `spawn(recipient, attributes)` API
16
+ - Auto-formats vector arguments for attributes
17
+
18
+ ### 3. Game State Sync
19
+ React to on-chain game events in real-time.
20
+ - Poll for specific Move events
21
+ - Subscription-based handler pattern
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install sui-game-sdk
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { SessionManager, AssetSpawner, GameStateSync } from 'sui-game-sdk';
33
+ import { SuiClient } from '@mysten/sui/client';
34
+
35
+ const client = new SuiClient({ url: 'https://fullnode.testnet.sui.io' });
36
+
37
+ // 1. Create Session
38
+ const session = new SessionManager(client);
39
+ console.log('Session Address:', session.sessionAddress);
40
+
41
+ // 2. Spawn Asset
42
+ const spawner = new AssetSpawner(client, {
43
+ packageId: '0x...',
44
+ module: 'hero',
45
+ function: 'mint_hero',
46
+ adminCapId: '0x...'
47
+ });
48
+
49
+ // 3. Listen to Events
50
+ const sync = new GameStateSync(client);
51
+ sync.subscribe('0x...::hero::LevelUpEvent', (event) => {
52
+ console.log('Level Up!', event.parsedJson);
53
+ });
54
+ sync.startPolling('0x...');
55
+ ```
56
+
57
+ ## Starter Templates
58
+
59
+ Kickstart your game development with our ready-to-use starter templates:
60
+
61
+ ### Generate a Starter
62
+
63
+ ```bash
64
+ npx sui-game-sdk init
65
+ ```
66
+
67
+ You'll be prompted to choose from:
68
+
69
+ #### 1. **TypeScript (Web/Node)**
70
+ Full-featured starter with all SDK components. Perfect for web games or backend services.
71
+ - Uses SessionManager, AssetSpawner, GameStateSync
72
+ - Includes sample "Sui Quest" game
73
+ - Move contract included
74
+
75
+ #### 2. **Next.js (Web App)**
76
+ Modern React web application with beautiful UI.
77
+ - Direct SDK integration
78
+ - Real-time event streaming
79
+ - Tailwind CSS + glassmorphic design
80
+ - Production-ready structure
81
+
82
+ #### 3. **Unity (C# Scripts)**
83
+ Game engine integration for Unity developers.
84
+ - Simulates SDK patterns in C#
85
+ - HTTP/RPC blockchain communication
86
+ - Organized Assets folder structure
87
+ - Compatible with Unity 2021.3+
88
+
89
+ #### 4. **Godot (GDScript)**
90
+ Game engine integration for Godot developers.
91
+ - Simulates SDK patterns in GDScript
92
+ - HTTP/RPC blockchain communication
93
+ - Standard Godot 4 project structure
94
+ - Signals for event handling
95
+
96
+ ### Starter Features
97
+
98
+ All starters include:
99
+ - āœ… Sample "Sui Quest" game (Mint Hero, Slay Boar, Level Up)
100
+ - āœ… Move smart contract (`hero.move`)
101
+ - āœ… Deployment scripts
102
+ - āœ… Comprehensive README
103
+ - āœ… SDK integration examples
104
+
105
+ ### How Starters Use the SDK
106
+
107
+ - **TypeScript & Next.js**: Direct SDK imports (`import { SessionManager } from 'sui-game-sdk'`)
108
+ - **Unity & Godot**: Simulate SDK concepts using native languages (C#/GDScript)
109
+
110
+ For detailed SDK usage comparison, see [SDK_USAGE.md](./SDK_USAGE.md)
111
+
112
+ ## Documentation
113
+
114
+ - [SDK Usage Guide](./SDK_USAGE.md) - How each starter uses the SDK
115
+ - [TypeScript Starter](./starter/README.md)
116
+ - [Next.js Starter](./starters/nextjs/README.md)
117
+ - [Unity Starter](./starters/unity/README.md)
118
+ - [Godot Starter](./starters/godot/README.md)
119
+
120
+ ## Example: Building a Simple RPG
121
+
122
+ ```typescript
123
+ import { SessionManager, GameStateSync } from 'sui-game-sdk';
124
+ import { Transaction } from '@mysten/sui/transactions';
125
+
126
+ // Setup
127
+ const client = new SuiClient({ url: 'https://fullnode.testnet.sui.io' });
128
+ const session = new SessionManager(client);
129
+
130
+ // Execute game action
131
+ const tx = new Transaction();
132
+ tx.moveCall({
133
+ target: `${PACKAGE_ID}::hero::slay_boar`,
134
+ arguments: [tx.object(heroId)]
135
+ });
136
+
137
+ const result = await session.executeSessionTx(tx);
138
+ console.log('Action completed:', result.digest);
139
+
140
+ // Listen for results
141
+ const sync = new GameStateSync(client);
142
+ sync.subscribe(`${PACKAGE_ID}::hero::AttackEvent`, (event) => {
143
+ console.log('Damage dealt:', event.parsedJson.damage_dealt);
144
+ });
145
+ sync.startPolling(PACKAGE_ID);
146
+ ```
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ # Install dependencies
152
+ npm install
153
+
154
+ # Build
155
+ npm run build
156
+
157
+ # Run tests
158
+ npm test
159
+ ```
160
+
161
+ ## License
162
+
163
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const prompts_1 = __importDefault(require("prompts"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const program = new commander_1.Command();
12
+ program
13
+ .name('sui-game-sdk')
14
+ .description('CLI to scaffold Sui Game projects')
15
+ .version('0.0.1');
16
+ program
17
+ .command('init')
18
+ .description('Initialize a new game project')
19
+ .action(async () => {
20
+ const response = await (0, prompts_1.default)([
21
+ {
22
+ type: 'text',
23
+ name: 'projectName',
24
+ message: 'What is the name of your project?',
25
+ initial: 'my-sui-game'
26
+ },
27
+ {
28
+ type: 'select',
29
+ name: 'template',
30
+ message: 'Which starter template would you like?',
31
+ choices: [
32
+ { title: 'TypeScript (Web/Node)', value: 'starter' },
33
+ { title: 'Next.js (Web App)', value: 'nextjs' },
34
+ { title: 'Unity (C# Scripts)', value: 'unity' },
35
+ { title: 'Godot (GDScript)', value: 'godot' }
36
+ ]
37
+ }
38
+ ]);
39
+ if (!response.template || !response.projectName)
40
+ return;
41
+ const targetDir = path_1.default.join(process.cwd(), response.projectName);
42
+ const templateDir = path_1.default.resolve(__dirname, '../starters', response.template === 'starter' ? '../starter' : response.template); // Fix path logic based on structure
43
+ // In installed package: dist/bin/cli.js -> __dirname is dist/bin
44
+ // We need to go up two levels to find 'starter' and 'starters' at root
45
+ let sourcePath = '';
46
+ if (response.template === 'starter') {
47
+ sourcePath = path_1.default.resolve(__dirname, '../../starter');
48
+ }
49
+ else {
50
+ sourcePath = path_1.default.resolve(__dirname, '../../starters', response.template);
51
+ }
52
+ if (fs_extra_1.default.existsSync(targetDir)) {
53
+ console.error(`Error: Directory ${targetDir} already exists.`);
54
+ process.exit(1);
55
+ }
56
+ console.log(`\nCreating project in ${targetDir}...`);
57
+ try {
58
+ await fs_extra_1.default.copy(sourcePath, targetDir);
59
+ console.log('\nSuccess! Created project.');
60
+ console.log(`\ncd ${response.projectName}`);
61
+ if (response.template === 'starter') {
62
+ console.log('npm install');
63
+ console.log('npm run deploy');
64
+ }
65
+ else if (response.template === 'nextjs') {
66
+ console.log('npm install');
67
+ console.log('cp .env.local.example .env.local');
68
+ console.log('# Edit .env.local with your package ID');
69
+ console.log('npm run dev');
70
+ }
71
+ else {
72
+ console.log('Follow the README.md instructions to integrate with your engine.');
73
+ }
74
+ }
75
+ catch (e) {
76
+ console.error('Error creating project:', e);
77
+ }
78
+ });
79
+ program.parse();
@@ -0,0 +1,3 @@
1
+ export * from './session';
2
+ export * from './spawner';
3
+ export * from './sync';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./session"), exports);
18
+ __exportStar(require("./spawner"), exports);
19
+ __exportStar(require("./sync"), exports);
@@ -0,0 +1,23 @@
1
+ import { SuiClient } from '@mysten/sui/client';
2
+ import { Transaction } from '@mysten/sui/transactions';
3
+ export declare class SessionManager {
4
+ private ephemeralKeyPair;
5
+ private client;
6
+ sessionAddress: string;
7
+ constructor(client: SuiClient, existingSecretKey?: string);
8
+ /**
9
+ * Gets the public key of the session (ephemeral) wallet.
10
+ * The main wallet should authorize this key to sign transactions on its behalf
11
+ * (e.g. via Sponsored Transactions or a Smart Account capability).
12
+ */
13
+ getPublicKey(): string;
14
+ /**
15
+ * Export the session key to save local state (e.g. localStorage).
16
+ */
17
+ exportSession(): string;
18
+ /**
19
+ * Signs and Executes a transaction using the session key.
20
+ * Note: This usually requires the transaction to be Sponsored or the Session Key to handle gas.
21
+ */
22
+ executeSessionTx(tx: Transaction): Promise<import("@mysten/sui/client").SuiTransactionBlockResponse>;
23
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionManager = void 0;
4
+ const ed25519_1 = require("@mysten/sui/keypairs/ed25519");
5
+ class SessionManager {
6
+ constructor(client, existingSecretKey) {
7
+ this.client = client;
8
+ if (existingSecretKey) {
9
+ this.ephemeralKeyPair = ed25519_1.Ed25519Keypair.fromSecretKey(existingSecretKey);
10
+ }
11
+ else {
12
+ this.ephemeralKeyPair = new ed25519_1.Ed25519Keypair();
13
+ }
14
+ this.sessionAddress = this.ephemeralKeyPair.toSuiAddress();
15
+ }
16
+ /**
17
+ * Gets the public key of the session (ephemeral) wallet.
18
+ * The main wallet should authorize this key to sign transactions on its behalf
19
+ * (e.g. via Sponsored Transactions or a Smart Account capability).
20
+ */
21
+ getPublicKey() {
22
+ return this.ephemeralKeyPair.getPublicKey().toBase64();
23
+ }
24
+ /**
25
+ * Export the session key to save local state (e.g. localStorage).
26
+ */
27
+ exportSession() {
28
+ return this.ephemeralKeyPair.getSecretKey();
29
+ }
30
+ /**
31
+ * Signs and Executes a transaction using the session key.
32
+ * Note: This usually requires the transaction to be Sponsored or the Session Key to handle gas.
33
+ */
34
+ async executeSessionTx(tx) {
35
+ tx.setSender(this.sessionAddress);
36
+ const { bytes, signature } = await tx.sign({ client: this.client, signer: this.ephemeralKeyPair });
37
+ return this.client.executeTransactionBlock({
38
+ transactionBlock: bytes,
39
+ signature: signature,
40
+ options: {
41
+ showEffects: true,
42
+ showEvents: true
43
+ }
44
+ });
45
+ }
46
+ }
47
+ exports.SessionManager = SessionManager;
@@ -0,0 +1,19 @@
1
+ import { Transaction } from '@mysten/sui/transactions';
2
+ import { SuiClient } from '@mysten/sui/client';
3
+ export type SpawnConfig = {
4
+ packageId: string;
5
+ module: string;
6
+ function: string;
7
+ adminCapId: string;
8
+ };
9
+ export declare class AssetSpawner {
10
+ private client;
11
+ private config;
12
+ constructor(client: SuiClient, config: SpawnConfig);
13
+ /**
14
+ * Creates a transaction to spawn a new Game Asset (NFT).
15
+ * @param recipient The player address receiving the asset
16
+ * @param attributes Optional map of attributes to set immediately (if contract supports it)
17
+ */
18
+ spawn(recipient: string, attributes?: Record<string, any>): Transaction;
19
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AssetSpawner = void 0;
4
+ const transactions_1 = require("@mysten/sui/transactions");
5
+ class AssetSpawner {
6
+ constructor(client, config) {
7
+ this.client = client;
8
+ this.config = config;
9
+ }
10
+ /**
11
+ * Creates a transaction to spawn a new Game Asset (NFT).
12
+ * @param recipient The player address receiving the asset
13
+ * @param attributes Optional map of attributes to set immediately (if contract supports it)
14
+ */
15
+ spawn(recipient, attributes) {
16
+ const tx = new transactions_1.Transaction();
17
+ // This assumes a standard mint function signature:
18
+ // mint(admin_cap, recipient, attributes_keys, attributes_values)
19
+ // or similar. This is a generic implementation.
20
+ const args = [
21
+ tx.object(this.config.adminCapId),
22
+ tx.pure.address(recipient)
23
+ ];
24
+ if (attributes) {
25
+ const keys = Object.keys(attributes);
26
+ const values = Object.values(attributes).map(String);
27
+ args.push(tx.pure.vector('string', keys));
28
+ args.push(tx.pure.vector('string', values));
29
+ }
30
+ tx.moveCall({
31
+ target: `${this.config.packageId}::${this.config.module}::${this.config.function}`,
32
+ arguments: args
33
+ });
34
+ return tx;
35
+ }
36
+ }
37
+ exports.AssetSpawner = AssetSpawner;
package/dist/sync.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { SuiClient, SuiEvent } from '@mysten/sui/client';
2
+ export type GameEventHandler = (event: SuiEvent) => void;
3
+ export declare class GameStateSync {
4
+ private client;
5
+ private pollingInterval;
6
+ private handlers;
7
+ private lastTxDigest;
8
+ constructor(client: SuiClient);
9
+ /**
10
+ * Subscribe to a specific Move Event type (e.g. "0x...::game::AttackEvent")
11
+ */
12
+ subscribe(eventType: string, handler: GameEventHandler): void;
13
+ /**
14
+ * Starts polling for events.
15
+ * @param packageId The package to filter events by
16
+ * @param intervalMs Polling interval in milliseconds
17
+ */
18
+ startPolling(packageId: string, intervalMs?: number): Promise<void>;
19
+ stopPolling(): void;
20
+ }
package/dist/sync.js ADDED
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GameStateSync = void 0;
4
+ class GameStateSync {
5
+ constructor(client) {
6
+ this.pollingInterval = null;
7
+ this.handlers = new Map();
8
+ this.client = client;
9
+ }
10
+ /**
11
+ * Subscribe to a specific Move Event type (e.g. "0x...::game::AttackEvent")
12
+ */
13
+ subscribe(eventType, handler) {
14
+ if (!this.handlers.has(eventType)) {
15
+ this.handlers.set(eventType, []);
16
+ }
17
+ this.handlers.get(eventType)?.push(handler);
18
+ }
19
+ /**
20
+ * Starts polling for events.
21
+ * @param packageId The package to filter events by
22
+ * @param intervalMs Polling interval in milliseconds
23
+ */
24
+ async startPolling(packageId, intervalMs = 2000) {
25
+ if (this.pollingInterval)
26
+ return;
27
+ this.pollingInterval = setInterval(async () => {
28
+ try {
29
+ // Fetch events
30
+ const events = await this.client.queryEvents({
31
+ query: { MoveModule: { package: packageId, module: 'game' } }, // simplified query
32
+ limit: 10,
33
+ cursor: this.lastTxDigest ? { txDigest: this.lastTxDigest, eventSeq: '0' } : undefined
34
+ });
35
+ for (const event of events.data) {
36
+ const handlers = this.handlers.get(event.type);
37
+ if (handlers) {
38
+ handlers.forEach(h => h(event));
39
+ }
40
+ this.lastTxDigest = event.id.txDigest;
41
+ }
42
+ }
43
+ catch (e) {
44
+ console.error('Error polling game state:', e);
45
+ }
46
+ }, intervalMs);
47
+ }
48
+ stopPolling() {
49
+ if (this.pollingInterval) {
50
+ clearInterval(this.pollingInterval);
51
+ this.pollingInterval = null;
52
+ }
53
+ }
54
+ }
55
+ exports.GameStateSync = GameStateSync;
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "sui-game-sdk",
3
+ "version": "0.0.1",
4
+ "description": "The ultimate SDK for building games on SUI. Session Keys, Spawning, & Sync.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "sui-game-sdk": "./dist/bin/cli.js"
8
+ },
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist",
12
+ "starter",
13
+ "starters",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "rm -rf dist && tsc",
18
+ "test": "vitest",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "sui",
23
+ "game",
24
+ "sdk",
25
+ "move",
26
+ "blockchain",
27
+ "gaming"
28
+ ],
29
+ "author": "dotandev",
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "@mysten/bcs": "^1.1.0",
33
+ "@mysten/sui": "^1.21.1",
34
+ "commander": "^14.0.2",
35
+ "fs-extra": "^11.3.3",
36
+ "prompts": "^2.4.2"
37
+ },
38
+ "devDependencies": {
39
+ "@types/fs-extra": "^11.0.4",
40
+ "@types/node": "^22.10.7",
41
+ "@types/prompts": "^2.4.9",
42
+ "ts-node": "^10.9.2",
43
+ "typescript": "^5.7.3",
44
+ "vitest": "^3.0.4"
45
+ }
46
+ }
@@ -0,0 +1,57 @@
1
+ # SUI Game SDK - TypeScript Starter
2
+
3
+ The reference implementation for the `sui-game-sdk`. This starter project demonstrates the full capabilities of the SDK including Session Management, Asset Spawning, and Game State Synchronization.
4
+
5
+ ## šŸ“¦ Features
6
+ - **Session Manager**: Automated session key creation and management.
7
+ - **Asset Spawner**: Streamlined asset creation transactions.
8
+ - **Game State Sync**: Real-time event listening and object syncing.
9
+ - **Sui Quest**: A complete sample game logic (Mint Hero, Slay Boar).
10
+
11
+ ## šŸš€ Quick Start
12
+
13
+ ### 1. Install Dependencies
14
+ ```bash
15
+ npm install
16
+ ```
17
+
18
+ ### 2. Deploy the Game Contract
19
+ This starter requires the `sui_quest` Move package to be published.
20
+ ```bash
21
+ # Deploys the contract in 'move/' to Testnet (or localnet if configured)
22
+ npm run deploy
23
+ ```
24
+ > **Important**: The deploy script will update `client/index.ts` automatically (or print the ID you need to set).
25
+
26
+ ### 3. Run the Client
27
+ Start the game client simulation:
28
+ ```bash
29
+ npm start
30
+ ```
31
+
32
+ ## šŸŽ® How it Works
33
+
34
+ ### Session
35
+ The `SessionManager` creates a temporary wallet for the player.
36
+ ```typescript
37
+ const session = new SessionManager(client);
38
+ console.log(session.sessionAddress); // fund this!
39
+ ```
40
+
41
+ ### Spawning
42
+ The `AssetSpawner` handles complex Move calls to create assets.
43
+ ```typescript
44
+ // (Conceptual usage, check specific implementation)
45
+ spawner.spawn(playerAddress, { ... });
46
+ ```
47
+
48
+ ### Syncing
49
+ The `GameStateSync` listens for on-chain events to update the game UI.
50
+ ```typescript
51
+ syncer.subscribe('AttackEvent', (event) => {
52
+ console.log('Damage:', event.damage);
53
+ });
54
+ ```
55
+
56
+ ## šŸ“„ License
57
+ MIT
@@ -0,0 +1,79 @@
1
+ import { SuiClient } from '@mysten/sui/client';
2
+ import { SessionManager, AssetSpawner, GameStateSync } from '../../src'; // Import from local SDK
3
+ import { Transaction } from '@mysten/sui/transactions';
4
+
5
+ // API: Replace these after running `npm run deploy`
6
+ const PACKAGE_ID = process.env.GAME_PACKAGE_ID || '0x_REPLACE_WITH_PACKAGE_ID';
7
+ const GAME_ADMIN_CAP = process.env.GAME_ADMIN_CAP || '0x_REPLACE_WITH_ADMIN_CAP'; // Found in publish output
8
+
9
+ const MODULE = 'hero';
10
+
11
+ async function main() {
12
+ console.log('--- SUI QUEST CLIENT ---');
13
+
14
+ const client = new SuiClient({ url: 'https://fullnode.testnet.sui.io' });
15
+
16
+ // 1. Account Setup (Session)
17
+ // In a real app, we'd take a private key from env or wallet adapter
18
+ const session = new SessionManager(client);
19
+ console.log(`Player Session Address: ${session.sessionAddress}`);
20
+ console.log('Fund this address with Testnet SUI to play!');
21
+
22
+ // 2. Listen to the Game
23
+ const syncer = new GameStateSync(client);
24
+ console.log(`\nSubscribing to events from package: ${PACKAGE_ID}...`);
25
+
26
+ syncer.subscribe(`${PACKAGE_ID}::${MODULE}::AttackEvent`, (event) => {
27
+ const data = event.parsedJson as any;
28
+ console.log(`āš”ļø Hero dealt ${data.damage_dealt} damage!`);
29
+ });
30
+ syncer.subscribe(`${PACKAGE_ID}::${MODULE}::LevelUpEvent`, (event) => {
31
+ const data = event.parsedJson as any;
32
+ console.log(`šŸŽ‰ LEVEL UP! New Level: ${data.new_level}`);
33
+ });
34
+
35
+ console.log('Polling for events...');
36
+ syncer.startPolling(PACKAGE_ID);
37
+
38
+ // 3. Gameplay Loop (Simulation)
39
+ if (PACKAGE_ID.startsWith('0x_REPLACE')) {
40
+ console.warn('\nāš ļø Please deploy the contract first and set game package ID in client/index.ts');
41
+ console.warn('Run: npm run deploy');
42
+ return;
43
+ }
44
+
45
+ // Demo: Simulation of Action
46
+ console.log('\n[Simulating "Slay Boar" Action...]');
47
+ console.log('Note: This will fail if the session wallet has no gas or no Hero object.');
48
+
49
+ // In a real game, you would find the Hero object ID owned by the session
50
+ try {
51
+ const heroes = await client.getOwnedObjects({
52
+ owner: session.sessionAddress,
53
+ options: { showType: true, showContent: true }
54
+ });
55
+
56
+ const heroObj = heroes.data.find(d => d.data?.type === `${PACKAGE_ID}::${MODULE}::Hero`);
57
+
58
+ if (heroObj) {
59
+ const heroId = heroObj.data?.objectId;
60
+ console.log(`Found Hero: ${heroId}. Attempting to Slay Boar...`);
61
+
62
+ const tx = new Transaction();
63
+ tx.moveCall({
64
+ target: `${PACKAGE_ID}::${MODULE}::slay_boar`,
65
+ arguments: [tx.object(heroId!)]
66
+ });
67
+ const res = await session.executeSessionTx(tx);
68
+ console.log('Action Executed! Digest:', res.digest);
69
+ } else {
70
+ console.log('No Hero found. You need to mint one first (requires Admin Cap or open minting).');
71
+ // Mock minting call if we had one open
72
+ }
73
+
74
+ } catch (e) {
75
+ console.error('Error during simulation:', e);
76
+ }
77
+ }
78
+
79
+ main().catch(console.error);
@@ -0,0 +1,9 @@
1
+ [package]
2
+ name = "SuiQuest"
3
+ version = "0.0.1"
4
+
5
+ [dependencies]
6
+ Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
7
+
8
+ [addresses]
9
+ sui_quest = "0x0"