horizon-mcp 1.5.0 → 1.5.2
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.
- package/LICENSE +21 -0
- package/README.md +20 -6
- package/dist/prompts/integrate-feature.js +1 -1
- package/dist/prompts/integrate-feature.js.map +1 -1
- package/dist/resources/index.js +1 -1
- package/dist/resources/index.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/__tests__/admin-news.test.d.ts +2 -0
- package/dist/tools/__tests__/admin-news.test.d.ts.map +1 -0
- package/dist/tools/__tests__/admin-news.test.js +64 -0
- package/dist/tools/__tests__/admin-news.test.js.map +1 -0
- package/dist/tools/admin/news.d.ts.map +1 -1
- package/dist/tools/admin/news.js +17 -2
- package/dist/tools/admin/news.js.map +1 -1
- package/package.json +2 -2
- package/src/resources/docs/overview.md +1 -1
- package/src/resources/quickstart/unreal.md +189 -360
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ProjectMakers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Add to your MCP client configuration (Claude Desktop, Cursor, etc.):
|
|
|
28
28
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
31
|
-
### Resources (
|
|
31
|
+
### Resources (16 docs)
|
|
32
32
|
|
|
33
33
|
Documentation resources are served directly from the MCP server. No API key required.
|
|
34
34
|
|
|
@@ -39,16 +39,19 @@ Documentation resources are served directly from the MCP server. No API key requ
|
|
|
39
39
|
| `horizon://docs/leaderboard` | Leaderboard score submission, top entries, user rank, entries around user, with SDK examples |
|
|
40
40
|
| `horizon://docs/cloud-save` | Cloud save/load for JSON and binary data, tier size limits, SDK examples |
|
|
41
41
|
| `horizon://docs/remote-config` | Server-side key-value configuration: feature flags, game balance, A/B testing. SDK examples |
|
|
42
|
+
| `horizon://docs/localization` | Server-side localized strings across 15 languages: per-key translations, single/all fetch, available languages. SDK examples |
|
|
42
43
|
| `horizon://docs/news` | In-game news and announcements with language filtering. SDK examples |
|
|
43
44
|
| `horizon://docs/gift-codes` | Gift code validation and redemption for promotional rewards. SDK examples |
|
|
44
45
|
| `horizon://docs/feedback` | Bug reports, feature requests, and general feedback submission. SDK examples |
|
|
45
46
|
| `horizon://docs/user-logs` | Server-side event and error tracking. Requires BASIC tier or higher. SDK examples |
|
|
47
|
+
| `horizon://docs/crash-reporting` | Crash report submission, session tracking, fingerprinting, breadcrumbs, and auto-regression detection. SDK examples |
|
|
48
|
+
| `horizon://docs/email-sending` | Transactional and event-based email delivery to registered users. Templates, scheduling, status tracking, and SMTP integration. SDK examples |
|
|
46
49
|
| `horizon://api/reference` | Complete API reference for all horizOn App API endpoints with request/response schemas |
|
|
47
50
|
| `horizon://quickstart/godot` | Step-by-step guide to integrate horizOn in Godot with GDScript examples |
|
|
48
51
|
| `horizon://quickstart/unity` | Step-by-step guide to integrate horizOn in Unity with C# examples |
|
|
49
|
-
| `horizon://quickstart/unreal` |
|
|
52
|
+
| `horizon://quickstart/unreal` | Step-by-step guide to integrate horizOn in Unreal Engine 5.5+ with the official horizOn SDK plugin. C++ and Blueprint examples |
|
|
50
53
|
|
|
51
|
-
### Tools (
|
|
54
|
+
### Tools (28 tools)
|
|
52
55
|
|
|
53
56
|
Live API tools that call the horizOn backend. Requires a valid API key.
|
|
54
57
|
|
|
@@ -69,11 +72,19 @@ Live API tools that call the horizOn backend. Requires a valid API key.
|
|
|
69
72
|
| `horizon_load_cloud_data` | Load cloud save data for a user |
|
|
70
73
|
| `horizon_get_remote_config` | Get a single remote config value by key |
|
|
71
74
|
| `horizon_get_all_remote_configs` | Get all remote config values |
|
|
75
|
+
| `horizon_get_localization` | Get a single localized string by key, optionally for a specific language |
|
|
76
|
+
| `horizon_get_all_localizations` | Get all localized strings, optionally for a specific language |
|
|
77
|
+
| `horizon_get_localization_languages` | List the languages that have localizations for the app |
|
|
72
78
|
| `horizon_get_news` | Get news articles with optional language filtering |
|
|
73
79
|
| `horizon_validate_gift_code` | Validate a gift code without redeeming it |
|
|
74
80
|
| `horizon_redeem_gift_code` | Redeem a gift code for a user |
|
|
75
81
|
| `horizon_submit_feedback` | Submit user feedback (bug reports, feature requests) |
|
|
76
82
|
| `horizon_create_log` | Create a server-side log entry (INFO, WARN, ERROR) |
|
|
83
|
+
| `horizon_create_crash_report` | Submit a crash report (grouped by fingerprint, with regression detection) |
|
|
84
|
+
| `horizon_create_crash_session` | Register a game session for the crash-free rate |
|
|
85
|
+
| `horizon_send_email` | Send a transactional email to a registered user from a template |
|
|
86
|
+
| `horizon_cancel_email` | Cancel a pending or scheduled email |
|
|
87
|
+
| `horizon_get_email_status` | Get the status of a sent or scheduled email |
|
|
77
88
|
|
|
78
89
|
### Prompts (4 prompts)
|
|
79
90
|
|
|
@@ -159,14 +170,17 @@ horizOn is a multi-tenant Backend-as-a-Service platform built for game and app d
|
|
|
159
170
|
|
|
160
171
|
Core features:
|
|
161
172
|
|
|
162
|
-
- Authentication (Anonymous, Email, Google)
|
|
173
|
+
- Authentication (Anonymous, Email, Google, Apple)
|
|
163
174
|
- Leaderboards
|
|
164
175
|
- Cloud Save
|
|
165
176
|
- Remote Config
|
|
177
|
+
- Localization
|
|
166
178
|
- News and Announcements
|
|
167
179
|
- Gift Codes
|
|
168
180
|
- User Feedback
|
|
169
181
|
- User Logs
|
|
182
|
+
- Crash Reporting
|
|
183
|
+
- Email Sending
|
|
170
184
|
|
|
171
185
|
Learn more at [horizon.pm](https://horizon.pm). Install this MCP server via [npm](https://www.npmjs.com/package/horizon-mcp).
|
|
172
186
|
|
|
@@ -174,13 +188,13 @@ Learn more at [horizon.pm](https://horizon.pm). Install this MCP server via [npm
|
|
|
174
188
|
|
|
175
189
|
- **Godot 4.5+** -- GDScript SDK
|
|
176
190
|
- **Unity 6** -- C# SDK
|
|
177
|
-
- **Unreal Engine
|
|
191
|
+
- **Unreal Engine 5.5+** -- C++ and Blueprint SDK
|
|
178
192
|
|
|
179
193
|
## Development
|
|
180
194
|
|
|
181
195
|
```bash
|
|
182
196
|
# Clone the repository
|
|
183
|
-
git clone https://github.com/
|
|
197
|
+
git clone https://github.com/ProjectMakersDE/horizOn-mcp.git
|
|
184
198
|
cd horizOn-mcp
|
|
185
199
|
|
|
186
200
|
# Install dependencies
|
|
@@ -27,7 +27,7 @@ export function registerIntegrateFeaturePrompt(server) {
|
|
|
27
27
|
role: "user",
|
|
28
28
|
content: {
|
|
29
29
|
type: "text",
|
|
30
|
-
text: `I want to integrate the horizOn "${feature}" feature into my ${engine} project.\n\nPlease:\n1. Read the horizOn documentation for this feature (resource: horizon://docs/${feature})\n2. Read the ${engine} quickstart guide (resource: horizon://quickstart/${engine})\n3. Generate complete, production-ready integration code\n4. Include error handling and best practices\n5. Show how to test it works using the horizOn MCP tools\n\nUse the horizOn SDK for ${engine === "godot" ? "Godot (GDScript)" : engine === "unity" ? "Unity (C#)" : "Unreal Engine (
|
|
30
|
+
text: `I want to integrate the horizOn "${feature}" feature into my ${engine} project.\n\nPlease:\n1. Read the horizOn documentation for this feature (resource: horizon://docs/${feature})\n2. Read the ${engine} quickstart guide (resource: horizon://quickstart/${engine})\n3. Generate complete, production-ready integration code\n4. Include error handling and best practices\n5. Show how to test it works using the horizOn MCP tools\n\nUse the horizOn SDK for ${engine === "godot" ? "Godot (GDScript)" : engine === "unity" ? "Unity (C#)" : "Unreal Engine (C++ and Blueprints)"}`,
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrate-feature.js","sourceRoot":"","sources":["../../src/prompts/integrate-feature.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,MAAM,UAAU,8BAA8B,CAAC,MAAiB;IAC9D,MAAM,CAAC,cAAc,CAAC,mBAAmB,EAAE;QACzC,KAAK,EAAE,2BAA2B;QAClC,WAAW,EACT,+EAA+E;QACjF,UAAU,EAAE;YACV,OAAO,EAAE,CAAC;iBACP,IAAI,CAAC;gBACJ,MAAM;gBACN,aAAa;gBACb,YAAY;gBACZ,eAAe;gBACf,MAAM;gBACN,YAAY;gBACZ,UAAU;gBACV,WAAW;gBACX,iBAAiB;aAClB,CAAC;iBACD,QAAQ,CAAC,kCAAkC,CAAC;YAC/C,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;iBAClC,QAAQ,CAAC,iBAAiB,CAAC;SAC/B;KACF,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3B,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,oCAAoC,OAAO,qBAAqB,MAAM,sGAAsG,OAAO,kBAAkB,MAAM,qDAAqD,MAAM,iMAAiM,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"integrate-feature.js","sourceRoot":"","sources":["../../src/prompts/integrate-feature.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B,MAAM,UAAU,8BAA8B,CAAC,MAAiB;IAC9D,MAAM,CAAC,cAAc,CAAC,mBAAmB,EAAE;QACzC,KAAK,EAAE,2BAA2B;QAClC,WAAW,EACT,+EAA+E;QACjF,UAAU,EAAE;YACV,OAAO,EAAE,CAAC;iBACP,IAAI,CAAC;gBACJ,MAAM;gBACN,aAAa;gBACb,YAAY;gBACZ,eAAe;gBACf,MAAM;gBACN,YAAY;gBACZ,UAAU;gBACV,WAAW;gBACX,iBAAiB;aAClB,CAAC;iBACD,QAAQ,CAAC,kCAAkC,CAAC;YAC/C,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;iBAClC,QAAQ,CAAC,iBAAiB,CAAC;SAC/B;KACF,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3B,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,oCAAoC,OAAO,qBAAqB,MAAM,sGAAsG,OAAO,kBAAkB,MAAM,qDAAqD,MAAM,iMAAiM,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,oCAAoC,EAAE;iBAClkB;aACF;SACF;KACF,CAAC,CAAC,CAAC;AACN,CAAC"}
|
package/dist/resources/index.js
CHANGED
|
@@ -235,7 +235,7 @@ export function registerAllResources(server) {
|
|
|
235
235
|
// Quickstart: Unreal
|
|
236
236
|
server.registerResource("quickstart-unreal", "horizon://quickstart/unreal", {
|
|
237
237
|
title: "Unreal Engine Quickstart",
|
|
238
|
-
description: "
|
|
238
|
+
description: "Step-by-step guide to integrate horizOn in Unreal Engine 5.5+ with the official horizOn SDK plugin. C++ and Blueprint examples.",
|
|
239
239
|
mimeType: "text/markdown",
|
|
240
240
|
}, () => ({
|
|
241
241
|
contents: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMpE,wBAAgB,YAAY,IAAI,SAAS,CAWxC"}
|
package/dist/server.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
import { registerAllResources } from "./resources/index.js";
|
|
3
4
|
import { registerAllTools } from "./tools/index.js";
|
|
4
5
|
import { registerAllPrompts } from "./prompts/index.js";
|
|
5
6
|
export function createServer() {
|
|
6
7
|
const server = new McpServer({
|
|
7
8
|
name: "horizon-mcp",
|
|
8
|
-
version: "
|
|
9
|
+
version: JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version,
|
|
9
10
|
});
|
|
10
11
|
registerAllResources(server);
|
|
11
12
|
registerAllTools(server);
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO;KAC/F,CAAC,CAAC;IAEH,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE3B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-news.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/admin-news.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
4
|
+
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
5
|
+
import { registerAdminNewsTools } from "../admin/news.js";
|
|
6
|
+
// Contract checked against horizOn-Server NewsService.applyPublicationState and
|
|
7
|
+
// CreateNewsRequest/UpdateNewsRequest on 2026-09-23. Never call a live API here.
|
|
8
|
+
describe("admin news scheduling over MCP", () => {
|
|
9
|
+
let server;
|
|
10
|
+
let client;
|
|
11
|
+
let request;
|
|
12
|
+
const id = "11111111-1111-4111-8111-111111111111";
|
|
13
|
+
const input = {
|
|
14
|
+
projectApiKeyId: id, titles: { de: "Neu", en: "New" },
|
|
15
|
+
messages: { de: "Nachricht", en: "Message" },
|
|
16
|
+
releaseDate: "2026-09-24T12:30:00",
|
|
17
|
+
};
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
vi.stubEnv("HORIZON_ACCOUNT_API_KEY", "test-account-key");
|
|
20
|
+
vi.stubEnv("HORIZON_BASE_URL", "https://news.test.invalid");
|
|
21
|
+
request = vi.fn().mockResolvedValue(new Response(JSON.stringify({ id })));
|
|
22
|
+
vi.stubGlobal("fetch", request);
|
|
23
|
+
server = new McpServer({ name: "test", version: "1.0.0" });
|
|
24
|
+
registerAdminNewsTools(server);
|
|
25
|
+
client = new Client({ name: "test", version: "1.0.0" });
|
|
26
|
+
const [a, b] = InMemoryTransport.createLinkedPair();
|
|
27
|
+
await Promise.all([server.connect(a), client.connect(b)]);
|
|
28
|
+
});
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
await client.close();
|
|
31
|
+
await server.close();
|
|
32
|
+
vi.unstubAllEnvs();
|
|
33
|
+
vi.unstubAllGlobals();
|
|
34
|
+
});
|
|
35
|
+
it("advertises scheduling for create and update", async () => {
|
|
36
|
+
const { tools } = await client.listTools();
|
|
37
|
+
for (const action of ["create", "update"]) {
|
|
38
|
+
expect(tools.find(t => t.name === `horizon_admin_news_${action}`)?.inputSchema.properties)
|
|
39
|
+
.toHaveProperty("isScheduled");
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
it.each([true, false])("forwards isScheduled=%s on create", async (isScheduled) => {
|
|
43
|
+
const result = await client.callTool({ name: "horizon_admin_news_create", arguments: { ...input, isPublished: false, isScheduled } });
|
|
44
|
+
expect(result.isError).not.toBe(true);
|
|
45
|
+
const [url, options] = request.mock.calls[0];
|
|
46
|
+
expect(url).toBe("https://news.test.invalid/api/v1/admin/news");
|
|
47
|
+
expect(options.headers["X-Account-API-Key"]).toBe("test-account-key");
|
|
48
|
+
expect(JSON.parse(options.body)).toEqual({ apiKeyId: id, titles: input.titles, messages: input.messages, releaseDate: input.releaseDate, isPublished: false, isScheduled });
|
|
49
|
+
});
|
|
50
|
+
it.each([true, false])("forwards isScheduled=%s on update", async (isScheduled) => {
|
|
51
|
+
await client.callTool({ name: "horizon_admin_news_update", arguments: { id, isScheduled, releaseDate: input.releaseDate } });
|
|
52
|
+
expect(JSON.parse(request.mock.calls[0][1].body)).toEqual({ isScheduled, releaseDate: input.releaseDate });
|
|
53
|
+
});
|
|
54
|
+
it("keeps omitted scheduling absent on partial update", async () => {
|
|
55
|
+
await client.callTool({ name: "horizon_admin_news_update", arguments: { id, titles: input.titles } });
|
|
56
|
+
expect(JSON.parse(request.mock.calls[0][1].body)).toEqual({ titles: input.titles });
|
|
57
|
+
});
|
|
58
|
+
it.each(["create", "update"])("rejects published plus scheduled for %s before HTTP", async (action) => {
|
|
59
|
+
const result = await client.callTool({ name: `horizon_admin_news_${action}`, arguments: { ...(action === "create" ? input : { id }), isPublished: true, isScheduled: true } });
|
|
60
|
+
expect(result.isError).toBe(true);
|
|
61
|
+
expect(request).not.toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=admin-news.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-news.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/admin-news.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"news.d.ts","sourceRoot":"","sources":["../../../src/tools/admin/news.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAepE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"news.d.ts","sourceRoot":"","sources":["../../../src/tools/admin/news.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAepE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4Q9D"}
|
package/dist/tools/admin/news.js
CHANGED
|
@@ -105,12 +105,19 @@ export function registerAdminNewsTools(server) {
|
|
|
105
105
|
.boolean()
|
|
106
106
|
.optional()
|
|
107
107
|
.describe("Whether to publish on creation (default false)"),
|
|
108
|
+
isScheduled: z
|
|
109
|
+
.boolean()
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Publish automatically when releaseDate is due (default false). Cannot be true with isPublished."),
|
|
108
112
|
},
|
|
109
|
-
}, async ({ projectApiKeyId, titles, messages, releaseDate, isPublished, }) => {
|
|
113
|
+
}, async ({ projectApiKeyId, titles, messages, releaseDate, isPublished, isScheduled, }) => {
|
|
110
114
|
const client = getAdminClient();
|
|
111
115
|
if (!client)
|
|
112
116
|
return noAdminClientResponse();
|
|
113
117
|
try {
|
|
118
|
+
if (isPublished && isScheduled) {
|
|
119
|
+
return errorResponse(new Error("isPublished and isScheduled cannot both be true"));
|
|
120
|
+
}
|
|
114
121
|
const body = {
|
|
115
122
|
apiKeyId: projectApiKeyId,
|
|
116
123
|
titles,
|
|
@@ -119,6 +126,8 @@ export function registerAdminNewsTools(server) {
|
|
|
119
126
|
};
|
|
120
127
|
if (isPublished !== undefined)
|
|
121
128
|
body.isPublished = isPublished;
|
|
129
|
+
if (isScheduled !== undefined)
|
|
130
|
+
body.isScheduled = isScheduled;
|
|
122
131
|
const result = await client.post("/api/v1/admin/news", body);
|
|
123
132
|
return jsonResponse(result);
|
|
124
133
|
}
|
|
@@ -139,13 +148,17 @@ export function registerAdminNewsTools(server) {
|
|
|
139
148
|
.optional()
|
|
140
149
|
.describe("New release date as ISO-8601 LocalDateTime"),
|
|
141
150
|
isPublished: z.boolean().optional(),
|
|
151
|
+
isScheduled: z.boolean().optional().describe("Enable or cancel scheduled publication. Cannot be true with isPublished."),
|
|
142
152
|
isActive: z.boolean().optional(),
|
|
143
153
|
},
|
|
144
|
-
}, async ({ id, titles, messages, releaseDate, isPublished, isActive }) => {
|
|
154
|
+
}, async ({ id, titles, messages, releaseDate, isPublished, isScheduled, isActive }) => {
|
|
145
155
|
const client = getAdminClient();
|
|
146
156
|
if (!client)
|
|
147
157
|
return noAdminClientResponse();
|
|
148
158
|
try {
|
|
159
|
+
if (isPublished && isScheduled) {
|
|
160
|
+
return errorResponse(new Error("isPublished and isScheduled cannot both be true"));
|
|
161
|
+
}
|
|
149
162
|
const body = {};
|
|
150
163
|
if (titles !== undefined)
|
|
151
164
|
body.titles = titles;
|
|
@@ -155,6 +168,8 @@ export function registerAdminNewsTools(server) {
|
|
|
155
168
|
body.releaseDate = releaseDate;
|
|
156
169
|
if (isPublished !== undefined)
|
|
157
170
|
body.isPublished = isPublished;
|
|
171
|
+
if (isScheduled !== undefined)
|
|
172
|
+
body.isScheduled = isScheduled;
|
|
158
173
|
if (isActive !== undefined)
|
|
159
174
|
body.isActive = isActive;
|
|
160
175
|
const result = await client.put(`/api/v1/admin/news/${id}`, body);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"news.js","sourceRoot":"","sources":["../../../src/tools/admin/news.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"news.js","sourceRoot":"","sources":["../../../src/tools/admin/news.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "horizon-mcp",
|
|
3
|
-
"version": "1.5.
|
|
4
|
-
"description": "MCP server for horizOn Backend-as-a-Service
|
|
3
|
+
"version": "1.5.2",
|
|
4
|
+
"description": "MCP server for horizOn Backend-as-a-Service: documentation, live API tools, and workflow prompts for Godot, Unity, and Unreal Engine integration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -64,7 +64,7 @@ Content-Type: application/json
|
|
|
64
64
|
|--------|-----|----------|
|
|
65
65
|
| **Godot** | [horizOn SDK for Godot](https://github.com/ProjectMakersDE/horizOn-SDK-Godot) | GDScript |
|
|
66
66
|
| **Unity** | [horizOn Cloud SDK for Unity](https://github.com/ProjectMakersDE/horizOn-SDK-Unity) | C# |
|
|
67
|
-
| **Unreal Engine** |
|
|
67
|
+
| **Unreal Engine** | [horizOn SDK for Unreal Engine](https://github.com/ProjectMakersDE/horizOn-SDK-Unreal) | C++ and Blueprints (Unreal Engine 5.5+) |
|
|
68
68
|
|
|
69
69
|
## Common HTTP Status Codes
|
|
70
70
|
|
|
@@ -2,432 +2,261 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
horizOn has an **official Unreal Engine SDK**: the horizOn SDK plugin ([ProjectMakersDE/horizOn-SDK-Unreal](https://github.com/ProjectMakersDE/horizOn-SDK-Unreal)). It covers authentication, leaderboards, cloud save, remote config, localization, news, gift codes, feedback, user logs, crash reporting and email sending.
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
7
|
+
- **C++:** all features are reached through `UHorizonSubsystem`, a Game Instance Subsystem that owns one manager per feature (`Auth`, `Leaderboard`, `CloudSave`, `RemoteConfig`, `Localization`, `News`, `GiftCodes`, `Feedback`, `UserLogs`, `Crashes`, `EmailSending`).
|
|
8
|
+
- **Blueprints:** every call is also available as an async node with **On Success** and **On Failure** pins.
|
|
9
9
|
|
|
10
10
|
## Requirements
|
|
11
11
|
|
|
12
|
-
- **Unreal Engine 5.
|
|
12
|
+
- **Unreal Engine 5.5** or later
|
|
13
13
|
- horizOn API key (get one at [horizon.pm](https://horizon.pm))
|
|
14
|
-
-
|
|
14
|
+
- Target platforms declared by the plugin: Win64, Mac, Linux, Android, iOS
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Step 1: Install the Plugin
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
1. Download `horizOn-SDK-vX.Y.Z.zip` from [GitHub Releases](https://github.com/ProjectMakersDE/horizOn-SDK-Unreal/releases)
|
|
19
|
+
2. Extract the `HorizonSDK/` folder into your project's `Plugins/` directory
|
|
20
|
+
3. Open the project in Unreal Editor
|
|
21
|
+
4. Go to **Edit > Plugins**, search for "horizOn SDK" and enable it
|
|
22
|
+
5. Restart the editor when prompted
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|---------|-------|
|
|
22
|
-
| Base URL | `https://horizon.pm` |
|
|
23
|
-
| API Key Header | `X-API-Key: YOUR_API_KEY` |
|
|
24
|
-
| Content Type | `Content-Type: application/json` |
|
|
25
|
-
| Rate Limit | 10 requests/minute per client |
|
|
24
|
+
To call the SDK from C++, add `"HorizonSDK"` to the dependency module names in your game module's `.Build.cs`.
|
|
26
25
|
|
|
27
|
-
##
|
|
26
|
+
## Step 2: Configure
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
Open **Project Settings > Plugins > horizOn SDK**:
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
| Option | Default | Description |
|
|
31
|
+
|--------|---------|-------------|
|
|
32
|
+
| API Key | (empty) | Your horizOn API key |
|
|
33
|
+
| Backend Hosts | (empty) | Backend URL(s), for example `https://horizon.pm`. One host connects directly, several hosts use ping-based selection. |
|
|
34
|
+
| Connection Timeout | 10 | HTTP request timeout in seconds |
|
|
35
|
+
| Max Retries | 3 | Retry count for failed requests |
|
|
36
|
+
| Retry Delay | 1.0 | Delay between retries in seconds |
|
|
37
|
+
| Log Level | Info | SDK log verbosity |
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
// MyHorizonManager.h
|
|
35
|
-
#pragma once
|
|
36
|
-
#include "CoreMinimal.h"
|
|
37
|
-
#include "VaRestSubsystem.h"
|
|
39
|
+
Alternatively, use **Tools > horizOn > Import Config...** to import the JSON config file from the horizOn dashboard. The importer reads `apiKey` plus either `backendDomains` (list) or `backendUrl` (single host). `ConnectToServer()` fails with "No hosts configured" if Backend Hosts is empty.
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
class UMyHorizonManager : public UObject
|
|
41
|
-
{
|
|
42
|
-
GENERATED_BODY()
|
|
41
|
+
## Step 3: Connect to Server
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
```cpp
|
|
44
|
+
// MyActor.h: connection handlers are bound to dynamic delegates, so declare them as UFUNCTION()
|
|
45
|
+
UFUNCTION()
|
|
46
|
+
void HandleConnected();
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
UFUNCTION()
|
|
49
|
+
void HandleConnectionFailed(const FString& ErrorMessage);
|
|
50
|
+
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
```cpp
|
|
53
|
+
// MyActor.cpp
|
|
54
|
+
#include "HorizonSubsystem.h"
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
void AMyActor::BeginPlay()
|
|
57
|
+
{
|
|
58
|
+
Super::BeginPlay();
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
UHorizonSubsystem* Horizon = GetGameInstance()->GetSubsystem<UHorizonSubsystem>();
|
|
61
|
+
Horizon->OnConnected.AddUniqueDynamic(this, &AMyActor::HandleConnected);
|
|
62
|
+
Horizon->OnConnectionFailed.AddUniqueDynamic(this, &AMyActor::HandleConnectionFailed);
|
|
63
|
+
Horizon->ConnectToServer();
|
|
64
|
+
}
|
|
65
|
+
```
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
Other connection calls: `Horizon->IsConnected()`, `Horizon->GetConnectionStatus()`, `Horizon->Disconnect()`.
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
## Step 4: Authenticate
|
|
64
70
|
|
|
65
71
|
```cpp
|
|
66
|
-
|
|
67
|
-
#pragma once
|
|
68
|
-
#include "CoreMinimal.h"
|
|
69
|
-
#include "Http.h"
|
|
70
|
-
#include "Json.h"
|
|
72
|
+
#include "Managers/HorizonAuthManager.h"
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
void AMyActor::HandleConnected()
|
|
73
75
|
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// POST request helper
|
|
79
|
-
static void Post(
|
|
80
|
-
const FString& Endpoint,
|
|
81
|
-
const TSharedRef<FJsonObject>& Body,
|
|
82
|
-
TFunction<void(bool bSuccess, TSharedPtr<FJsonObject> Response)> Callback)
|
|
83
|
-
{
|
|
84
|
-
FHttpModule& Http = FHttpModule::Get();
|
|
85
|
-
TSharedRef<IHttpRequest> Request = Http.CreateRequest();
|
|
86
|
-
|
|
87
|
-
Request->SetURL(BaseUrl + Endpoint);
|
|
88
|
-
Request->SetVerb(TEXT("POST"));
|
|
89
|
-
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
|
|
90
|
-
Request->SetHeader(TEXT("X-API-Key"), ApiKey);
|
|
91
|
-
|
|
92
|
-
FString BodyString;
|
|
93
|
-
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&BodyString);
|
|
94
|
-
FJsonSerializer::Serialize(Body, Writer);
|
|
95
|
-
Request->SetContentAsString(BodyString);
|
|
96
|
-
|
|
97
|
-
Request->OnProcessRequestComplete().BindLambda(
|
|
98
|
-
[Callback](FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bConnected)
|
|
99
|
-
{
|
|
100
|
-
if (bConnected && Resp.IsValid() && Resp->GetResponseCode() == 200)
|
|
101
|
-
{
|
|
102
|
-
TSharedPtr<FJsonObject> JsonResponse;
|
|
103
|
-
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Resp->GetContentAsString());
|
|
104
|
-
FJsonSerializer::Deserialize(Reader, JsonResponse);
|
|
105
|
-
Callback(true, JsonResponse);
|
|
106
|
-
}
|
|
107
|
-
else
|
|
108
|
-
{
|
|
109
|
-
Callback(false, nullptr);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
Request->ProcessRequest();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// GET request helper
|
|
117
|
-
static void Get(
|
|
118
|
-
const FString& Endpoint,
|
|
119
|
-
TFunction<void(bool bSuccess, TSharedPtr<FJsonObject> Response)> Callback)
|
|
76
|
+
UHorizonSubsystem* Horizon = GetGameInstance()->GetSubsystem<UHorizonSubsystem>();
|
|
77
|
+
|
|
78
|
+
// Anonymous sign-up (guest account)
|
|
79
|
+
Horizon->Auth->SignUpAnonymous(TEXT("Player1"), FOnAuthComplete::CreateLambda([Horizon](bool bSuccess)
|
|
120
80
|
{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Request->OnProcessRequestComplete().BindLambda(
|
|
129
|
-
[Callback](FHttpRequestPtr Req, FHttpResponsePtr Resp, bool bConnected)
|
|
130
|
-
{
|
|
131
|
-
if (bConnected && Resp.IsValid() && Resp->GetResponseCode() == 200)
|
|
132
|
-
{
|
|
133
|
-
TSharedPtr<FJsonObject> JsonResponse;
|
|
134
|
-
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Resp->GetContentAsString());
|
|
135
|
-
FJsonSerializer::Deserialize(Reader, JsonResponse);
|
|
136
|
-
Callback(true, JsonResponse);
|
|
137
|
-
}
|
|
138
|
-
else
|
|
139
|
-
{
|
|
140
|
-
Callback(false, nullptr);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
Request->ProcessRequest();
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// HorizonAPI.cpp
|
|
149
|
-
FString FHorizonAPI::BaseUrl = TEXT("https://horizon.pm");
|
|
150
|
-
FString FHorizonAPI::ApiKey = TEXT("");
|
|
81
|
+
if (bSuccess)
|
|
82
|
+
{
|
|
83
|
+
FHorizonUserData User = Horizon->Auth->GetCurrentUser();
|
|
84
|
+
UE_LOG(LogTemp, Log, TEXT("Signed in as %s (%s)"), *User.DisplayName, *User.UserId);
|
|
85
|
+
}
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
151
88
|
```
|
|
152
89
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
### Anonymous Signup
|
|
90
|
+
### Other Authentication Methods
|
|
156
91
|
|
|
157
92
|
```cpp
|
|
158
|
-
|
|
159
|
-
{
|
|
160
|
-
|
|
161
|
-
Body->SetStringField(TEXT("type"), TEXT("ANONYMOUS"));
|
|
162
|
-
Body->SetStringField(TEXT("username"), Username);
|
|
93
|
+
// Email sign-up and sign-in
|
|
94
|
+
Horizon->Auth->SignUpEmail(TEXT("user@example.com"), TEXT("password"), TEXT("Username"), FOnAuthComplete::CreateLambda([](bool bSuccess) { }));
|
|
95
|
+
Horizon->Auth->SignInEmail(TEXT("user@example.com"), TEXT("password"), FOnAuthComplete::CreateLambda([](bool bSuccess) { }));
|
|
163
96
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
Body->SetStringField(TEXT("anonymousToken"), Token);
|
|
97
|
+
// Restore the cached anonymous session on the next launch
|
|
98
|
+
Horizon->Auth->RestoreAnonymousSession(FOnAuthComplete::CreateLambda([](bool bSuccess) { }));
|
|
167
99
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
FString Name = Response->GetStringField(TEXT("username"));
|
|
175
|
-
UE_LOG(LogTemp, Log, TEXT("Signed up: %s (%s)"), *Name, *UserId);
|
|
176
|
-
}
|
|
177
|
-
else
|
|
178
|
-
{
|
|
179
|
-
UE_LOG(LogTemp, Error, TEXT("Signup failed"));
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
100
|
+
// Sign in with Apple (native sheet on iOS, system browser elsewhere)
|
|
101
|
+
Horizon->Auth->SignInWithApple(FOnAuthComplete::CreateLambda([](bool bSuccess) { }));
|
|
102
|
+
|
|
103
|
+
// Session state
|
|
104
|
+
bool bSignedIn = Horizon->Auth->IsSignedIn();
|
|
105
|
+
Horizon->Auth->SignOut();
|
|
183
106
|
```
|
|
184
107
|
|
|
185
|
-
|
|
108
|
+
Google sign-in is available through `SignUpGoogle(AuthCode, RedirectUri, Username, OnComplete)` and `SignInGoogle(AuthCode, RedirectUri, OnComplete)`.
|
|
109
|
+
|
|
110
|
+
## Step 5: Use Features
|
|
111
|
+
|
|
112
|
+
Each manager lives in `Managers/Horizon<Feature>Manager.h`. Include the header of the manager you call.
|
|
113
|
+
|
|
114
|
+
### Leaderboards
|
|
186
115
|
|
|
187
116
|
```cpp
|
|
188
|
-
|
|
189
|
-
{
|
|
190
|
-
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
191
|
-
Body->SetStringField(TEXT("type"), TEXT("EMAIL"));
|
|
192
|
-
Body->SetStringField(TEXT("email"), Email);
|
|
193
|
-
Body->SetStringField(TEXT("password"), Password);
|
|
117
|
+
// Submit a score (optional Metadata and BoardKey parameters follow the callback)
|
|
118
|
+
Horizon->Leaderboard->SubmitScore(12500, FOnRequestComplete::CreateLambda([](bool bSuccess, const FString& Error) { }));
|
|
194
119
|
|
|
195
|
-
|
|
196
|
-
|
|
120
|
+
// Top 10 entries (second argument: use the local cache)
|
|
121
|
+
Horizon->Leaderboard->GetTop(10, false, FOnLeaderboardEntriesComplete::CreateLambda(
|
|
122
|
+
[](bool bSuccess, const TArray<FHorizonLeaderboardEntry>& Entries)
|
|
123
|
+
{
|
|
124
|
+
for (const FHorizonLeaderboardEntry& Entry : Entries)
|
|
197
125
|
{
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
126
|
+
UE_LOG(LogTemp, Log, TEXT("#%d %s: %lld"), Entry.Position, *Entry.Username, Entry.Score);
|
|
127
|
+
}
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
// Own rank and the entries around it
|
|
131
|
+
Horizon->Leaderboard->GetRank(false, FOnLeaderboardRankComplete::CreateLambda([](bool bSuccess, const FHorizonLeaderboardEntry& Entry) { }));
|
|
132
|
+
Horizon->Leaderboard->GetAround(5, false, FOnLeaderboardEntriesComplete::CreateLambda([](bool bSuccess, const TArray<FHorizonLeaderboardEntry>& Entries) { }));
|
|
209
133
|
```
|
|
210
134
|
|
|
211
|
-
###
|
|
135
|
+
### Cloud Saves
|
|
212
136
|
|
|
213
137
|
```cpp
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
217
|
-
Body->SetStringField(TEXT("userId"), UserId);
|
|
218
|
-
Body->SetNumberField(TEXT("score"), Score);
|
|
138
|
+
Horizon->CloudSave->Save(TEXT("{\"level\": 5, \"coins\": 1000}"), FOnRequestComplete::CreateLambda([](bool bSuccess, const FString& Error) { }));
|
|
139
|
+
Horizon->CloudSave->Load(FOnStringComplete::CreateLambda([](bool bSuccess, const FString& Data) { }));
|
|
219
140
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (bSuccess)
|
|
224
|
-
{
|
|
225
|
-
UE_LOG(LogTemp, Log, TEXT("Score submitted: %lld"), Score);
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
141
|
+
// Binary data
|
|
142
|
+
Horizon->CloudSave->SaveBytes(Bytes, FOnRequestComplete::CreateLambda([](bool bSuccess, const FString& Error) { }));
|
|
143
|
+
Horizon->CloudSave->LoadBytes(FOnBinaryComplete::CreateLambda([](bool bSuccess, const TArray<uint8>& Data) { }));
|
|
229
144
|
```
|
|
230
145
|
|
|
231
|
-
###
|
|
146
|
+
### Remote Config
|
|
232
147
|
|
|
233
148
|
```cpp
|
|
234
|
-
|
|
235
|
-
{
|
|
236
|
-
|
|
237
|
-
TEXT("/api/v1/app/leaderboard/top?userId=%s&limit=%d"),
|
|
238
|
-
*UserId, Limit);
|
|
149
|
+
// Typed getters with defaults (Key, Default, bUseCache, callback)
|
|
150
|
+
Horizon->RemoteConfig->GetString(TEXT("welcome_message"), TEXT("Welcome!"), true, FOnConfigComplete::CreateLambda([](bool bSuccess, const FString& Value) { }));
|
|
151
|
+
Horizon->RemoteConfig->GetInt(TEXT("max_level"), 100, true, TDelegate<void(bool, int32)>::CreateLambda([](bool bSuccess, int32 Value) { }));
|
|
239
152
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
{
|
|
243
|
-
if (bSuccess && Response.IsValid())
|
|
244
|
-
{
|
|
245
|
-
const TArray<TSharedPtr<FJsonValue>>* Entries;
|
|
246
|
-
if (Response->TryGetArrayField(TEXT("entries"), Entries))
|
|
247
|
-
{
|
|
248
|
-
for (const auto& Entry : *Entries)
|
|
249
|
-
{
|
|
250
|
-
auto Obj = Entry->AsObject();
|
|
251
|
-
int32 Position = Obj->GetIntegerField(TEXT("position"));
|
|
252
|
-
FString Username = Obj->GetStringField(TEXT("username"));
|
|
253
|
-
int64 Score = Obj->GetIntegerField(TEXT("score"));
|
|
254
|
-
UE_LOG(LogTemp, Log, TEXT("#%d %s: %lld"), Position, *Username, Score);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
}
|
|
153
|
+
// All configs at once (recommended at startup)
|
|
154
|
+
Horizon->RemoteConfig->GetAllConfigs(true, FOnAllConfigsComplete::CreateLambda([](bool bSuccess, const TMap<FString, FString>& Configs) { }));
|
|
260
155
|
```
|
|
261
156
|
|
|
262
|
-
|
|
157
|
+
`GetFloat`, `GetBool`, `GetJson` and `HasKey` follow the same pattern.
|
|
158
|
+
|
|
159
|
+
### Localization
|
|
263
160
|
|
|
264
161
|
```cpp
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
268
|
-
Body->SetStringField(TEXT("userId"), UserId);
|
|
269
|
-
Body->SetStringField(TEXT("saveData"), SaveData);
|
|
162
|
+
// Active language: en, de, es, fr, it, pt, nl, pl, ru, ja, zh, ar, ko, tr, id
|
|
163
|
+
Horizon->Localization->SetLanguage(TEXT("de"));
|
|
270
164
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
bool Success = Response->GetBoolField(TEXT("success"));
|
|
277
|
-
int32 Size = Response->GetIntegerField(TEXT("dataSizeBytes"));
|
|
278
|
-
UE_LOG(LogTemp, Log, TEXT("Saved: %d bytes"), Size);
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
}
|
|
165
|
+
// Empty language uses the active language
|
|
166
|
+
Horizon->Localization->GetLocalization(TEXT("welcome_message"), TEXT(""), FOnLocalizationComplete::CreateLambda([](bool bSuccess, const FString& Value) { }));
|
|
167
|
+
Horizon->Localization->GetAllLocalizations(TEXT("de"), FOnAllLocalizationsComplete::CreateLambda([](bool bSuccess, const TMap<FString, FString>& Translations) { }));
|
|
168
|
+
Horizon->Localization->GetAvailableLanguages(FOnLanguagesComplete::CreateLambda([](bool bSuccess, const TArray<FString>& Languages) { }));
|
|
169
|
+
```
|
|
282
170
|
|
|
283
|
-
|
|
284
|
-
{
|
|
285
|
-
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
286
|
-
Body->SetStringField(TEXT("userId"), UserId);
|
|
171
|
+
### News
|
|
287
172
|
|
|
288
|
-
|
|
289
|
-
|
|
173
|
+
```cpp
|
|
174
|
+
// Limit, language code, bUseCache, callback
|
|
175
|
+
Horizon->News->LoadNews(20, TEXT("en"), true, FOnNewsComplete::CreateLambda(
|
|
176
|
+
[](bool bSuccess, const TArray<FHorizonNewsEntry>& Entries)
|
|
177
|
+
{
|
|
178
|
+
for (const FHorizonNewsEntry& Entry : Entries)
|
|
290
179
|
{
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (Found)
|
|
295
|
-
{
|
|
296
|
-
FString Data = Response->GetStringField(TEXT("saveData"));
|
|
297
|
-
UE_LOG(LogTemp, Log, TEXT("Loaded: %s"), *Data);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
}
|
|
180
|
+
UE_LOG(LogTemp, Log, TEXT("%s: %s"), *Entry.Title, *Entry.Message);
|
|
181
|
+
}
|
|
182
|
+
}));
|
|
302
183
|
```
|
|
303
184
|
|
|
304
|
-
###
|
|
185
|
+
### Gift Codes
|
|
305
186
|
|
|
306
187
|
```cpp
|
|
307
|
-
|
|
308
|
-
{
|
|
309
|
-
FHorizonAPI::Get(TEXT("/api/v1/app/remote-config/all"),
|
|
310
|
-
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
311
|
-
{
|
|
312
|
-
if (bSuccess && Response.IsValid())
|
|
313
|
-
{
|
|
314
|
-
const TSharedPtr<FJsonObject>* Configs;
|
|
315
|
-
if (Response->TryGetObjectField(TEXT("configs"), Configs))
|
|
316
|
-
{
|
|
317
|
-
for (const auto& Pair : (*Configs)->Values)
|
|
318
|
-
{
|
|
319
|
-
UE_LOG(LogTemp, Log, TEXT("Config: %s = %s"),
|
|
320
|
-
*Pair.Key, *Pair.Value->AsString());
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
}
|
|
188
|
+
Horizon->GiftCodes->Validate(TEXT("ABCD-1234"), FOnGiftCodeValidateComplete::CreateLambda([](bool bRequestSuccess, bool bValid) { }));
|
|
189
|
+
Horizon->GiftCodes->Redeem(TEXT("ABCD-1234"), FOnGiftCodeRedeemComplete::CreateLambda([](bool bSuccess, const FString& GiftData, const FString& Message) { }));
|
|
326
190
|
```
|
|
327
191
|
|
|
328
|
-
###
|
|
192
|
+
### Feedback
|
|
329
193
|
|
|
330
194
|
```cpp
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
{
|
|
335
|
-
FHorizonAPI::Get(FString::Printf(TEXT("/api/v1/app/localization/%s?lang=%s"), *Key, *Lang),
|
|
336
|
-
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
337
|
-
{
|
|
338
|
-
if (bSuccess && Response.IsValid() && Response->GetBoolField(TEXT("found")))
|
|
339
|
-
{
|
|
340
|
-
UE_LOG(LogTemp, Log, TEXT("Localization: %s"),
|
|
341
|
-
*Response->GetStringField(TEXT("value")));
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
}
|
|
195
|
+
Horizon->Feedback->ReportBug(TEXT("Crash on level 5"), TEXT("Game crashes when opening the inventory"), FOnRequestComplete::CreateLambda([](bool bSuccess, const FString& Error) { }));
|
|
196
|
+
Horizon->Feedback->RequestFeature(TEXT("Dark mode"), TEXT("Please add a dark mode option"), FOnRequestComplete::CreateLambda([](bool bSuccess, const FString& Error) { }));
|
|
197
|
+
```
|
|
345
198
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (bSuccess && Response.IsValid())
|
|
352
|
-
{
|
|
353
|
-
const TSharedPtr<FJsonObject>* Translations;
|
|
354
|
-
if (Response->TryGetObjectField(TEXT("translations"), Translations))
|
|
355
|
-
{
|
|
356
|
-
for (const auto& Pair : (*Translations)->Values)
|
|
357
|
-
{
|
|
358
|
-
UE_LOG(LogTemp, Log, TEXT("Localization: %s = %s"),
|
|
359
|
-
*Pair.Key, *Pair.Value->AsString());
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}
|
|
199
|
+
### User Logs
|
|
200
|
+
|
|
201
|
+
```cpp
|
|
202
|
+
// Requires BASIC tier or higher
|
|
203
|
+
Horizon->UserLogs->Info(TEXT("Tutorial completed"), FOnUserLogComplete::CreateLambda([](bool bSuccess, const FString& LogId, const FString& CreatedAt) { }));
|
|
365
204
|
```
|
|
366
205
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
-H "Content-Type: application/json" \
|
|
382
|
-
-d '{"userId": "user123", "score": 12500}'
|
|
383
|
-
|
|
384
|
-
# Get remote config
|
|
385
|
-
curl "https://horizon.pm/api/v1/app/remote-config/all" \
|
|
386
|
-
-H "X-API-Key: YOUR_API_KEY"
|
|
387
|
-
|
|
388
|
-
# Get all localizations (German)
|
|
389
|
-
curl "https://horizon.pm/api/v1/app/localization/all?lang=de" \
|
|
390
|
-
-H "X-API-Key: YOUR_API_KEY"
|
|
391
|
-
|
|
392
|
-
# Save cloud data
|
|
393
|
-
curl -X POST https://horizon.pm/api/v1/app/cloud-save/save \
|
|
394
|
-
-H "X-API-Key: YOUR_API_KEY" \
|
|
395
|
-
-H "Content-Type: application/json" \
|
|
396
|
-
-d '{"userId": "user123", "saveData": "{\"level\":5}"}'
|
|
397
|
-
|
|
398
|
-
# Load cloud data
|
|
399
|
-
curl -X POST https://horizon.pm/api/v1/app/cloud-save/load \
|
|
400
|
-
-H "X-API-Key: YOUR_API_KEY" \
|
|
401
|
-
-H "Content-Type: application/json" \
|
|
402
|
-
-d '{"userId": "user123"}'
|
|
403
|
-
|
|
404
|
-
# Get news
|
|
405
|
-
curl "https://horizon.pm/api/v1/app/news?limit=10&languageCode=en" \
|
|
406
|
-
-H "X-API-Key: YOUR_API_KEY"
|
|
407
|
-
|
|
408
|
-
# Redeem gift code
|
|
409
|
-
curl -X POST https://horizon.pm/api/v1/app/gift-codes/redeem \
|
|
410
|
-
-H "X-API-Key: YOUR_API_KEY" \
|
|
411
|
-
-H "Content-Type: application/json" \
|
|
412
|
-
-d '{"code": "ABCD-1234", "userId": "user123"}'
|
|
413
|
-
|
|
414
|
-
# Submit feedback
|
|
415
|
-
curl -X POST https://horizon.pm/api/v1/app/user-feedback/submit \
|
|
416
|
-
-H "X-API-Key: YOUR_API_KEY" \
|
|
417
|
-
-H "Content-Type: application/json" \
|
|
418
|
-
-d '{"title": "Bug Report", "message": "Description", "userId": "user123", "category": "BUG"}'
|
|
419
|
-
|
|
420
|
-
# Create user log
|
|
421
|
-
curl -X POST https://horizon.pm/api/v1/app/user-logs/create \
|
|
422
|
-
-H "X-API-Key: YOUR_API_KEY" \
|
|
423
|
-
-H "Content-Type: application/json" \
|
|
424
|
-
-d '{"message": "Level complete", "type": "INFO", "userId": "user123"}'
|
|
206
|
+
`Warn` and `Error` take the same arguments. An optional error code can follow the callback.
|
|
207
|
+
|
|
208
|
+
### Crash Reporting
|
|
209
|
+
|
|
210
|
+
```cpp
|
|
211
|
+
// Automatic capture of engine errors (call once at game start)
|
|
212
|
+
Horizon->Crashes->StartCapture();
|
|
213
|
+
|
|
214
|
+
// Context for later reports
|
|
215
|
+
Horizon->Crashes->RecordBreadcrumb(TEXT("navigation"), TEXT("Entered level 5"));
|
|
216
|
+
Horizon->Crashes->SetCustomKey(TEXT("level"), TEXT("5"));
|
|
217
|
+
|
|
218
|
+
// Non-fatal exception
|
|
219
|
+
Horizon->Crashes->RecordException(TEXT("Failed to load texture"), TEXT("stack trace"));
|
|
425
220
|
```
|
|
426
221
|
|
|
427
|
-
|
|
222
|
+
### Email Sending
|
|
223
|
+
|
|
224
|
+
```cpp
|
|
225
|
+
TMap<FString, FString> Variables;
|
|
226
|
+
Variables.Add(TEXT("username"), TEXT("John"));
|
|
227
|
+
|
|
228
|
+
// UserId, template slug, variables, language, callback (an overload takes an ISO 8601 ScheduledAt before the callback)
|
|
229
|
+
Horizon->EmailSending->SendEmail(TEXT("user-uuid"), TEXT("welcome"), Variables, TEXT("en"),
|
|
230
|
+
FOnSendEmailComplete::CreateLambda([](bool bSuccess, const FSendEmailResponse& Response) { }));
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`CancelEmail(EmailId, ...)` and `GetEmailStatus(EmailId, ...)` manage emails that were already sent or scheduled.
|
|
234
|
+
|
|
235
|
+
## Blueprints
|
|
236
|
+
|
|
237
|
+
Get the subsystem with the **Get HorizonSubsystem** node or use the async nodes directly. Examples of node names:
|
|
238
|
+
|
|
239
|
+
- **Connect to horizOn Server**
|
|
240
|
+
- **Sign Up Anonymous**, **Sign In Email**, **Restore Anonymous Session**, **Sign In With Apple (Native)**
|
|
241
|
+
- **Submit Leaderboard Score**, **Get Top Scores**, **Get Leaderboard Rank**
|
|
242
|
+
- **Cloud Save Data**, **Cloud Load Data**
|
|
243
|
+
- **Get Remote Config**, **Get All Remote Configs**, **Get Localization**, **Load News**
|
|
244
|
+
- **Validate Gift Code**, **Redeem Gift Code**, **Report Bug**, **Submit Feedback**
|
|
245
|
+
- **Record Exception**, **Report Crash**, **Send Email**
|
|
246
|
+
|
|
247
|
+
## Hello horizOn Example
|
|
248
|
+
|
|
249
|
+
The plugin ships `AHelloHorizonActor`. Set the API key and Backend Hosts in Project Settings, drop the actor into a level and press Play. It connects, signs up anonymously, submits a leaderboard score and logs the result. Per-feature example actors (`AHorizonAuthExample`, `AHorizonLeaderboardExample`, `AHorizonCloudSaveExample` and others) live in `Source/HorizonSDK/Public/Examples/`.
|
|
250
|
+
|
|
251
|
+
## Rate Limit Best Practices
|
|
252
|
+
|
|
253
|
+
All tiers are limited to **10 requests per minute per client**. The SDK retries HTTP 429 responses automatically.
|
|
254
|
+
|
|
255
|
+
- Load all remote configs and localizations once at startup
|
|
256
|
+
- Use the `bUseCache` parameters for leaderboards, remote config and news
|
|
257
|
+
- Submit scores only on improvement, not every frame
|
|
258
|
+
- Start crash capture once, not repeatedly
|
|
259
|
+
|
|
260
|
+
## Without the Plugin
|
|
428
261
|
|
|
429
|
-
|
|
430
|
-
- **Handle async properly** — All HTTP requests are asynchronous. Use delegates or lambdas for responses.
|
|
431
|
-
- **Rate limit** — 10 requests/minute. Cache responses and batch operations.
|
|
432
|
-
- **Error handling** — Always check HTTP status codes and handle 429 (rate limit) with exponential backoff.
|
|
433
|
-
- **Store session tokens** — After sign-in, cache the `accessToken` and `userId` for subsequent requests.
|
|
262
|
+
The SDK is the recommended path. If you cannot use the plugin, the App API can be called directly over HTTP with the `X-API-Key` header. See the `horizon://api/reference` resource for all endpoints.
|