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,38 @@
1
+ using UnityEngine;
2
+
3
+ public class GameManager : MonoBehaviour
4
+ {
5
+ [Header("UI References")]
6
+ public GameObject LoginPanel;
7
+ public GameObject GamePanel;
8
+
9
+ [Header("Dependencies")]
10
+ public SuiManager Sui;
11
+ public HeroManager Hero;
12
+
13
+ private void Start()
14
+ {
15
+ // Simulating a game boot sequence
16
+ Debug.Log("Game Starting...");
17
+ LoginPanel.SetActive(true);
18
+ GamePanel.SetActive(false);
19
+ }
20
+
21
+ public void OnLoginClicked()
22
+ {
23
+ // Mock Login
24
+ string mockAddress = "0x7e875ea78ee09f08d72e2676cf84e0f1c8ac61094297ee4a057aa015e297ca";
25
+ Sui.Initialize(mockAddress);
26
+
27
+ LoginPanel.SetActive(false);
28
+ GamePanel.SetActive(true);
29
+
30
+ // HeroManager will auto-check on init, but we can trigger it too
31
+ // Hero.CheckForHero();
32
+ }
33
+
34
+ public void OnExitClicked()
35
+ {
36
+ Application.Quit();
37
+ }
38
+ }
@@ -0,0 +1,37 @@
1
+ using UnityEngine;
2
+
3
+ public class PlayerController : MonoBehaviour
4
+ {
5
+ public float MoveSpeed = 5f;
6
+
7
+ private Vector2 _input;
8
+ private Rigidbody2D _rb;
9
+
10
+ private void Awake()
11
+ {
12
+ _rb = GetComponent<Rigidbody2D>();
13
+ }
14
+
15
+ private void Update()
16
+ {
17
+ // Simple Top-Down Movement
18
+ float x = Input.GetAxisRaw("Horizontal");
19
+ float y = Input.GetAxisRaw("Vertical");
20
+ _input = new Vector2(x, y).normalized;
21
+
22
+ // Animation logic would go here
23
+ }
24
+
25
+ private void FixedUpdate()
26
+ {
27
+ if (_rb != null)
28
+ {
29
+ _rb.velocity = _input * MoveSpeed;
30
+ }
31
+ }
32
+
33
+ public void PlayAttackAnimation()
34
+ {
35
+ Debug.Log("Player swinging sword visualization!");
36
+ }
37
+ }
@@ -0,0 +1,99 @@
1
+ using System.Collections;
2
+ using UnityEngine;
3
+ using UnityEngine.Events;
4
+
5
+ public class HeroManager : MonoBehaviour
6
+ {
7
+ [Header("Game Config")]
8
+ public string GamePackageId = "0x_REPLACE_WITH_PACKAGE_ID";
9
+ public string HeroModule = "hero";
10
+
11
+ [Header("Events")]
12
+ public UnityEvent OnHeroFound;
13
+ public UnityEvent OnHeroMissing;
14
+
15
+ private string _heroObjectId;
16
+
17
+ private void Start()
18
+ {
19
+ // Wait for initialization
20
+ StartCoroutine(WaitForInit());
21
+ }
22
+
23
+ private IEnumerator WaitForInit()
24
+ {
25
+ while (string.IsNullOrEmpty(SuiManager.Instance.ActiveAddress))
26
+ {
27
+ yield return new WaitForSeconds(0.5f);
28
+ }
29
+ CheckForHero();
30
+ }
31
+
32
+ public void MintHero(string heroName)
33
+ {
34
+ Debug.Log($"Minting Hero: {heroName}...");
35
+ // Uses the "Spawner" concept
36
+ SuiManager.Instance.SpawnAsset(
37
+ GamePackageId,
38
+ HeroModule,
39
+ "mint_hero",
40
+ new string[] { heroName },
41
+ (payload) => {
42
+ Debug.Log(">> SIGN THIS PAYLOAD (Mint): " + payload);
43
+ }
44
+ );
45
+ }
46
+
47
+ public void SlayBoar()
48
+ {
49
+ if (string.IsNullOrEmpty(_heroObjectId))
50
+ {
51
+ Debug.LogError("No Hero to slay with!");
52
+ return;
53
+ }
54
+
55
+ Debug.Log("Slaying Boar...");
56
+ // Uses the "Spawner" concept (Action execution)
57
+ SuiManager.Instance.SpawnAsset(
58
+ GamePackageId,
59
+ HeroModule,
60
+ "slay_boar",
61
+ new string[] { _heroObjectId },
62
+ (payload) => Debug.Log(">> SIGN THIS PAYLOAD (Slay): " + payload)
63
+ );
64
+ }
65
+
66
+ public void CheckForHero()
67
+ {
68
+ string address = SuiManager.Instance.ActiveAddress;
69
+ // Request owned objects with type info
70
+ // JSON-RPC params: [address, { showType: true }]
71
+ // We manually construct the inner object JSON for the string param
72
+ string queryParams = $"[\"{address}\", {{ \"options\": {{ \"showType\": true }} }}]";
73
+
74
+ StartCoroutine(SuiManager.Instance.RpcCall(
75
+ "suix_getOwnedObjects",
76
+ queryParams,
77
+ (response) => {
78
+ // Naive parsing for demo. Use NewtonSoft in real project.
79
+ if (response.Contains($"{GamePackageId}::{HeroModule}::Hero"))
80
+ {
81
+ Debug.Log("Hero found via RPC!");
82
+ // Hypothetical parsing:
83
+ // _heroObjectId = ExtractId(response);
84
+ // _heroObjectId = "0x123..."; // Mock for now if parsing fails
85
+
86
+ // Allow manual set for demo if parsing fails (since JsonUtility is weak with nested arrays)
87
+ _heroObjectId = "0x_MOCKED_ID_IF_PARSING_FAILS";
88
+ OnHeroFound?.Invoke();
89
+ }
90
+ else
91
+ {
92
+ Debug.Log("No Hero found.");
93
+ OnHeroMissing?.Invoke();
94
+ }
95
+ },
96
+ (error) => Debug.LogError("Failed to check hero: " + error)
97
+ ));
98
+ }
99
+ }
@@ -0,0 +1,94 @@
1
+ using System;
2
+ using System.Collections;
3
+ using System.Text;
4
+ using UnityEngine;
5
+ using UnityEngine.Networking;
6
+
7
+ public class SuiManager : MonoBehaviour
8
+ {
9
+ [Header("Configuration")]
10
+ public string RpcUrl = "https://fullnode.testnet.sui.io";
11
+
12
+ // In a real game, this would be managed by a Wallet Adapter
13
+ public string ActiveAddress { get; private set; }
14
+
15
+ public static SuiManager Instance { get; private set; }
16
+
17
+ private void Awake()
18
+ {
19
+ if (Instance == null) Instance = this;
20
+ else Destroy(gameObject);
21
+ }
22
+
23
+ public void Initialize(string address)
24
+ {
25
+ ActiveAddress = address;
26
+ Debug.Log($"[SuiManager] Initialized with address: {ActiveAddress}");
27
+ }
28
+
29
+ /// <summary>
30
+ /// Executes a read-only RPC call
31
+ /// </summary>
32
+ public IEnumerator RpcCall(string method, string paramsJson, Action<string> onSuccess, Action<string> onError)
33
+ {
34
+ // Simple JSON-RPC construction
35
+ string jsonRequestBody = $"{{\"jsonrpc\":\"2.0\", \"id\": 1, \"method\": \"{method}\", \"params\": {paramsJson}}}";
36
+
37
+ using (UnityWebRequest req = new UnityWebRequest(RpcUrl, "POST"))
38
+ {
39
+ byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonRequestBody);
40
+ req.uploadHandler = new UploadHandlerRaw(bodyRaw);
41
+ req.downloadHandler = new DownloadHandlerBuffer();
42
+ req.SetRequestHeader("Content-Type", "application/json");
43
+
44
+ yield return req.SendWebRequest();
45
+
46
+ if (req.result != UnityWebRequest.Result.Success)
47
+ {
48
+ Debug.LogError($"[SuiManager] RPC Error: {req.error}");
49
+ onError?.Invoke(req.error);
50
+ }
51
+ else
52
+ {
53
+ // Debug.Log($"[SuiManager] Response: {req.downloadHandler.text}");
54
+ onSuccess?.Invoke(req.downloadHandler.text);
55
+ }
56
+ }
57
+ }
58
+
59
+ /// <summary>
60
+ /// Spawns an asset by constructing a Move Call.
61
+ /// Corresponds to `AssetSpawner.spawn` in the JS SDK.
62
+ /// </summary>
63
+ public void SpawnAsset(string packageId, string module, string function, string[] args, Action<string> onConstructed)
64
+ {
65
+ // In the JS SDK: spawner.spawn(...)
66
+ ConstructMoveCall(packageId, module, function, args, onConstructed);
67
+ }
68
+
69
+ /// <summary>
70
+ /// Syncs game state by checking for events or object updates.
71
+ /// Corresponds to `GameStateSync` in the JS SDK.
72
+ /// </summary>
73
+ public void SyncState(string address, Action<string> onResult)
74
+ {
75
+ string queryParams = $"[\"{address}\", {{ \"options\": {{ \"showType\": true }} }}]";
76
+ StartCoroutine(RpcCall("suix_getOwnedObjects", queryParams, onResult, (err) => Debug.LogWarning(err)));
77
+ }
78
+
79
+ private void ConstructMoveCall(string packageId, string module, string function, string[] args, Action<string> onConstructed)
80
+ {
81
+ // Simulation of payload construction
82
+ var moveCallInfo = new
83
+ {
84
+ target = $"{packageId}::{module}::{function}",
85
+ arguments = args,
86
+ sender = ActiveAddress,
87
+ type = "move_call" // Tag for the wallet adapter
88
+ };
89
+
90
+ string json = JsonUtility.ToJson(moveCallInfo);
91
+ Debug.Log($"[SuiManager] Transaction Payload Ready: {json}");
92
+ onConstructed?.Invoke(json);
93
+ }
94
+ }
@@ -0,0 +1,72 @@
1
+ # Unity 2D RPG Starter + Sui Game SDK
2
+
3
+ A specialized Unity starter kit designed to work with the `sui-game-sdk` ecosystem.
4
+
5
+ ## 🌟 Features
6
+ - **Authentic Project Structure**: Pre-configured with `Assets/Scripts` and `Assets/Scenes`.
7
+ - **Sui Integration**:
8
+ - `SuiManager`: Handles RPC connections and mimics the JS SDK's `Session` management.
9
+ - `HeroManager`: Demonstrates `Spawner` (Minting) and `Sync` (State updates) concepts.
10
+ - **Sample Game**: "Sui Quest" - Mint a hero and slay boars on-chain!
11
+ - **Independent**: Includes its own Move contract and deployment script.
12
+
13
+ ## 🚀 Getting Started
14
+
15
+ ### Prerequisites
16
+ - Unity 2021.3+ (LTS recommended)
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 Unity starter directory
23
+ node deploy.js
24
+ ```
25
+ Copy the **Package ID** output from the deployment.
26
+
27
+ ### 2. Configure Unity
28
+ 1. Open this folder in Unity Hub.
29
+ 2. Open `Assets/Scripts/Sui/HeroManager.cs`.
30
+ 3. Replace `GamePackageId` with your deployed Package ID.
31
+ ```csharp
32
+ public string GamePackageId = "0x..."; // Your ID here
33
+ ```
34
+
35
+ ### 3. Play
36
+ 1. Open `Assets/Scenes/Main.unity`.
37
+ 2. Press **Play**.
38
+ 3. Use the (Simulated) UI or Console logs to interact.
39
+ - **Mint Hero**: Generates a transaction payload.
40
+ - **Slay Boar**: Uses the hero to perform an action.
41
+
42
+ ## 🧩 Architecture
43
+
44
+ This starter mirrors the `sui-game-sdk` (TypeScript) architecture:
45
+
46
+ | Concept | Unity Implementation | Description |
47
+ |---|---|---|
48
+ | **Session** | `SuiManager.ActiveAddress` | Manages the player's active wallet address |
49
+ | **Spawner** | `SuiManager.SpawnAsset` | Constructs Move Calls to create/modify objects |
50
+ | **Sync** | `SuiManager.SyncState` | Polls the chain for object updates and events |
51
+
52
+ ## 📁 Project Structure
53
+
54
+ ```
55
+ unity/
56
+ ├── Assets/
57
+ │ ├── Scenes/
58
+ │ │ └── Main.unity
59
+ │ └── Scripts/
60
+ │ ├── Core/
61
+ │ │ ├── GameManager.cs
62
+ │ │ └── PlayerController.cs
63
+ │ └── Sui/
64
+ │ ├── SuiManager.cs
65
+ │ └── HeroManager.cs
66
+ ├── move/ # Move smart contract
67
+ │ └── sources/
68
+ │ └── hero.move
69
+ └── deploy.js # Deployment script
70
+ ```
71
+
72
+ > **Note**: This starter uses raw HTTP requests to keep dependencies light. For production, integrate a dedicated Unity Wallet Adapter.
@@ -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 HeroManager.cs:');
32
+ console.log(`public string GamePackageId = "${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
+ }