horizon-mcp 0.0.0-development

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 (99) hide show
  1. package/README.md +138 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/prompts/debug-connection.d.ts +3 -0
  7. package/dist/prompts/debug-connection.d.ts.map +1 -0
  8. package/dist/prompts/debug-connection.js +17 -0
  9. package/dist/prompts/debug-connection.js.map +1 -0
  10. package/dist/prompts/explain-feature.d.ts +3 -0
  11. package/dist/prompts/explain-feature.d.ts.map +1 -0
  12. package/dist/prompts/explain-feature.js +32 -0
  13. package/dist/prompts/explain-feature.js.map +1 -0
  14. package/dist/prompts/index.d.ts +6 -0
  15. package/dist/prompts/index.d.ts.map +1 -0
  16. package/dist/prompts/index.js +14 -0
  17. package/dist/prompts/index.js.map +1 -0
  18. package/dist/prompts/integrate-feature.d.ts +3 -0
  19. package/dist/prompts/integrate-feature.d.ts.map +1 -0
  20. package/dist/prompts/integrate-feature.js +35 -0
  21. package/dist/prompts/integrate-feature.js.map +1 -0
  22. package/dist/prompts/setup-auth.d.ts +3 -0
  23. package/dist/prompts/setup-auth.d.ts.map +1 -0
  24. package/dist/prompts/setup-auth.js +26 -0
  25. package/dist/prompts/setup-auth.js.map +1 -0
  26. package/dist/resources/index.d.ts +6 -0
  27. package/dist/resources/index.d.ts.map +1 -0
  28. package/dist/resources/index.js +208 -0
  29. package/dist/resources/index.js.map +1 -0
  30. package/dist/server.d.ts +3 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +15 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/tools/__tests__/api-client.test.d.ts +2 -0
  35. package/dist/tools/__tests__/api-client.test.d.ts.map +1 -0
  36. package/dist/tools/__tests__/api-client.test.js +156 -0
  37. package/dist/tools/__tests__/api-client.test.js.map +1 -0
  38. package/dist/tools/api-client.d.ts +25 -0
  39. package/dist/tools/api-client.d.ts.map +1 -0
  40. package/dist/tools/api-client.js +78 -0
  41. package/dist/tools/api-client.js.map +1 -0
  42. package/dist/tools/auth.d.ts +3 -0
  43. package/dist/tools/auth.d.ts.map +1 -0
  44. package/dist/tools/auth.js +123 -0
  45. package/dist/tools/auth.js.map +1 -0
  46. package/dist/tools/cloud-save.d.ts +3 -0
  47. package/dist/tools/cloud-save.d.ts.map +1 -0
  48. package/dist/tools/cloud-save.js +50 -0
  49. package/dist/tools/cloud-save.js.map +1 -0
  50. package/dist/tools/connection.d.ts +3 -0
  51. package/dist/tools/connection.d.ts.map +1 -0
  52. package/dist/tools/connection.js +20 -0
  53. package/dist/tools/connection.js.map +1 -0
  54. package/dist/tools/feedback.d.ts +3 -0
  55. package/dist/tools/feedback.d.ts.map +1 -0
  56. package/dist/tools/feedback.js +36 -0
  57. package/dist/tools/feedback.js.map +1 -0
  58. package/dist/tools/gift-codes.d.ts +3 -0
  59. package/dist/tools/gift-codes.d.ts.map +1 -0
  60. package/dist/tools/gift-codes.js +52 -0
  61. package/dist/tools/gift-codes.js.map +1 -0
  62. package/dist/tools/index.d.ts +6 -0
  63. package/dist/tools/index.d.ts.map +1 -0
  64. package/dist/tools/index.js +24 -0
  65. package/dist/tools/index.js.map +1 -0
  66. package/dist/tools/leaderboard.d.ts +3 -0
  67. package/dist/tools/leaderboard.d.ts.map +1 -0
  68. package/dist/tools/leaderboard.js +96 -0
  69. package/dist/tools/leaderboard.js.map +1 -0
  70. package/dist/tools/news.d.ts +3 -0
  71. package/dist/tools/news.d.ts.map +1 -0
  72. package/dist/tools/news.js +29 -0
  73. package/dist/tools/news.js.map +1 -0
  74. package/dist/tools/remote-config.d.ts +3 -0
  75. package/dist/tools/remote-config.d.ts.map +1 -0
  76. package/dist/tools/remote-config.js +42 -0
  77. package/dist/tools/remote-config.js.map +1 -0
  78. package/dist/tools/tool-helpers.d.ts +24 -0
  79. package/dist/tools/tool-helpers.d.ts.map +1 -0
  80. package/dist/tools/tool-helpers.js +54 -0
  81. package/dist/tools/tool-helpers.js.map +1 -0
  82. package/dist/tools/user-logs.d.ts +3 -0
  83. package/dist/tools/user-logs.d.ts.map +1 -0
  84. package/dist/tools/user-logs.js +30 -0
  85. package/dist/tools/user-logs.js.map +1 -0
  86. package/package.json +52 -0
  87. package/src/resources/api/app-api.md +495 -0
  88. package/src/resources/docs/auth.md +280 -0
  89. package/src/resources/docs/cloud-save.md +180 -0
  90. package/src/resources/docs/feedback.md +126 -0
  91. package/src/resources/docs/gift-codes.md +153 -0
  92. package/src/resources/docs/leaderboard.md +201 -0
  93. package/src/resources/docs/news.md +114 -0
  94. package/src/resources/docs/overview.md +80 -0
  95. package/src/resources/docs/remote-config.md +149 -0
  96. package/src/resources/docs/user-logs.md +144 -0
  97. package/src/resources/quickstart/godot.md +224 -0
  98. package/src/resources/quickstart/unity.md +317 -0
  99. package/src/resources/quickstart/unreal.md +390 -0
@@ -0,0 +1,144 @@
1
+ # User Logs
2
+
3
+ ## Overview
4
+
5
+ User Logs allow you to track player events, errors, and actions on the server. This is useful for debugging, analytics, and monitoring player behavior. Log entries are viewable per-user in the horizOn Dashboard.
6
+
7
+ **Important:** User Logs require **BASIC tier or higher**. The feature is not available on the FREE tier and will return a `403` error.
8
+
9
+ ## Endpoints
10
+
11
+ ### Create Log
12
+
13
+ **`POST /api/v1/app/user-logs/create`**
14
+
15
+ Creates a log entry for a user.
16
+
17
+ **Request Body:**
18
+
19
+ | Field | Type | Required | Description |
20
+ |-------|------|----------|-------------|
21
+ | `message` | string | Yes | Log message (max 1000 characters) |
22
+ | `type` | string | Yes | Log type: `INFO`, `WARN`, or `ERROR` |
23
+ | `userId` | string | Yes | The user's ID |
24
+ | `errorCode` | string | No | Error code for categorization (max 50 characters) |
25
+
26
+ **Response (200):**
27
+
28
+ ```json
29
+ {
30
+ "id": "log-abc123",
31
+ "createdAt": "2025-01-15T10:30:00Z"
32
+ }
33
+ ```
34
+
35
+ ## Log Types
36
+
37
+ | Type | Use Case |
38
+ |------|----------|
39
+ | `INFO` | General events: tutorial complete, level up, purchase |
40
+ | `WARN` | Non-critical issues: low memory, retry attempt, degraded performance |
41
+ | `ERROR` | Critical issues: crash, save failure, network error |
42
+
43
+ ## Code Examples
44
+
45
+ ### Godot (GDScript)
46
+
47
+ ```gdscript
48
+ # Create typed log entries
49
+ await Horizon.userLogs.info("Player completed tutorial")
50
+ await Horizon.userLogs.warn("Low memory detected")
51
+ await Horizon.userLogs.error("Failed to load asset", "ERR_001")
52
+
53
+ # Log game events
54
+ await Horizon.userLogs.logEvent("level_complete", "Level 5")
55
+ await Horizon.userLogs.logEvent("purchase", "item_sword_01")
56
+
57
+ # Log errors with stack trace
58
+ await Horizon.userLogs.logError("Null reference in combat system", "Line 42: combat.gd")
59
+
60
+ # Full createLog call
61
+ var result = await Horizon.userLogs.createLog(
62
+ HorizonUserLogs.LogType.ERROR,
63
+ "Detailed error message here",
64
+ "SAVE_FAILED"
65
+ )
66
+ if not result.is_empty():
67
+ print("Log ID: %s, Created: %s" % [result.id, result.createdAt])
68
+
69
+ # Listen for events
70
+ Horizon.userLogs.log_created.connect(func(id, created_at): print("Log %s created" % id))
71
+ Horizon.userLogs.log_create_failed.connect(func(error): print("Failed: %s" % error))
72
+ ```
73
+
74
+ ### Unity (C#)
75
+
76
+ ```csharp
77
+ using PM.horizOn.Cloud.Manager;
78
+ using PM.horizOn.Cloud.Enums;
79
+
80
+ // Convenience methods
81
+ await UserLogManager.Instance.Info("Tutorial completed");
82
+ await UserLogManager.Instance.Warn("Low memory detected");
83
+ await UserLogManager.Instance.Error("Save failed", errorCode: "SAVE_001");
84
+
85
+ // Full CreateLog call
86
+ var result = await UserLogManager.Instance.CreateLog(
87
+ LogType.ERROR,
88
+ "Detailed error message",
89
+ errorCode: "CRASH_002"
90
+ );
91
+ if (result != null)
92
+ {
93
+ Debug.Log($"Log ID: {result.id}, Created: {result.createdAt}");
94
+ }
95
+ ```
96
+
97
+ ### REST (cURL)
98
+
99
+ ```bash
100
+ curl -X POST https://horizon.pm/api/v1/app/user-logs/create \
101
+ -H "X-API-Key: YOUR_API_KEY" \
102
+ -H "Content-Type: application/json" \
103
+ -d '{
104
+ "message": "Player completed tutorial",
105
+ "type": "INFO",
106
+ "userId": "user123"
107
+ }'
108
+
109
+ # With error code
110
+ curl -X POST https://horizon.pm/api/v1/app/user-logs/create \
111
+ -H "X-API-Key: YOUR_API_KEY" \
112
+ -H "Content-Type: application/json" \
113
+ -d '{
114
+ "message": "Failed to save game data",
115
+ "type": "ERROR",
116
+ "userId": "user123",
117
+ "errorCode": "SAVE_FAILED"
118
+ }'
119
+ ```
120
+
121
+ ## Best Practices
122
+
123
+ - **Log meaningful events** — Track milestones (tutorial complete, level up), not routine actions.
124
+ - **Use error codes** — Consistent error codes make it easy to filter and analyze issues in the Dashboard.
125
+ - **Truncate long messages** — Messages are limited to 1000 characters. Both SDKs auto-truncate.
126
+ - **Be aware of rate limits** — Each log creation counts toward the 10 req/min limit. Batch or throttle logging.
127
+
128
+ ## Tier Requirements
129
+
130
+ | Tier | User Logs Available |
131
+ |------|-------------------|
132
+ | FREE | No (returns 403) |
133
+ | BASIC | Yes |
134
+ | PRO | Yes |
135
+ | ENTERPRISE | Yes |
136
+
137
+ ## Common Errors
138
+
139
+ | Status | Cause | Solution |
140
+ |--------|-------|----------|
141
+ | 400 | Missing required fields | Ensure `message`, `type`, and `userId` are provided |
142
+ | 401 | Invalid API key | Check `X-API-Key` header |
143
+ | 403 | FREE tier account | Upgrade to BASIC or higher |
144
+ | 429 | Rate limit exceeded | Throttle log submissions |
@@ -0,0 +1,224 @@
1
+ # Godot Quickstart Guide
2
+
3
+ ## Requirements
4
+
5
+ - **Godot 4.5** or later
6
+ - horizOn API key (get one at [horizon.pm](https://horizon.pm))
7
+
8
+ ## Step 1: Install the SDK
9
+
10
+ ### Option A: Asset Library (Recommended)
11
+
12
+ 1. Open Godot and go to **AssetLib**
13
+ 2. Search for "horizOn SDK"
14
+ 3. Download and install
15
+ 4. Enable the plugin: **Project > Project Settings > Plugins > horizOn SDK**
16
+
17
+ ### Option B: Manual Installation
18
+
19
+ 1. Download the latest release from [GitHub Releases](https://github.com/ProjectMakersDE/horizOn-SDK-Godot/releases)
20
+ 2. Copy the `addons/horizon_sdk` folder to your project's `addons/` directory
21
+ 3. Enable the plugin: **Project > Project Settings > Plugins > horizOn SDK**
22
+
23
+ ## Step 2: Import Configuration
24
+
25
+ 1. Go to the [horizOn Dashboard](https://horizon.pm) and navigate to SDK Settings
26
+ 2. Download your `horizOn_config.json` file
27
+ 3. In Godot, go to **Project > Tools > horizOn: Import Config...**
28
+ 4. Select your downloaded JSON file
29
+
30
+ This creates a config resource at `res://addons/horizon_sdk/horizon_config.tres` with your API key and server hosts.
31
+
32
+ ## Step 3: Connect to Server
33
+
34
+ The SDK is accessed through the `Horizon` singleton (auto-registered when the plugin is enabled).
35
+
36
+ ```gdscript
37
+ extends Node
38
+
39
+ func _ready():
40
+ # Connect to the best available server (auto-selects lowest latency)
41
+ var connected = await Horizon.connect_to_server()
42
+ if not connected:
43
+ print("Failed to connect to horizOn!")
44
+ return
45
+
46
+ print("Connected to %s" % Horizon.getActiveHost())
47
+ ```
48
+
49
+ ## Step 4: Authenticate
50
+
51
+ ```gdscript
52
+ # Quick anonymous sign-in (creates new user or restores cached session)
53
+ var signed_in = await Horizon.quickSignInAnonymous("Player1")
54
+ if signed_in:
55
+ var user = Horizon.getCurrentUser()
56
+ print("Welcome, %s! (ID: %s)" % [user.displayName, user.userId])
57
+ ```
58
+
59
+ ### Other Authentication Methods
60
+
61
+ ```gdscript
62
+ # Email sign-up
63
+ await Horizon.auth.signUpEmail("user@example.com", "password123", "MyUsername")
64
+
65
+ # Email sign-in
66
+ await Horizon.auth.signInEmail("user@example.com", "password123")
67
+
68
+ # Anonymous sign-up with explicit token
69
+ await Horizon.auth.signUpAnonymous("DisplayName")
70
+
71
+ # Restore anonymous session from cache
72
+ await Horizon.auth.restoreAnonymousSession()
73
+
74
+ # Sign out
75
+ Horizon.auth.signOut()
76
+ ```
77
+
78
+ ## Step 5: Use Features
79
+
80
+ ### Leaderboards
81
+
82
+ ```gdscript
83
+ # Submit a score (only updates if higher than previous best)
84
+ await Horizon.leaderboard.submitScore(1000)
85
+
86
+ # Get top 10 players
87
+ var top: Array[HorizonLeaderboardEntry] = await Horizon.leaderboard.getTop(10)
88
+ for entry in top:
89
+ print("#%d %s: %d" % [entry.position, entry.username, entry.score])
90
+
91
+ # Get your rank
92
+ var rank: HorizonLeaderboardEntry = await Horizon.leaderboard.getRank()
93
+
94
+ # Get entries around your position
95
+ var around: Array[HorizonLeaderboardEntry] = await Horizon.leaderboard.getAround(5)
96
+ ```
97
+
98
+ ### Cloud Saves
99
+
100
+ ```gdscript
101
+ # Save a Dictionary (recommended)
102
+ await Horizon.cloudSave.saveObject({"level": 5, "coins": 1000})
103
+
104
+ # Load as Dictionary
105
+ var data: Dictionary = await Horizon.cloudSave.loadObject()
106
+ if not data.is_empty():
107
+ var level = data.get("level", 1)
108
+
109
+ # Save/load raw strings
110
+ await Horizon.cloudSave.saveData('{"custom": "json"}')
111
+ var json: String = await Horizon.cloudSave.loadData()
112
+
113
+ # Binary data
114
+ await Horizon.cloudSave.saveBytes(my_bytes)
115
+ var bytes: PackedByteArray = await Horizon.cloudSave.loadBytes()
116
+ ```
117
+
118
+ ### Remote Config
119
+
120
+ ```gdscript
121
+ # Typed getters with defaults
122
+ var max_level: int = await Horizon.remoteConfig.getInt("max_level", 100)
123
+ var difficulty: float = await Horizon.remoteConfig.getFloat("difficulty", 1.0)
124
+ var maintenance: bool = await Horizon.remoteConfig.getBool("maintenance_mode", false)
125
+
126
+ # Get all configs at once (recommended at startup)
127
+ var all_configs: Dictionary = await Horizon.remoteConfig.getAllConfigs()
128
+ ```
129
+
130
+ ### News
131
+
132
+ ```gdscript
133
+ var news: Array[HorizonNewsEntry] = await Horizon.news.loadNews(20, "en")
134
+ for entry in news:
135
+ print("%s: %s" % [entry.title, entry.message])
136
+ ```
137
+
138
+ ### Gift Codes
139
+
140
+ ```gdscript
141
+ var is_valid = await Horizon.giftCodes.validate("ABCD-1234")
142
+ if is_valid:
143
+ var result = await Horizon.giftCodes.redeem("ABCD-1234")
144
+ if result.get("success", false):
145
+ print("Rewards: %s" % result.get("giftData", ""))
146
+ ```
147
+
148
+ ### Feedback
149
+
150
+ ```gdscript
151
+ await Horizon.feedback.submitBugReport("Crash on Level 5", "Game crashes when...")
152
+ await Horizon.feedback.submitFeatureRequest("Dark Mode", "Add dark mode option")
153
+ ```
154
+
155
+ ### User Logs
156
+
157
+ ```gdscript
158
+ # Requires BASIC tier or higher
159
+ await Horizon.userLogs.info("Player completed tutorial")
160
+ await Horizon.userLogs.warn("Low memory detected")
161
+ await Horizon.userLogs.error("Failed to load asset", "ERR_001")
162
+ await Horizon.userLogs.logEvent("level_complete", "Level 5")
163
+ ```
164
+
165
+ ## Signals (Event-Driven)
166
+
167
+ All operations emit signals for reactive programming:
168
+
169
+ ```gdscript
170
+ # SDK lifecycle
171
+ Horizon.sdk_initialized.connect(func(): print("SDK ready"))
172
+ Horizon.sdk_connected.connect(func(host): print("Connected to %s" % host))
173
+
174
+ # Authentication
175
+ Horizon.auth.signin_completed.connect(func(user): print("Signed in: %s" % user.userId))
176
+ Horizon.auth.signin_failed.connect(func(error): print("Auth error: %s" % error))
177
+
178
+ # Features
179
+ Horizon.leaderboard.score_submitted.connect(func(score): print("Score: %d" % score))
180
+ Horizon.cloudSave.data_saved.connect(func(size): print("Saved %d bytes" % size))
181
+ Horizon.news.news_loaded.connect(func(entries): print("Loaded %d news" % entries.size()))
182
+ ```
183
+
184
+ ## Configuration Options
185
+
186
+ Edit your config resource at `addons/horizon_sdk/horizon_config.tres`:
187
+
188
+ | Option | Default | Description |
189
+ |--------|---------|-------------|
190
+ | `api_key` | - | Your horizOn API key |
191
+ | `hosts` | - | Array of backend server URLs |
192
+ | `connection_timeout_seconds` | 10 | HTTP request timeout |
193
+ | `max_retry_attempts` | 3 | Retry count for failed requests |
194
+ | `retry_delay_seconds` | 1.0 | Delay between retries |
195
+ | `log_level` | INFO | DEBUG, INFO, WARNING, ERROR, NONE |
196
+
197
+ ## Rate Limit Best Practices
198
+
199
+ The rate limit is **10 requests per minute per client** (all tiers). Plan your API calls:
200
+
201
+ ```gdscript
202
+ func _ready():
203
+ var connected = await Horizon.connect_to_server()
204
+ if not connected:
205
+ return
206
+
207
+ # Authenticate (1 request)
208
+ await Horizon.quickSignInAnonymous("Player")
209
+
210
+ # Startup batch (3 requests)
211
+ await Horizon.remoteConfig.getAllConfigs()
212
+ await Horizon.news.loadNews()
213
+ await Horizon.leaderboard.getTop(10)
214
+
215
+ # 6 requests remaining for gameplay actions
216
+ ```
217
+
218
+ ## Example Project
219
+
220
+ The SDK includes a test scene demonstrating all features:
221
+
222
+ ```
223
+ res://addons/horizon_sdk/examples/horizon_test_scene.tscn
224
+ ```
@@ -0,0 +1,317 @@
1
+ # Unity Quickstart Guide
2
+
3
+ ## Requirements
4
+
5
+ - **Unity 2023.3+** (Unity 6)
6
+ - horizOn API key (get one at [horizon.pm](https://horizon.pm))
7
+ - Namespace: `PM.horizOn.Cloud`
8
+
9
+ ## Step 1: Import the SDK
10
+
11
+ 1. Download the horizOn Cloud SDK package
12
+ 2. Import into your project: `Assets/Plugins/ProjectMakers/horizOn/`
13
+ 3. The SDK is ready to use once imported (no plugin activation needed)
14
+
15
+ ## Step 2: Import Configuration
16
+
17
+ 1. Go to the [horizOn Dashboard](https://horizon.pm) and navigate to SDK Settings
18
+ 2. Download your `horizOn_config.json` file
19
+ 3. In Unity, go to **Window > horizOn > Config Importer**
20
+ 4. Select your downloaded JSON file
21
+ 5. The config is saved as a ScriptableObject in the SDK's Resources folder
22
+
23
+ ## Step 3: Initialize and Connect
24
+
25
+ ```csharp
26
+ using PM.horizOn.Cloud.Core;
27
+ using PM.horizOn.Cloud.Manager;
28
+
29
+ public class GameManager : MonoBehaviour
30
+ {
31
+ async void Start()
32
+ {
33
+ // Initialize the SDK (creates services, loads config)
34
+ HorizonApp.Initialize();
35
+
36
+ // Connect to the best available server
37
+ var server = new HorizonServer();
38
+ await server.Connect();
39
+
40
+ Debug.Log("Connected to horizOn!");
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## Step 4: Authenticate
46
+
47
+ ```csharp
48
+ // Anonymous sign-up (auto-generates and caches token)
49
+ await UserManager.Instance.SignUpAnonymous("PlayerName");
50
+
51
+ // Check if signed in
52
+ if (UserManager.Instance.IsSignedIn)
53
+ {
54
+ var user = UserManager.Instance.CurrentUser;
55
+ Debug.Log($"Welcome, {user.DisplayName}! (ID: {user.UserId})");
56
+ }
57
+ ```
58
+
59
+ ### Other Authentication Methods
60
+
61
+ ```csharp
62
+ // Email sign-up
63
+ await UserManager.Instance.SignUpEmail("user@example.com", "password123", "DisplayName");
64
+
65
+ // Email sign-in
66
+ await UserManager.Instance.SignInEmail("user@example.com", "password123");
67
+
68
+ // Anonymous sign-in (restore cached session)
69
+ await UserManager.Instance.RestoreAnonymousSession();
70
+
71
+ // Google sign-in
72
+ await UserManager.Instance.SignInGoogle("google-authorization-code");
73
+
74
+ // Change display name
75
+ await UserManager.Instance.ChangeName("NewName");
76
+
77
+ // Sign out (preserves anonymous token by default)
78
+ UserManager.Instance.SignOut();
79
+ ```
80
+
81
+ ## Step 5: Use Features
82
+
83
+ ### Leaderboards
84
+
85
+ ```csharp
86
+ using PM.horizOn.Cloud.Manager;
87
+
88
+ // Submit score (only updates if higher than previous best)
89
+ await LeaderboardManager.Instance.SubmitScore(12500);
90
+
91
+ // Get top 10 players
92
+ var top = await LeaderboardManager.Instance.GetTop(10);
93
+ foreach (var entry in top)
94
+ {
95
+ Debug.Log($"#{entry.position} {entry.username}: {entry.score}");
96
+ }
97
+
98
+ // Get your rank
99
+ var rank = await LeaderboardManager.Instance.GetRank();
100
+ Debug.Log($"My rank: #{rank.position} (Score: {rank.score})");
101
+
102
+ // Get entries around your position
103
+ var around = await LeaderboardManager.Instance.GetAround(5);
104
+ ```
105
+
106
+ ### Cloud Saves
107
+
108
+ ```csharp
109
+ using PM.horizOn.Cloud.Manager;
110
+
111
+ // Define your save structure
112
+ [System.Serializable]
113
+ public class GameData
114
+ {
115
+ public int Level;
116
+ public int Coins;
117
+ public string[] Inventory;
118
+ }
119
+
120
+ // Save a typed object
121
+ var saveData = new GameData { Level = 5, Coins = 1000, Inventory = new[] { "sword" } };
122
+ await CloudSaveManager.Instance.SaveObject(saveData);
123
+
124
+ // Load a typed object
125
+ var loaded = await CloudSaveManager.Instance.LoadObject<GameData>();
126
+ if (loaded != null)
127
+ {
128
+ Debug.Log($"Level: {loaded.Level}, Coins: {loaded.Coins}");
129
+ }
130
+
131
+ // Save/load raw strings
132
+ await CloudSaveManager.Instance.Save("{\"level\": 5}");
133
+ string json = await CloudSaveManager.Instance.Load();
134
+
135
+ // Binary data
136
+ byte[] data = GetBinaryData();
137
+ await CloudSaveManager.Instance.SaveBytes(data);
138
+ byte[] loadedBytes = await CloudSaveManager.Instance.LoadBytes();
139
+ ```
140
+
141
+ ### Remote Config
142
+
143
+ ```csharp
144
+ using PM.horizOn.Cloud.Manager;
145
+
146
+ // Type-safe getters with defaults
147
+ int maxLives = await RemoteConfigManager.Instance.GetInt("max_lives", 3);
148
+ float difficulty = await RemoteConfigManager.Instance.GetFloat("difficulty", 1.0f);
149
+ bool eventActive = await RemoteConfigManager.Instance.GetBool("holiday_event", false);
150
+ string version = await RemoteConfigManager.Instance.GetString("game_version", "1.0.0");
151
+
152
+ // Get all configs at once (recommended at startup)
153
+ var configs = await RemoteConfigManager.Instance.GetAllConfigs();
154
+ ```
155
+
156
+ ### News
157
+
158
+ ```csharp
159
+ using PM.horizOn.Cloud.Manager;
160
+
161
+ // Load news entries
162
+ var news = await NewsManager.Instance.LoadNews(limit: 10);
163
+ foreach (var item in news)
164
+ {
165
+ Debug.Log($"{item.title}: {item.message}");
166
+ }
167
+
168
+ // Filter by language
169
+ var germanNews = await NewsManager.Instance.LoadNews(limit: 10, languageCode: "de");
170
+
171
+ // Get cached entry by ID
172
+ var entry = NewsManager.Instance.GetNewsById("news-001");
173
+ ```
174
+
175
+ ### Gift Codes
176
+
177
+ ```csharp
178
+ using PM.horizOn.Cloud.Manager;
179
+
180
+ // Validate first (optional)
181
+ bool? valid = await GiftCodeManager.Instance.Validate("PROMO2024");
182
+
183
+ // Redeem
184
+ var result = await GiftCodeManager.Instance.Redeem("PROMO2024");
185
+ if (result != null && result.success)
186
+ {
187
+ string giftData = result.giftData; // JSON string with rewards
188
+ Debug.Log($"Rewards: {giftData}");
189
+ }
190
+ ```
191
+
192
+ ### Feedback
193
+
194
+ ```csharp
195
+ using PM.horizOn.Cloud.Manager;
196
+
197
+ // Bug report with auto device info
198
+ await FeedbackManager.Instance.ReportBug(
199
+ title: "Crash on level 5",
200
+ message: "Game crashes when opening inventory"
201
+ );
202
+
203
+ // Feature request
204
+ await FeedbackManager.Instance.RequestFeature(
205
+ title: "Dark mode",
206
+ message: "Please add dark mode option"
207
+ );
208
+
209
+ // General feedback with email
210
+ await FeedbackManager.Instance.SendGeneral(
211
+ title: "Great game!",
212
+ message: "Really enjoying the new update",
213
+ email: "user@example.com"
214
+ );
215
+ ```
216
+
217
+ ### User Logs
218
+
219
+ ```csharp
220
+ using PM.horizOn.Cloud.Manager;
221
+
222
+ // Requires BASIC tier or higher
223
+ await UserLogManager.Instance.Info("Tutorial completed");
224
+ await UserLogManager.Instance.Warn("Low memory detected");
225
+ await UserLogManager.Instance.Error("Save failed", errorCode: "SAVE_001");
226
+ ```
227
+
228
+ ## Event System
229
+
230
+ Subscribe to SDK events for reactive patterns:
231
+
232
+ ```csharp
233
+ using PM.horizOn.Cloud.Core;
234
+ using PM.horizOn.Cloud.Enums;
235
+ using PM.horizOn.Cloud.Objects.Data;
236
+
237
+ // Subscribe to auth events
238
+ HorizonApp.Events.Subscribe<UserData>(EventKeys.UserSignInSuccess, OnUserSignedIn);
239
+ HorizonApp.Events.Subscribe<string>(EventKeys.UserSignInFailed, OnSignInFailed);
240
+
241
+ void OnUserSignedIn(UserData user)
242
+ {
243
+ Debug.Log($"Welcome back, {user.DisplayName}!");
244
+ }
245
+
246
+ void OnSignInFailed(string error)
247
+ {
248
+ Debug.Log($"Sign-in failed: {error}");
249
+ }
250
+ ```
251
+
252
+ ## Error Handling
253
+
254
+ ```csharp
255
+ // Check return values
256
+ bool success = await UserManager.Instance.SignInEmail(email, password);
257
+ if (!success)
258
+ {
259
+ Debug.Log("Sign-in failed. Check credentials.");
260
+ }
261
+
262
+ // Cloud save with fallback
263
+ var data = await CloudSaveManager.Instance.LoadObject<GameData>();
264
+ if (data == null)
265
+ {
266
+ data = new GameData(); // Use defaults on first load
267
+ }
268
+ ```
269
+
270
+ ## Tier Limits
271
+
272
+ | Feature | FREE | BASIC | PRO | ENTERPRISE |
273
+ |---------|------|-------|-----|------------|
274
+ | Cloud Save | 1 KB | 5 KB | 20 KB | 250 KB |
275
+ | User Logs | No | Yes | Yes | Yes |
276
+ | Rate Limit | 10/min | 10/min | 10/min | 10/min |
277
+
278
+ ## Efficient Startup Pattern
279
+
280
+ Plan your API calls to stay within the rate limit:
281
+
282
+ ```csharp
283
+ async void Start()
284
+ {
285
+ // Initialize and connect
286
+ HorizonApp.Initialize();
287
+ await new HorizonServer().Connect();
288
+
289
+ // Startup loads (3 requests)
290
+ await UserManager.Instance.CheckAuth();
291
+ await RemoteConfigManager.Instance.GetAllConfigs();
292
+ await NewsManager.Instance.LoadNews();
293
+
294
+ // 7 requests remaining for gameplay actions
295
+ }
296
+ ```
297
+
298
+ ## Project Structure
299
+
300
+ ```
301
+ Assets/Plugins/ProjectMakers/horizOn/
302
+ ├── CloudSDK/
303
+ │ ├── Core/ # HorizonApp, HorizonServer, HorizonConfig
304
+ │ ├── Manager/ # Feature managers (UserManager, LeaderboardManager, etc.)
305
+ │ ├── Service/ # EventService, NetworkService, LogService
306
+ │ ├── Objects/ # Data models, requests, responses
307
+ │ └── Resources/ # HorizonConfig.asset
308
+ ```
309
+
310
+ ## Common Status Codes
311
+
312
+ | Code | Meaning | Action |
313
+ |------|---------|--------|
314
+ | 400 | Bad Request | Check parameters |
315
+ | 401 | Unauthorized | Verify API key |
316
+ | 403 | Forbidden | Check tier/permissions |
317
+ | 429 | Rate Limited | Implement caching, wait and retry |