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,153 @@
1
+ # Gift Codes
2
+
3
+ ## Overview
4
+
5
+ Gift Codes allow you to create promotional codes that players can redeem for in-game rewards. Codes are created in the horizOn Dashboard with associated reward data. Each code can be single-use or multi-use, and can optionally have an expiration date.
6
+
7
+ The typical flow is:
8
+
9
+ 1. **Validate** (optional) — Check if a code is valid before redeeming
10
+ 2. **Redeem** — Redeem the code to receive the reward data
11
+
12
+ ## Endpoints
13
+
14
+ ### Validate Gift Code
15
+
16
+ **`POST /api/v1/app/gift-codes/validate`**
17
+
18
+ Checks if a gift code is valid and can be redeemed by the user, without consuming it.
19
+
20
+ **Request Body:**
21
+
22
+ | Field | Type | Required | Description |
23
+ |-------|------|----------|-------------|
24
+ | `code` | string | Yes | The gift code to validate |
25
+ | `userId` | string | Yes | The user's ID |
26
+
27
+ **Response (200):**
28
+
29
+ ```json
30
+ {
31
+ "valid": true
32
+ }
33
+ ```
34
+
35
+ `valid` is `false` if the code does not exist, is expired, or has already been redeemed by this user.
36
+
37
+ ---
38
+
39
+ ### Redeem Gift Code
40
+
41
+ **`POST /api/v1/app/gift-codes/redeem`**
42
+
43
+ Redeems a gift code and returns the associated reward data.
44
+
45
+ **Request Body:**
46
+
47
+ | Field | Type | Required | Description |
48
+ |-------|------|----------|-------------|
49
+ | `code` | string | Yes | The gift code to redeem |
50
+ | `userId` | string | Yes | The user's ID |
51
+
52
+ **Response (200):**
53
+
54
+ ```json
55
+ {
56
+ "success": true,
57
+ "message": "Gift code redeemed successfully",
58
+ "giftData": "{\"coins\": 500, \"gems\": 10}"
59
+ }
60
+ ```
61
+
62
+ `giftData` is a JSON string set by the developer in the Dashboard. Your app must parse and apply the rewards.
63
+
64
+ **Failed redemption:**
65
+
66
+ ```json
67
+ {
68
+ "success": false,
69
+ "message": "Gift code already redeemed by this user",
70
+ "giftData": null
71
+ }
72
+ ```
73
+
74
+ ## Code Examples
75
+
76
+ ### Godot (GDScript)
77
+
78
+ ```gdscript
79
+ # Validate a code first (optional)
80
+ var is_valid = await Horizon.giftCodes.validate("ABCD-1234")
81
+ if is_valid:
82
+ print("Code is valid!")
83
+
84
+ # Redeem a code
85
+ var result = await Horizon.giftCodes.redeem("ABCD-1234")
86
+ if result.get("success", false):
87
+ var gift_data = result.get("giftData", "")
88
+ print("Rewards: %s" % gift_data)
89
+
90
+ # Redeem and auto-parse rewards as Dictionary
91
+ var parsed = await Horizon.giftCodes.redeemParsed("ABCD-1234")
92
+ if parsed.get("success", false):
93
+ var rewards: Dictionary = parsed.get("rewards", {})
94
+ var coins = rewards.get("coins", 0)
95
+ var gems = rewards.get("gems", 0)
96
+
97
+ # Listen for events
98
+ Horizon.giftCodes.code_validated.connect(func(code, valid): print("%s: %s" % [code, "valid" if valid else "invalid"]))
99
+ Horizon.giftCodes.code_redeemed.connect(func(code, data): print("Redeemed %s: %s" % [code, data]))
100
+ Horizon.giftCodes.code_redeem_failed.connect(func(code, error): print("Failed: %s" % error))
101
+ ```
102
+
103
+ ### Unity (C#)
104
+
105
+ ```csharp
106
+ using PM.horizOn.Cloud.Manager;
107
+
108
+ // Validate (optional)
109
+ bool? valid = await GiftCodeManager.Instance.Validate("PROMO2024");
110
+ if (valid == true)
111
+ {
112
+ Debug.Log("Code is valid!");
113
+ }
114
+
115
+ // Redeem
116
+ var result = await GiftCodeManager.Instance.Redeem("PROMO2024");
117
+ if (result != null && result.success)
118
+ {
119
+ // Parse giftData JSON for rewards
120
+ string giftData = result.giftData;
121
+ Debug.Log($"Rewards: {giftData}");
122
+ }
123
+ ```
124
+
125
+ ### REST (cURL)
126
+
127
+ ```bash
128
+ # Validate
129
+ curl -X POST https://horizon.pm/api/v1/app/gift-codes/validate \
130
+ -H "X-API-Key: YOUR_API_KEY" \
131
+ -H "Content-Type: application/json" \
132
+ -d '{"code": "ABCD-1234", "userId": "user123"}'
133
+
134
+ # Redeem
135
+ curl -X POST https://horizon.pm/api/v1/app/gift-codes/redeem \
136
+ -H "X-API-Key: YOUR_API_KEY" \
137
+ -H "Content-Type: application/json" \
138
+ -d '{"code": "ABCD-1234", "userId": "user123"}'
139
+ ```
140
+
141
+ ## Best Practices
142
+
143
+ - **Validate before redeeming** — Show the user whether their code is valid before consuming it.
144
+ - **Parse giftData in your app** — The reward structure is defined by you in the Dashboard. Parse the JSON string and apply rewards accordingly.
145
+ - **Handle already-redeemed codes** — Check the `success` field and display the `message` to the user.
146
+
147
+ ## Common Errors
148
+
149
+ | Status | Cause | Solution |
150
+ |--------|-------|----------|
151
+ | 401 | Invalid API key | Check `X-API-Key` header |
152
+ | 404 | Code does not exist | Verify the code spelling |
153
+ | 429 | Rate limit exceeded | Avoid rapid repeated redemption attempts |
@@ -0,0 +1,201 @@
1
+ # Leaderboards
2
+
3
+ ## Overview
4
+
5
+ horizOn leaderboards provide global rankings for your app. Scores are submitted per user and **only update if the new score is higher** than the user's previous best. This ensures leaderboards always reflect each player's best achievement.
6
+
7
+ ## Endpoints
8
+
9
+ ### Submit Score
10
+
11
+ **`POST /api/v1/app/leaderboard/submit`**
12
+
13
+ Submits a score for the authenticated user. Only updates if the score is higher than the previous best.
14
+
15
+ **Request Body:**
16
+
17
+ | Field | Type | Required | Description |
18
+ |-------|------|----------|-------------|
19
+ | `userId` | string | Yes | The user's ID |
20
+ | `score` | number | Yes | Score value (positive integer) |
21
+
22
+ **Response (200):**
23
+
24
+ ```json
25
+ {
26
+ "success": true
27
+ }
28
+ ```
29
+
30
+ ---
31
+
32
+ ### Get Top Entries
33
+
34
+ **`GET /api/v1/app/leaderboard/top?userId={userId}&limit={limit}`**
35
+
36
+ Returns the top entries on the leaderboard.
37
+
38
+ **Query Parameters:**
39
+
40
+ | Param | Type | Required | Description |
41
+ |-------|------|----------|-------------|
42
+ | `userId` | string | Yes | The requesting user's ID |
43
+ | `limit` | number | No | Number of entries (default 10, max 100) |
44
+
45
+ **Response (200):**
46
+
47
+ ```json
48
+ {
49
+ "entries": [
50
+ { "position": 1, "username": "TopPlayer", "score": 50000 },
51
+ { "position": 2, "username": "Runner-Up", "score": 45000 },
52
+ { "position": 3, "username": "ThirdPlace", "score": 40000 }
53
+ ]
54
+ }
55
+ ```
56
+
57
+ ---
58
+
59
+ ### Get User Rank
60
+
61
+ **`GET /api/v1/app/leaderboard/rank?userId={userId}`**
62
+
63
+ Returns the current user's position on the leaderboard.
64
+
65
+ **Query Parameters:**
66
+
67
+ | Param | Type | Required | Description |
68
+ |-------|------|----------|-------------|
69
+ | `userId` | string | Yes | The user's ID |
70
+
71
+ **Response (200):**
72
+
73
+ ```json
74
+ {
75
+ "position": 42,
76
+ "username": "MyPlayer",
77
+ "score": 12500
78
+ }
79
+ ```
80
+
81
+ ---
82
+
83
+ ### Get Entries Around User
84
+
85
+ **`GET /api/v1/app/leaderboard/around?userId={userId}&range={range}`**
86
+
87
+ Returns entries around the user's position (players ranked near them).
88
+
89
+ **Query Parameters:**
90
+
91
+ | Param | Type | Required | Description |
92
+ |-------|------|----------|-------------|
93
+ | `userId` | string | Yes | The user's ID |
94
+ | `range` | number | No | Number of entries before and after (default 10) |
95
+
96
+ **Response (200):**
97
+
98
+ ```json
99
+ {
100
+ "entries": [
101
+ { "position": 40, "username": "NearbyPlayer1", "score": 13000 },
102
+ { "position": 41, "username": "NearbyPlayer2", "score": 12800 },
103
+ { "position": 42, "username": "MyPlayer", "score": 12500 },
104
+ { "position": 43, "username": "NearbyPlayer3", "score": 12200 }
105
+ ]
106
+ }
107
+ ```
108
+
109
+ ## Code Examples
110
+
111
+ ### Godot (GDScript)
112
+
113
+ ```gdscript
114
+ # Submit a score (only updates if higher than previous best)
115
+ await Horizon.leaderboard.submitScore(1000)
116
+
117
+ # Get top 10 players
118
+ var top: Array[HorizonLeaderboardEntry] = await Horizon.leaderboard.getTop(10)
119
+ for entry in top:
120
+ print("#%d %s: %d" % [entry.position, entry.username, entry.score])
121
+
122
+ # Get current user's rank
123
+ var myRank: HorizonLeaderboardEntry = await Horizon.leaderboard.getRank()
124
+ print("My rank: #%d (Score: %d)" % [myRank.position, myRank.score])
125
+
126
+ # Get entries around the user's position
127
+ var around: Array[HorizonLeaderboardEntry] = await Horizon.leaderboard.getAround(5)
128
+
129
+ # Use caching (enabled by default)
130
+ var cached_top = await Horizon.leaderboard.getTop(10, true) # uses cache
131
+ var fresh_top = await Horizon.leaderboard.getTop(10, false) # forces refresh
132
+
133
+ # Clear cache manually
134
+ Horizon.leaderboard.clearCache()
135
+
136
+ # Listen for events
137
+ Horizon.leaderboard.score_submitted.connect(func(score): print("Submitted: %d" % score))
138
+ Horizon.leaderboard.top_entries_loaded.connect(func(entries): print("Loaded %d entries" % entries.size()))
139
+ ```
140
+
141
+ ### Unity (C#)
142
+
143
+ ```csharp
144
+ using PM.horizOn.Cloud.Manager;
145
+
146
+ // Submit score
147
+ await LeaderboardManager.Instance.SubmitScore(12500);
148
+
149
+ // Get top 10 players
150
+ var top = await LeaderboardManager.Instance.GetTop(10);
151
+ foreach (var entry in top)
152
+ {
153
+ Debug.Log($"#{entry.position} {entry.username}: {entry.score}");
154
+ }
155
+
156
+ // Get your rank
157
+ var rank = await LeaderboardManager.Instance.GetRank();
158
+ Debug.Log($"My rank: #{rank.position} (Score: {rank.score})");
159
+
160
+ // Get entries around user
161
+ var around = await LeaderboardManager.Instance.GetAround(5);
162
+
163
+ // Clear cache
164
+ LeaderboardManager.Instance.ClearCache();
165
+ ```
166
+
167
+ ### REST (cURL)
168
+
169
+ ```bash
170
+ # Submit score
171
+ curl -X POST https://horizon.pm/api/v1/app/leaderboard/submit \
172
+ -H "X-API-Key: YOUR_API_KEY" \
173
+ -H "Content-Type: application/json" \
174
+ -d '{"userId": "user123", "score": 12500}'
175
+
176
+ # Get top 10
177
+ curl "https://horizon.pm/api/v1/app/leaderboard/top?userId=user123&limit=10" \
178
+ -H "X-API-Key: YOUR_API_KEY"
179
+
180
+ # Get rank
181
+ curl "https://horizon.pm/api/v1/app/leaderboard/rank?userId=user123" \
182
+ -H "X-API-Key: YOUR_API_KEY"
183
+
184
+ # Get around
185
+ curl "https://horizon.pm/api/v1/app/leaderboard/around?userId=user123&range=5" \
186
+ -H "X-API-Key: YOUR_API_KEY"
187
+ ```
188
+
189
+ ## Best Practices
190
+
191
+ - **Only submit on new high score** — The server already enforces "highest wins," but avoiding unnecessary requests saves your rate limit quota.
192
+ - **Cache leaderboard data** — Both SDKs cache by default. Use cached data for display and refresh periodically.
193
+ - **Limit query size** — Request only as many entries as you display (e.g., top 10, not top 100).
194
+
195
+ ## Common Errors
196
+
197
+ | Status | Cause | Solution |
198
+ |--------|-------|----------|
199
+ | 401 | Invalid API key | Check `X-API-Key` header |
200
+ | 404 | User not found on leaderboard | User may not have submitted a score yet |
201
+ | 429 | Rate limit exceeded | Cache data and reduce API calls |
@@ -0,0 +1,114 @@
1
+ # News
2
+
3
+ ## Overview
4
+
5
+ The News feature lets you display in-game announcements, patch notes, and updates to your players. News entries are created in the horizOn Dashboard and fetched by the app at runtime. Entries can be filtered by language.
6
+
7
+ ## Endpoints
8
+
9
+ ### Get News
10
+
11
+ **`GET /api/v1/app/news?limit={limit}&languageCode={languageCode}`**
12
+
13
+ Returns a list of published news entries, ordered by release date (newest first).
14
+
15
+ **Query Parameters:**
16
+
17
+ | Param | Type | Required | Description |
18
+ |-------|------|----------|-------------|
19
+ | `limit` | number | No | Max entries to return (default 20, max 100) |
20
+ | `languageCode` | string | No | Filter by language (e.g., `en`, `de`, `fr`) |
21
+
22
+ **Response (200):**
23
+
24
+ ```json
25
+ [
26
+ {
27
+ "id": "news-001",
28
+ "title": "Version 2.0 Released!",
29
+ "message": "We've added new features including cloud saves and leaderboards.",
30
+ "releaseDate": "2025-01-15T12:00:00Z",
31
+ "languageCode": "en"
32
+ },
33
+ {
34
+ "id": "news-002",
35
+ "title": "Holiday Event",
36
+ "message": "Earn double XP during the holiday season!",
37
+ "releaseDate": "2025-01-10T08:00:00Z",
38
+ "languageCode": "en"
39
+ }
40
+ ]
41
+ ```
42
+
43
+ ## Code Examples
44
+
45
+ ### Godot (GDScript)
46
+
47
+ ```gdscript
48
+ # Load latest 20 news entries
49
+ var news: Array[HorizonNewsEntry] = await Horizon.news.loadNews()
50
+
51
+ # Load with filters
52
+ var english_news = await Horizon.news.loadNews(10, "en")
53
+
54
+ # Iterate over entries
55
+ for entry in news:
56
+ print("%s: %s" % [entry.title, entry.message])
57
+ print(" Date: %s | Language: %s" % [entry.releaseDate, entry.languageCode])
58
+
59
+ # Caching (enabled by default)
60
+ var cached = await Horizon.news.loadNews(20, "", true) # uses cache
61
+ var fresh = await Horizon.news.loadNews(20, "", false) # forces refresh
62
+
63
+ # Get cached news without network request
64
+ var cached_entries = Horizon.news.getCachedNews()
65
+
66
+ # Clear cache
67
+ Horizon.news.clearCache()
68
+
69
+ # Listen for events
70
+ Horizon.news.news_loaded.connect(func(entries): print("Loaded %d entries" % entries.size()))
71
+ ```
72
+
73
+ ### Unity (C#)
74
+
75
+ ```csharp
76
+ using PM.horizOn.Cloud.Manager;
77
+
78
+ // Load news
79
+ var news = await NewsManager.Instance.LoadNews(limit: 10);
80
+ foreach (var item in news)
81
+ {
82
+ Debug.Log($"{item.title}: {item.message}");
83
+ }
84
+
85
+ // Filter by language
86
+ var germanNews = await NewsManager.Instance.LoadNews(limit: 10, languageCode: "de");
87
+
88
+ // Get specific entry from cache
89
+ var entry = NewsManager.Instance.GetNewsById("news-001");
90
+
91
+ // Clear cache
92
+ NewsManager.Instance.ClearCache();
93
+ ```
94
+
95
+ ### REST (cURL)
96
+
97
+ ```bash
98
+ # Get latest 10 news entries in English
99
+ curl "https://horizon.pm/api/v1/app/news?limit=10&languageCode=en" \
100
+ -H "X-API-Key: YOUR_API_KEY"
101
+ ```
102
+
103
+ ## Best Practices
104
+
105
+ - **Fetch news at startup** — Load news once when the app starts and display in a news panel.
106
+ - **Use language filtering** — Show news in the user's preferred language.
107
+ - **Cache results** — News does not change frequently; cache for the session duration.
108
+
109
+ ## Common Errors
110
+
111
+ | Status | Cause | Solution |
112
+ |--------|-------|----------|
113
+ | 401 | Invalid API key | Check `X-API-Key` header |
114
+ | 429 | Rate limit exceeded | Cache news data, fetch once at startup |
@@ -0,0 +1,80 @@
1
+ # horizOn Overview
2
+
3
+ ## What is horizOn?
4
+
5
+ horizOn is a **multi-tenant Backend-as-a-Service (BaaS)** designed for game and app developers. It provides ready-to-use backend features so developers can focus on building their games instead of managing servers.
6
+
7
+ **Base URL:** `https://horizon.pm`
8
+
9
+ ## Key Concept: Account vs User
10
+
11
+ This distinction is critical when working with horizOn:
12
+
13
+ - **Account** — The developer or company using horizOn. An account owns one or more apps, manages API keys, and configures features via the horizOn Dashboard.
14
+ - **User** — The end-user of the developer's app (a player). Users sign up, sign in, save data, submit scores, etc.
15
+
16
+ The App API (used by SDKs and this MCP server) operates on behalf of **Users** within a developer's **Account**. The `X-API-Key` header identifies which Account/App the request belongs to.
17
+
18
+ ## Core Features
19
+
20
+ horizOn provides 8 core features:
21
+
22
+ | # | Feature | Description |
23
+ |---|---------|-------------|
24
+ | 1 | **Authentication** | Anonymous, email/password, and Google OAuth sign-in/sign-up |
25
+ | 2 | **Cloud Save** | Persist player data across devices (JSON or binary) |
26
+ | 3 | **Leaderboards** | Global rankings with score submission and queries |
27
+ | 4 | **Remote Config** | Server-side key-value configuration (feature flags, balancing) |
28
+ | 5 | **News** | In-game news and announcements with language filtering |
29
+ | 6 | **Gift Codes** | Promotional code validation and redemption |
30
+ | 7 | **User Feedback** | Bug reports, feature requests, and general feedback |
31
+ | 8 | **User Logs** | Server-side event and error tracking per user |
32
+
33
+ ## Tier System
34
+
35
+ horizOn offers four pricing tiers with different limits:
36
+
37
+ | Feature | FREE | BASIC | PRO | ENTERPRISE |
38
+ |---------|------|-------|-----|------------|
39
+ | **Cloud Save Size** | 1 KB | 5 KB | 20 KB | 250 KB |
40
+ | **User Logs** | Not available | Available | Available | Available |
41
+ | **Rate Limit** | 10 req/min | 10 req/min | 10 req/min | 10 req/min |
42
+ | **All other features** | Available | Available | Available | Available |
43
+
44
+ **Rate Limit:** All tiers are limited to **10 requests per minute per client**. Plan API calls carefully and use caching.
45
+
46
+ ## API Structure
47
+
48
+ All App API endpoints follow the pattern:
49
+
50
+ ```
51
+ {METHOD} /api/v1/app/{feature}/{action}
52
+ ```
53
+
54
+ Every request requires the `X-API-Key` header:
55
+
56
+ ```
57
+ X-API-Key: your-api-key-here
58
+ Content-Type: application/json
59
+ ```
60
+
61
+ ## SDKs
62
+
63
+ | Engine | SDK | Language |
64
+ |--------|-----|----------|
65
+ | **Godot** | [horizOn SDK for Godot](https://github.com/ProjectMakersDE/horizOn-SDK-Godot) | GDScript |
66
+ | **Unity** | [horizOn Cloud SDK for Unity](https://github.com/ProjectMakersDE/horizOn-SDK-Unity) | C# |
67
+ | **Unreal Engine** | No official SDK | Use REST API directly (C++ FHttpModule or VaRest plugin) |
68
+
69
+ ## Common HTTP Status Codes
70
+
71
+ | Code | Meaning | Action |
72
+ |------|---------|--------|
73
+ | 200 | Success | Request completed successfully |
74
+ | 400 | Bad Request | Check request parameters |
75
+ | 401 | Unauthorized | Invalid or missing API key |
76
+ | 403 | Forbidden | Feature not available for tier, or user not verified |
77
+ | 404 | Not Found | Resource does not exist |
78
+ | 409 | Conflict | User already exists (signup) |
79
+ | 429 | Too Many Requests | Rate limit exceeded, wait and retry |
80
+ | 500 | Server Error | Internal server error, retry later |
@@ -0,0 +1,149 @@
1
+ # Remote Config
2
+
3
+ ## Overview
4
+
5
+ Remote Config lets you store key-value pairs on the server that your app can fetch at runtime. This is useful for:
6
+
7
+ - **Feature flags** — Enable/disable features without an app update
8
+ - **Game balance** — Tweak difficulty, rewards, or economy values
9
+ - **A/B testing** — Serve different values to different users
10
+ - **Maintenance mode** — Toggle server maintenance messages
11
+ - **Version gating** — Enforce minimum app versions
12
+
13
+ Config values are set in the horizOn Dashboard and are read-only from the app side.
14
+
15
+ ## Endpoints
16
+
17
+ ### Get Single Config
18
+
19
+ **`GET /api/v1/app/remote-config/{configKey}`**
20
+
21
+ Retrieves a single configuration value by its key.
22
+
23
+ **Path Parameters:**
24
+
25
+ | Param | Type | Required | Description |
26
+ |-------|------|----------|-------------|
27
+ | `configKey` | string | Yes | The configuration key |
28
+
29
+ **Response (200):**
30
+
31
+ ```json
32
+ {
33
+ "configKey": "max_level",
34
+ "configValue": "50",
35
+ "found": true
36
+ }
37
+ ```
38
+
39
+ If the key does not exist:
40
+
41
+ ```json
42
+ {
43
+ "configKey": "unknown_key",
44
+ "configValue": null,
45
+ "found": false
46
+ }
47
+ ```
48
+
49
+ ---
50
+
51
+ ### Get All Configs
52
+
53
+ **`GET /api/v1/app/remote-config/all`**
54
+
55
+ Retrieves all configuration values for the app.
56
+
57
+ **Response (200):**
58
+
59
+ ```json
60
+ {
61
+ "configs": {
62
+ "max_level": "50",
63
+ "difficulty": "1.5",
64
+ "maintenance_mode": "false",
65
+ "welcome_message": "Hello, Player!"
66
+ },
67
+ "total": 4
68
+ }
69
+ ```
70
+
71
+ ## Code Examples
72
+
73
+ ### Godot (GDScript)
74
+
75
+ ```gdscript
76
+ # Get a single config value
77
+ var version: String = await Horizon.remoteConfig.getConfig("game_version")
78
+
79
+ # Typed getters with defaults
80
+ var maxLevel: int = await Horizon.remoteConfig.getInt("max_level", 100)
81
+ var difficulty: float = await Horizon.remoteConfig.getFloat("difficulty", 1.0)
82
+ var maintenance: bool = await Horizon.remoteConfig.getBool("maintenance_mode", false)
83
+
84
+ # Parse JSON config values
85
+ var jsonData = await Horizon.remoteConfig.getJson("event_config")
86
+
87
+ # Get all configs at once (recommended at startup)
88
+ var all: Dictionary = await Horizon.remoteConfig.getAllConfigs()
89
+
90
+ # Caching (enabled by default)
91
+ var cached = await Horizon.remoteConfig.getConfig("key", true) # uses cache
92
+ var fresh = await Horizon.remoteConfig.getConfig("key", false) # forces refresh
93
+
94
+ # Check if a key exists
95
+ var exists: bool = await Horizon.remoteConfig.hasKey("some_key")
96
+
97
+ # Clear cache
98
+ Horizon.remoteConfig.clearCache()
99
+
100
+ # Listen for events
101
+ Horizon.remoteConfig.config_loaded.connect(func(key, value): print("%s = %s" % [key, value]))
102
+ Horizon.remoteConfig.all_configs_loaded.connect(func(configs): print("Loaded %d configs" % configs.size()))
103
+ ```
104
+
105
+ ### Unity (C#)
106
+
107
+ ```csharp
108
+ using PM.horizOn.Cloud.Manager;
109
+
110
+ // Get a single config
111
+ string value = await RemoteConfigManager.Instance.GetConfig("game_version");
112
+
113
+ // Type-safe getters with defaults
114
+ int maxLives = await RemoteConfigManager.Instance.GetInt("max_lives", 3);
115
+ float difficulty = await RemoteConfigManager.Instance.GetFloat("difficulty", 1.0f);
116
+ bool eventActive = await RemoteConfigManager.Instance.GetBool("holiday_event", false);
117
+
118
+ // Get all configs at once (recommended at startup)
119
+ var configs = await RemoteConfigManager.Instance.GetAllConfigs();
120
+
121
+ // Clear cache
122
+ RemoteConfigManager.Instance.ClearCache();
123
+ ```
124
+
125
+ ### REST (cURL)
126
+
127
+ ```bash
128
+ # Get single config
129
+ curl "https://horizon.pm/api/v1/app/remote-config/max_level" \
130
+ -H "X-API-Key: YOUR_API_KEY"
131
+
132
+ # Get all configs
133
+ curl "https://horizon.pm/api/v1/app/remote-config/all" \
134
+ -H "X-API-Key: YOUR_API_KEY"
135
+ ```
136
+
137
+ ## Best Practices
138
+
139
+ - **Fetch all configs at startup** — Use `getAllConfigs()` once on app launch and rely on the cache.
140
+ - **Provide sensible defaults** — Always pass default values in typed getters in case the config key is missing.
141
+ - **Cache aggressively** — Config values rarely change during a session.
142
+ - **Avoid frequent polling** — Config changes take effect when users restart the app or you explicitly refresh.
143
+
144
+ ## Common Errors
145
+
146
+ | Status | Cause | Solution |
147
+ |--------|-------|----------|
148
+ | 401 | Invalid API key | Check `X-API-Key` header |
149
+ | 429 | Rate limit exceeded | Cache config values, fetch once at startup |