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
@@ -0,0 +1,109 @@
1
+ module sui_quest::hero {
2
+ use sui::object::{Self, UID};
3
+ use sui::tx_context::{Self, TxContext};
4
+ use sui::transfer;
5
+ use sui::event;
6
+
7
+ // --- Structs ---
8
+
9
+ struct Hero has key, store {
10
+ id: UID,
11
+ name: vector<u8>,
12
+ level: u64,
13
+ experience: u64,
14
+ hp: u64,
15
+ strength: u64,
16
+ }
17
+
18
+ struct GameAdmin has key {
19
+ id: UID,
20
+ }
21
+
22
+ // --- Events ---
23
+
24
+ struct HeroMinted has copy, drop {
25
+ id: address,
26
+ name: vector<u8>,
27
+ owner: address,
28
+ }
29
+
30
+ struct AttackEvent has copy, drop {
31
+ hero_id: address,
32
+ damage_dealt: u64,
33
+ enemy_hp_remaining: u64, // simplified
34
+ }
35
+
36
+ struct LevelUpEvent has copy, drop {
37
+ hero_id: address,
38
+ new_level: u64,
39
+ }
40
+
41
+ // --- Initializer ---
42
+
43
+ fun init(ctx: &mut TxContext) {
44
+ let admin = GameAdmin { id: object::new(ctx) };
45
+ transfer::transfer(admin, tx_context::sender(ctx));
46
+ }
47
+
48
+ // --- Entry Functions ---
49
+
50
+ public entry fun mint_hero(
51
+ _admin: &GameAdmin, // Require admin to mint (simulate controlled spawning)
52
+ name: vector<u8>,
53
+ recipient: address,
54
+ ctx: &mut TxContext
55
+ ) {
56
+ let id = object::new(ctx);
57
+ let hero_id = object::uid_to_address(&id);
58
+
59
+ let hero = Hero {
60
+ id,
61
+ name,
62
+ level: 1,
63
+ experience: 0,
64
+ hp: 100,
65
+ strength: 10,
66
+ };
67
+
68
+ event::emit(HeroMinted {
69
+ id: hero_id,
70
+ name,
71
+ owner: recipient
72
+ });
73
+
74
+ transfer::public_transfer(hero, recipient);
75
+ }
76
+
77
+ public entry fun slay_boar(hero: &mut Hero, ctx: &mut TxContext) {
78
+ // Simulate combat logic
79
+ // In reality, you'd use on-chain randomness or a specific Monster object
80
+
81
+ // 1. Calculate Damage (Strength +/- random noise simulated)
82
+ let damage = hero.strength + 2;
83
+
84
+ // 2. Grant XP
85
+ hero.experience = hero.experience + 10;
86
+
87
+ // 3. Check Level Up
88
+ if (hero.experience >= 100) {
89
+ hero.level = hero.level + 1;
90
+ hero.experience = 0;
91
+ hero.strength = hero.strength + 5;
92
+ event::emit(LevelUpEvent {
93
+ hero_id: object::uid_to_address(&hero.id),
94
+ new_level: hero.level
95
+ });
96
+ };
97
+
98
+ event::emit(AttackEvent {
99
+ hero_id: object::uid_to_address(&hero.id),
100
+ damage_dealt: damage,
101
+ enemy_hp_remaining: 0
102
+ });
103
+ }
104
+
105
+ // --- Accessors (View Functions) ---
106
+ public fun get_level(hero: &Hero): u64 {
107
+ hero.level
108
+ }
109
+ }
@@ -0,0 +1,37 @@
1
+ import { execSync } from 'child_process';
2
+ import path from 'path';
3
+
4
+ async function main() {
5
+ const moveDir = path.resolve(__dirname, '../move');
6
+ console.log(`Deploying from ${moveDir}...`);
7
+
8
+ console.log('Building Move Package...');
9
+ // This assumes the user has 'sui' CLI installed and in path.
10
+ // We capture the output JSON to parse the package ID.
11
+ try {
12
+ const output = execSync('sui client publish --gas-budget 100000000 --json', {
13
+ cwd: moveDir,
14
+ encoding: 'utf-8'
15
+ });
16
+
17
+ const result = JSON.parse(output);
18
+ const packageId = result.objectChanges.find((c: any) => c.type === 'published')?.packageId;
19
+
20
+ if (!packageId) {
21
+ console.error('Could not find package ID in publish output.');
22
+ console.log(output);
23
+ return;
24
+ }
25
+
26
+ console.log('\nSUCCESS! Package Published.');
27
+ console.log('Package ID:', packageId);
28
+
29
+ // You would typically save this to a config file for the client to use.
30
+ console.log('\nCopy this Package ID to your client config!');
31
+ } catch (e: any) {
32
+ console.error('Deployment validation failed:', e.message);
33
+ console.log('Ensure you have sufficient gas and the SUI CLI configured for the active network.');
34
+ }
35
+ }
36
+
37
+ main();
@@ -0,0 +1,71 @@
1
+ # Godot 4 RPG Starter + Sui Game SDK
2
+
3
+ A Godot 4 starter kit designed to seamlessly integrate with the `sui-game-sdk`.
4
+
5
+ ## 🎮 Features
6
+ - **Godot 4 Architecture**: Uses `Scenes` and `Scripts` in a standard project layout.
7
+ - **Sui Integration**:
8
+ - `SuiService`: Validates connection and handles RPC calls. Mimics `Session` and `Sync` logic.
9
+ - `HeroController`: Demonstrates `Spawner` logic for minting and state updates.
10
+ - **Sui Quest**: A sample implementation of the cross-platform hero game.
11
+ - **Independent**: Includes its own Move contract and deployment script.
12
+
13
+ ## 🔥 Quick Start
14
+
15
+ ### Prerequisites
16
+ - Godot 4.2+
17
+ - Node.js (for deploying the contract)
18
+ - Sui CLI installed and configured
19
+
20
+ ### 1. Deploy the Move Contract
21
+ ```bash
22
+ # From the Godot starter directory
23
+ node deploy.js
24
+ ```
25
+ Copy the **Package ID** output from the deployment.
26
+
27
+ ### 2. Configuration
28
+ 1. Open `scripts/sui/hero_controller.gd`.
29
+ 2. Update the `GAME_PACKAGE_ID` constant with your deployed contract ID.
30
+ ```gdscript
31
+ const GAME_PACKAGE_ID = "0x..."
32
+ ```
33
+
34
+ ### 3. Open in Godot
35
+ 1. Open Godot 4.
36
+ 2. Click **Import** and navigate to this folder.
37
+ 3. Select `project.godot`.
38
+
39
+ ### 4. Play
40
+ 1. Press **F5** (Play Main Scene).
41
+ 2. Watch the Output / Debug Console.
42
+ 3. Hook up the `mint_hero` and `slay_boar` functions to UI Buttons (not included in starter to keep it unopinionated).
43
+
44
+ ## 🛠 SDK Mapping
45
+
46
+ | Concept | Godot Script | Description |
47
+ |---|---|---|
48
+ | **Session** | `SuiService` | Manages connection parameters |
49
+ | **Spawner** | `sui_service.spawn_asset` | Creates Move Call payloads to spawn assets |
50
+ | **Sync** | `sui_service.sync_state` | Synchronizes on-chain state to game objects |
51
+
52
+ ## 📁 Project Structure
53
+
54
+ ```
55
+ godot/
56
+ ├── project.godot
57
+ ├── scenes/
58
+ │ └── main.tscn
59
+ ├── scripts/
60
+ │ ├── game_manager.gd
61
+ │ ├── player.gd
62
+ │ └── sui/
63
+ │ ├── sui_service.gd
64
+ │ └── hero_controller.gd
65
+ ├── move/ # Move smart contract
66
+ │ └── sources/
67
+ │ └── hero.move
68
+ └── deploy.js # Deployment script
69
+ ```
70
+
71
+ > **Pro Tip**: Use this starter as a module. Copy the `scripts/sui` folder into your existing Godot game!
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync } = require('child_process');
4
+ const path = require('path');
5
+
6
+ async function main() {
7
+ const moveDir = path.resolve(__dirname, '../move');
8
+ console.log(`Deploying from ${moveDir}...`);
9
+
10
+ console.log('Building Move Package...');
11
+ try {
12
+ const output = execSync('sui client publish --gas-budget 100000000 --json', {
13
+ cwd: moveDir,
14
+ encoding: 'utf-8'
15
+ });
16
+
17
+ const result = JSON.parse(output);
18
+ const packageId = result.objectChanges.find((c) => c.type === 'published')?.packageId;
19
+ const adminCap = result.objectChanges.find((c) => c.objectType?.includes('GameAdmin'))?.objectId;
20
+
21
+ if (!packageId) {
22
+ console.error('Could not find package ID in publish output.');
23
+ console.log(output);
24
+ return;
25
+ }
26
+
27
+ console.log('\nSUCCESS! Package Published.');
28
+ console.log('Package ID:', packageId);
29
+ if (adminCap) console.log('Admin Cap:', adminCap);
30
+
31
+ console.log('\nUpdate hero_controller.gd:');
32
+ console.log(`const GAME_PACKAGE_ID = "${packageId}"`);
33
+ } catch (e) {
34
+ console.error('Deployment failed:', e.message);
35
+ console.log('Ensure you have sufficient gas and the SUI CLI configured.');
36
+ }
37
+ }
38
+
39
+ main();
@@ -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"
@@ -0,0 +1,109 @@
1
+ module sui_quest::hero {
2
+ use sui::object::{Self, UID};
3
+ use sui::tx_context::{Self, TxContext};
4
+ use sui::transfer;
5
+ use sui::event;
6
+
7
+ // --- Structs ---
8
+
9
+ struct Hero has key, store {
10
+ id: UID,
11
+ name: vector<u8>,
12
+ level: u64,
13
+ experience: u64,
14
+ hp: u64,
15
+ strength: u64,
16
+ }
17
+
18
+ struct GameAdmin has key {
19
+ id: UID,
20
+ }
21
+
22
+ // --- Events ---
23
+
24
+ struct HeroMinted has copy, drop {
25
+ id: address,
26
+ name: vector<u8>,
27
+ owner: address,
28
+ }
29
+
30
+ struct AttackEvent has copy, drop {
31
+ hero_id: address,
32
+ damage_dealt: u64,
33
+ enemy_hp_remaining: u64, // simplified
34
+ }
35
+
36
+ struct LevelUpEvent has copy, drop {
37
+ hero_id: address,
38
+ new_level: u64,
39
+ }
40
+
41
+ // --- Initializer ---
42
+
43
+ fun init(ctx: &mut TxContext) {
44
+ let admin = GameAdmin { id: object::new(ctx) };
45
+ transfer::transfer(admin, tx_context::sender(ctx));
46
+ }
47
+
48
+ // --- Entry Functions ---
49
+
50
+ public entry fun mint_hero(
51
+ _admin: &GameAdmin, // Require admin to mint (simulate controlled spawning)
52
+ name: vector<u8>,
53
+ recipient: address,
54
+ ctx: &mut TxContext
55
+ ) {
56
+ let id = object::new(ctx);
57
+ let hero_id = object::uid_to_address(&id);
58
+
59
+ let hero = Hero {
60
+ id,
61
+ name,
62
+ level: 1,
63
+ experience: 0,
64
+ hp: 100,
65
+ strength: 10,
66
+ };
67
+
68
+ event::emit(HeroMinted {
69
+ id: hero_id,
70
+ name,
71
+ owner: recipient
72
+ });
73
+
74
+ transfer::public_transfer(hero, recipient);
75
+ }
76
+
77
+ public entry fun slay_boar(hero: &mut Hero, ctx: &mut TxContext) {
78
+ // Simulate combat logic
79
+ // In reality, you'd use on-chain randomness or a specific Monster object
80
+
81
+ // 1. Calculate Damage (Strength +/- random noise simulated)
82
+ let damage = hero.strength + 2;
83
+
84
+ // 2. Grant XP
85
+ hero.experience = hero.experience + 10;
86
+
87
+ // 3. Check Level Up
88
+ if (hero.experience >= 100) {
89
+ hero.level = hero.level + 1;
90
+ hero.experience = 0;
91
+ hero.strength = hero.strength + 5;
92
+ event::emit(LevelUpEvent {
93
+ hero_id: object::uid_to_address(&hero.id),
94
+ new_level: hero.level
95
+ });
96
+ };
97
+
98
+ event::emit(AttackEvent {
99
+ hero_id: object::uid_to_address(&hero.id),
100
+ damage_dealt: damage,
101
+ enemy_hp_remaining: 0
102
+ });
103
+ }
104
+
105
+ // --- Accessors (View Functions) ---
106
+ public fun get_level(hero: &Hero): u64 {
107
+ hero.level
108
+ }
109
+ }
@@ -0,0 +1,29 @@
1
+ ; Engine configuration file.
2
+ ; It's best edited using the editor UI and not directly,
3
+ ; since the parameters that go here are not all obvious.
4
+ ;
5
+ ; Format:
6
+ ; [section] ; section goes between []
7
+ ; param=value ; assign values to parameters
8
+
9
+ config_version=5
10
+
11
+ [application]
12
+
13
+ config/name="Sui Godot RPG"
14
+ run/main_scene="res://scenes/main.tscn"
15
+ config/features=PackedStringArray("4.2", "Forward Plus")
16
+ config/icon="res://icon.svg"
17
+
18
+ [autoload]
19
+
20
+ GameManager="*res://scripts/game_manager.gd"
21
+
22
+ [display]
23
+
24
+ window/size/viewport_width=1152
25
+ window/size/viewport_height=648
26
+
27
+ [dotnet]
28
+
29
+ project/assembly_name="Sui Godot RPG"
@@ -0,0 +1,22 @@
1
+ [gd_scene load_steps=2 format=3 uid="uid://c8q4q8q4q8q4"]
2
+
3
+ [ext_resource type="Script" path="res://scripts/sui/hero_controller.gd" id="1_hero"]
4
+
5
+ [node name="Main" type="Node2D"]
6
+
7
+ [node name="UI" type="Control" parent="."]
8
+ layout_mode = 3
9
+ anchors_preset = 0
10
+ offset_right = 1152.0
11
+ offset_bottom = 648.0
12
+
13
+ [node name="Label" type="Label" parent="UI"]
14
+ layout_mode = 0
15
+ offset_left = 469.0
16
+ offset_top = 281.0
17
+ offset_right = 684.0
18
+ offset_bottom = 304.0
19
+ text = "Sui Godot Starter"
20
+
21
+ [node name="HeroController" type="Node" parent="."]
22
+ script = ExtResource("1_hero")
@@ -0,0 +1,14 @@
1
+ extends Node
2
+
3
+ # GameManager.gd
4
+ # Autoloaded global script
5
+
6
+ var player_score = 0
7
+ var game_active = false
8
+
9
+ func _ready():
10
+ print("GameManager initialized.")
11
+
12
+ func start_game():
13
+ game_active = true
14
+ print("Game Started!")
@@ -0,0 +1,10 @@
1
+ extends CharacterBody2D
2
+
3
+ const SPEED = 200.0
4
+
5
+ func _physics_process(delta):
6
+ # Simple 4-way movement
7
+ var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
8
+ velocity = direction * SPEED
9
+
10
+ move_and_slide()
@@ -0,0 +1,56 @@
1
+ extends Node
2
+
3
+ # HeroController.gd
4
+ # Logic for the Hero game
5
+
6
+ const GAME_PACKAGE_ID = "0x_REPLACE_WITH_PACKAGE_ID"
7
+ const HERO_MODULE = "hero"
8
+ # In a real game, this comes from the Session/Wallet
9
+ const PLAYER_ADDRESS = "0x_PLAYER_ADDRESS_HERE"
10
+
11
+ var sui_service: SuiService
12
+ var hero_id = null
13
+
14
+ func _ready():
15
+ # Instance the service manually if not autoloaded, or find it
16
+ sui_service = SuiService.new()
17
+ add_child(sui_service) # Add to tree to enable processing
18
+
19
+ sui_service.rpc_response.connect(_on_sui_response)
20
+
21
+ # Wait a bit or trigger via UI
22
+ print("HeroController ready. Call check_hero() to start.")
23
+ check_hero()
24
+
25
+ func check_hero():
26
+ print("Checking for Hero...")
27
+ sui_service.sync_state(PLAYER_ADDRESS)
28
+
29
+ func mint_hero():
30
+ print("Minting Hero...")
31
+ sui_service.spawn_asset(GAME_PACKAGE_ID, HERO_MODULE, "mint_hero", ["GodotWarrior"], PLAYER_ADDRESS)
32
+
33
+ func slay_boar():
34
+ if hero_id:
35
+ print("Slaying Boar...")
36
+ sui_service.spawn_asset(GAME_PACKAGE_ID, HERO_MODULE, "slay_boar", [hero_id], PLAYER_ADDRESS)
37
+ else:
38
+ print("No Hero to slay with!")
39
+
40
+ func _on_sui_response(_method, result, error):
41
+ if error:
42
+ print("RPC Error: ", error)
43
+ return
44
+
45
+ # Parse for Hero
46
+ if result and typeof(result) == TYPE_DICTIONARY and "data" in result:
47
+ var found = false
48
+ for obj in result["data"]:
49
+ var type_str = obj.get("data", {}).get("type", "")
50
+ if "%s::%s::Hero" % [GAME_PACKAGE_ID, HERO_MODULE] in type_str:
51
+ hero_id = obj["data"]["objectId"]
52
+ print("Hero FOUND: ", hero_id)
53
+ found = true
54
+ break
55
+ if not found:
56
+ print("No hero found.")
@@ -0,0 +1,62 @@
1
+ extends Node
2
+
3
+ # SuiService.gd - Autoload or instanced helper
4
+ class_name SuiService
5
+
6
+ const RPC_URL = "https://fullnode.testnet.sui.io"
7
+ var http_request: HTTPRequest
8
+ signal rpc_response(method, result, error)
9
+
10
+ func _ready():
11
+ http_request = HTTPRequest.new()
12
+ add_child(http_request)
13
+ http_request.request_completed.connect(_on_request_completed)
14
+ print("[SuiService] Ready.")
15
+
16
+ func get_owned_objects(address: String):
17
+ # Request objects with Type info
18
+ var method = "suix_getOwnedObjects"
19
+ var params = [address, { "options": { "showType": true } }]
20
+ send_rpc(method, params)
21
+
22
+ func construct_move_call(package: String, module: String, function: String, args: Array, sender: String):
23
+ return spawn_asset(package, module, function, args, sender)
24
+
25
+ # Spawns asset / executes move call (AssetSpawner concept from SDK)
26
+ func spawn_asset(package: String, module: String, function: String, args: Array, sender: String):
27
+ var payload = {
28
+ "target": "%s::%s::%s" % [package, module, function],
29
+ "arguments": args,
30
+ "sender": sender,
31
+ "type": "move_call"
32
+ }
33
+ print("[SuiService] Asset Spawn Payload: ", JSON.stringify(payload))
34
+ return payload
35
+
36
+ # Syncs game state (GameStateSync concept from SDK)
37
+ func sync_state(address: String):
38
+ var method = "suix_getOwnedObjects"
39
+ var params = [address, { "options": { "showType": true } }]
40
+ send_rpc(method, params)
41
+
42
+ func send_rpc(method: String, params: Array):
43
+ var body = JSON.stringify({
44
+ "jsonrpc": "2.0",
45
+ "id": 1,
46
+ "method": method,
47
+ "params": params
48
+ })
49
+ var headers = ["Content-Type: application/json"]
50
+ var err = http_request.request(RPC_URL, headers, HTTPClient.METHOD_POST, body)
51
+ if err != OK:
52
+ emit_signal("rpc_response", method, null, "HTTP Request Failed")
53
+
54
+ func _on_request_completed(_result, response_code, _headers, body):
55
+ if response_code == 200:
56
+ var json = JSON.parse_string(body.get_string_from_utf8())
57
+ if "error" in json:
58
+ emit_signal("rpc_response", "unknown", null, json["error"])
59
+ else:
60
+ emit_signal("rpc_response", "unknown", json["result"], null)
61
+ else:
62
+ emit_signal("rpc_response", "unknown", null, str(response_code))
@@ -0,0 +1,2 @@
1
+ NEXT_PUBLIC_PACKAGE_ID=0x_REPLACE_WITH_PACKAGE_ID
2
+ NEXT_PUBLIC_ADMIN_CAP=0x_REPLACE_WITH_ADMIN_CAP
@@ -0,0 +1,110 @@
1
+ # Next.js Game Starter + Sui Game SDK
2
+
3
+ A modern Next.js 14 web game that **directly uses** the `sui-game-sdk` package.
4
+
5
+ ## ✨ Features
6
+ - **Direct SDK Integration**: Uses actual `SessionManager`, `AssetSpawner`, and `GameStateSync` from sui-game-sdk
7
+ - **Modern Stack**: Next.js 14 App Router + TypeScript + Tailwind CSS
8
+ - **Real-time Events**: Live event streaming from the blockchain
9
+ - **Beautiful UI**: Glassmorphic design with gradients and animations
10
+
11
+ ## 🚀 Quick Start
12
+
13
+ ### 1. Deploy the Move Contract
14
+ ```bash
15
+ npm install
16
+ npm run deploy
17
+ ```
18
+ Copy the **Package ID** and **Admin Cap** from the output.
19
+
20
+ ### 2. Configure Environment
21
+ Copy the example env file:
22
+ ```bash
23
+ cp .env.local.example .env.local
24
+ ```
25
+
26
+ Edit `.env.local` and add your deployed contract details:
27
+ ```env
28
+ NEXT_PUBLIC_PACKAGE_ID=0x... # Your deployed package ID
29
+ NEXT_PUBLIC_ADMIN_CAP=0x... # Your admin cap ID
30
+ ```
31
+
32
+ ### 3. Run Development Server
33
+ ```bash
34
+ npm run dev
35
+ ```
36
+
37
+ Open [http://localhost:3000](http://localhost:3000) in your browser.
38
+
39
+ ## 📦 How It Uses sui-game-sdk
40
+
41
+ This starter demonstrates **direct usage** of the SDK:
42
+
43
+ ### Session Management
44
+ ```typescript
45
+ import { SessionManager } from 'sui-game-sdk';
46
+
47
+ const sessionManager = new SessionManager(suiClient);
48
+ console.log(sessionManager.sessionAddress); // Auto-generated session wallet
49
+ ```
50
+
51
+ ### Asset Spawning
52
+ ```typescript
53
+ import { AssetSpawner } from 'sui-game-sdk';
54
+
55
+ const spawner = new AssetSpawner(suiClient, {
56
+ packageId: PACKAGE_ID,
57
+ module: 'hero',
58
+ function: 'mint_hero',
59
+ adminCapId: ADMIN_CAP
60
+ });
61
+ ```
62
+
63
+ ### Game State Sync
64
+ ```typescript
65
+ import { GameStateSync } from 'sui-game-sdk';
66
+
67
+ const sync = new GameStateSync(suiClient);
68
+
69
+ sync.subscribe('AttackEvent', (event) => {
70
+ console.log('Damage:', event.parsedJson.damage_dealt);
71
+ });
72
+
73
+ sync.startPolling(PACKAGE_ID);
74
+ ```
75
+
76
+ ## 🎮 Game Flow
77
+
78
+ 1. **Session Created**: Automatic on page load
79
+ 2. **Hero Check**: Queries blockchain for existing hero
80
+ 3. **Mint Hero**: Creates a new hero NFT (requires admin cap)
81
+ 4. **Slay Boar**: Executes on-chain action, earns XP
82
+ 5. **Level Up**: Automatic when XP threshold reached
83
+
84
+ ## 🏗️ Project Structure
85
+
86
+ ```
87
+ nextjs/
88
+ ├── app/
89
+ │ ├── layout.tsx # Root layout
90
+ │ ├── page.tsx # Main page
91
+ │ └── globals.css # Global styles
92
+ ├── components/
93
+ │ └── HeroGame.tsx # Main game component
94
+ ├── lib/
95
+ │ └── sui-client.ts # SDK initialization
96
+ └── package.json
97
+ ```
98
+
99
+ ## 🔧 Build for Production
100
+
101
+ ```bash
102
+ npm run build
103
+ npm start
104
+ ```
105
+
106
+ ## 📝 Notes
107
+
108
+ - This is a **client-side** app. Session keys are generated in the browser.
109
+ - For production, integrate a proper wallet adapter (e.g., Sui Wallet, Ethos).
110
+ - The SDK handles all blockchain interactions seamlessly.
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;