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.
- package/README.md +138 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/debug-connection.d.ts +3 -0
- package/dist/prompts/debug-connection.d.ts.map +1 -0
- package/dist/prompts/debug-connection.js +17 -0
- package/dist/prompts/debug-connection.js.map +1 -0
- package/dist/prompts/explain-feature.d.ts +3 -0
- package/dist/prompts/explain-feature.d.ts.map +1 -0
- package/dist/prompts/explain-feature.js +32 -0
- package/dist/prompts/explain-feature.js.map +1 -0
- package/dist/prompts/index.d.ts +6 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +14 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/integrate-feature.d.ts +3 -0
- package/dist/prompts/integrate-feature.d.ts.map +1 -0
- package/dist/prompts/integrate-feature.js +35 -0
- package/dist/prompts/integrate-feature.js.map +1 -0
- package/dist/prompts/setup-auth.d.ts +3 -0
- package/dist/prompts/setup-auth.d.ts.map +1 -0
- package/dist/prompts/setup-auth.js +26 -0
- package/dist/prompts/setup-auth.js.map +1 -0
- package/dist/resources/index.d.ts +6 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +208 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +15 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/__tests__/api-client.test.d.ts +2 -0
- package/dist/tools/__tests__/api-client.test.d.ts.map +1 -0
- package/dist/tools/__tests__/api-client.test.js +156 -0
- package/dist/tools/__tests__/api-client.test.js.map +1 -0
- package/dist/tools/api-client.d.ts +25 -0
- package/dist/tools/api-client.d.ts.map +1 -0
- package/dist/tools/api-client.js +78 -0
- package/dist/tools/api-client.js.map +1 -0
- package/dist/tools/auth.d.ts +3 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +123 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/cloud-save.d.ts +3 -0
- package/dist/tools/cloud-save.d.ts.map +1 -0
- package/dist/tools/cloud-save.js +50 -0
- package/dist/tools/cloud-save.js.map +1 -0
- package/dist/tools/connection.d.ts +3 -0
- package/dist/tools/connection.d.ts.map +1 -0
- package/dist/tools/connection.js +20 -0
- package/dist/tools/connection.js.map +1 -0
- package/dist/tools/feedback.d.ts +3 -0
- package/dist/tools/feedback.d.ts.map +1 -0
- package/dist/tools/feedback.js +36 -0
- package/dist/tools/feedback.js.map +1 -0
- package/dist/tools/gift-codes.d.ts +3 -0
- package/dist/tools/gift-codes.d.ts.map +1 -0
- package/dist/tools/gift-codes.js +52 -0
- package/dist/tools/gift-codes.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +24 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/leaderboard.d.ts +3 -0
- package/dist/tools/leaderboard.d.ts.map +1 -0
- package/dist/tools/leaderboard.js +96 -0
- package/dist/tools/leaderboard.js.map +1 -0
- package/dist/tools/news.d.ts +3 -0
- package/dist/tools/news.d.ts.map +1 -0
- package/dist/tools/news.js +29 -0
- package/dist/tools/news.js.map +1 -0
- package/dist/tools/remote-config.d.ts +3 -0
- package/dist/tools/remote-config.d.ts.map +1 -0
- package/dist/tools/remote-config.js +42 -0
- package/dist/tools/remote-config.js.map +1 -0
- package/dist/tools/tool-helpers.d.ts +24 -0
- package/dist/tools/tool-helpers.d.ts.map +1 -0
- package/dist/tools/tool-helpers.js +54 -0
- package/dist/tools/tool-helpers.js.map +1 -0
- package/dist/tools/user-logs.d.ts +3 -0
- package/dist/tools/user-logs.d.ts.map +1 -0
- package/dist/tools/user-logs.js +30 -0
- package/dist/tools/user-logs.js.map +1 -0
- package/package.json +52 -0
- package/src/resources/api/app-api.md +495 -0
- package/src/resources/docs/auth.md +280 -0
- package/src/resources/docs/cloud-save.md +180 -0
- package/src/resources/docs/feedback.md +126 -0
- package/src/resources/docs/gift-codes.md +153 -0
- package/src/resources/docs/leaderboard.md +201 -0
- package/src/resources/docs/news.md +114 -0
- package/src/resources/docs/overview.md +80 -0
- package/src/resources/docs/remote-config.md +149 -0
- package/src/resources/docs/user-logs.md +144 -0
- package/src/resources/quickstart/godot.md +224 -0
- package/src/resources/quickstart/unity.md +317 -0
- package/src/resources/quickstart/unreal.md +390 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# Unreal Engine Quickstart Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
There is **no official horizOn SDK for Unreal Engine**. Instead, you use the REST API directly via HTTP requests. This guide shows how to integrate horizOn using either:
|
|
6
|
+
|
|
7
|
+
- **VaRest Plugin** — Marketplace plugin for Blueprint-friendly HTTP requests
|
|
8
|
+
- **FHttpModule** — Unreal's built-in C++ HTTP module
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- **Unreal Engine 5.x**
|
|
13
|
+
- horizOn API key (get one at [horizon.pm](https://horizon.pm))
|
|
14
|
+
- HTTP request capability (VaRest plugin or FHttpModule)
|
|
15
|
+
|
|
16
|
+
## API Configuration
|
|
17
|
+
|
|
18
|
+
All requests use these common settings:
|
|
19
|
+
|
|
20
|
+
| Setting | Value |
|
|
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 |
|
|
26
|
+
|
|
27
|
+
## Option A: VaRest Plugin (Blueprint-Friendly)
|
|
28
|
+
|
|
29
|
+
Install VaRest from the Unreal Marketplace, then use it in Blueprints or C++.
|
|
30
|
+
|
|
31
|
+
### C++ Setup with VaRest
|
|
32
|
+
|
|
33
|
+
```cpp
|
|
34
|
+
// MyHorizonManager.h
|
|
35
|
+
#pragma once
|
|
36
|
+
#include "CoreMinimal.h"
|
|
37
|
+
#include "VaRestSubsystem.h"
|
|
38
|
+
|
|
39
|
+
UCLASS()
|
|
40
|
+
class UMyHorizonManager : public UObject
|
|
41
|
+
{
|
|
42
|
+
GENERATED_BODY()
|
|
43
|
+
|
|
44
|
+
public:
|
|
45
|
+
void SetApiKey(const FString& InApiKey) { ApiKey = InApiKey; }
|
|
46
|
+
|
|
47
|
+
void SignUpAnonymous(const FString& Username);
|
|
48
|
+
void SubmitScore(const FString& UserId, int64 Score);
|
|
49
|
+
void GetRemoteConfig(const FString& Key);
|
|
50
|
+
|
|
51
|
+
private:
|
|
52
|
+
FString BaseUrl = TEXT("https://horizon.pm");
|
|
53
|
+
FString ApiKey;
|
|
54
|
+
|
|
55
|
+
UVaRestJsonObject* CreateRequestWithHeaders();
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Option B: FHttpModule (Built-in C++)
|
|
60
|
+
|
|
61
|
+
No plugins required. Uses Unreal's native HTTP module.
|
|
62
|
+
|
|
63
|
+
### Base HTTP Helper
|
|
64
|
+
|
|
65
|
+
```cpp
|
|
66
|
+
// HorizonAPI.h
|
|
67
|
+
#pragma once
|
|
68
|
+
#include "CoreMinimal.h"
|
|
69
|
+
#include "Http.h"
|
|
70
|
+
#include "Json.h"
|
|
71
|
+
|
|
72
|
+
class FHorizonAPI
|
|
73
|
+
{
|
|
74
|
+
public:
|
|
75
|
+
static FString BaseUrl;
|
|
76
|
+
static FString ApiKey;
|
|
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)
|
|
120
|
+
{
|
|
121
|
+
FHttpModule& Http = FHttpModule::Get();
|
|
122
|
+
TSharedRef<IHttpRequest> Request = Http.CreateRequest();
|
|
123
|
+
|
|
124
|
+
Request->SetURL(BaseUrl + Endpoint);
|
|
125
|
+
Request->SetVerb(TEXT("GET"));
|
|
126
|
+
Request->SetHeader(TEXT("X-API-Key"), ApiKey);
|
|
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("");
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Feature Examples
|
|
154
|
+
|
|
155
|
+
### Anonymous Signup
|
|
156
|
+
|
|
157
|
+
```cpp
|
|
158
|
+
void SignUpAnonymous(const FString& Username)
|
|
159
|
+
{
|
|
160
|
+
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
161
|
+
Body->SetStringField(TEXT("type"), TEXT("ANONYMOUS"));
|
|
162
|
+
Body->SetStringField(TEXT("username"), Username);
|
|
163
|
+
|
|
164
|
+
// Generate a unique anonymous token (max 32 chars)
|
|
165
|
+
FString Token = FGuid::NewGuid().ToString(EGuidFormats::DigitsLower).Left(32);
|
|
166
|
+
Body->SetStringField(TEXT("anonymousToken"), Token);
|
|
167
|
+
|
|
168
|
+
FHorizonAPI::Post(TEXT("/api/v1/app/user-management/signup"), Body,
|
|
169
|
+
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
170
|
+
{
|
|
171
|
+
if (bSuccess && Response.IsValid())
|
|
172
|
+
{
|
|
173
|
+
FString UserId = Response->GetStringField(TEXT("userId"));
|
|
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
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Email Sign-In
|
|
186
|
+
|
|
187
|
+
```cpp
|
|
188
|
+
void SignInEmail(const FString& Email, const FString& Password)
|
|
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);
|
|
194
|
+
|
|
195
|
+
FHorizonAPI::Post(TEXT("/api/v1/app/user-management/signin"), Body,
|
|
196
|
+
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
197
|
+
{
|
|
198
|
+
if (bSuccess && Response.IsValid())
|
|
199
|
+
{
|
|
200
|
+
FString Status = Response->GetStringField(TEXT("authStatus"));
|
|
201
|
+
if (Status == TEXT("AUTHENTICATED"))
|
|
202
|
+
{
|
|
203
|
+
FString AccessToken = Response->GetStringField(TEXT("accessToken"));
|
|
204
|
+
UE_LOG(LogTemp, Log, TEXT("Signed in successfully"));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Submit Leaderboard Score
|
|
212
|
+
|
|
213
|
+
```cpp
|
|
214
|
+
void SubmitScore(const FString& UserId, int64 Score)
|
|
215
|
+
{
|
|
216
|
+
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
217
|
+
Body->SetStringField(TEXT("userId"), UserId);
|
|
218
|
+
Body->SetNumberField(TEXT("score"), Score);
|
|
219
|
+
|
|
220
|
+
FHorizonAPI::Post(TEXT("/api/v1/app/leaderboard/submit"), Body,
|
|
221
|
+
[Score](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
222
|
+
{
|
|
223
|
+
if (bSuccess)
|
|
224
|
+
{
|
|
225
|
+
UE_LOG(LogTemp, Log, TEXT("Score submitted: %lld"), Score);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Get Top Leaderboard
|
|
232
|
+
|
|
233
|
+
```cpp
|
|
234
|
+
void GetTopLeaderboard(const FString& UserId, int32 Limit)
|
|
235
|
+
{
|
|
236
|
+
FString Endpoint = FString::Printf(
|
|
237
|
+
TEXT("/api/v1/app/leaderboard/top?userId=%s&limit=%d"),
|
|
238
|
+
*UserId, Limit);
|
|
239
|
+
|
|
240
|
+
FHorizonAPI::Get(Endpoint,
|
|
241
|
+
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
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
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Save and Load Cloud Data
|
|
263
|
+
|
|
264
|
+
```cpp
|
|
265
|
+
void SaveCloudData(const FString& UserId, const FString& SaveData)
|
|
266
|
+
{
|
|
267
|
+
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
268
|
+
Body->SetStringField(TEXT("userId"), UserId);
|
|
269
|
+
Body->SetStringField(TEXT("saveData"), SaveData);
|
|
270
|
+
|
|
271
|
+
FHorizonAPI::Post(TEXT("/api/v1/app/cloud-save/save"), Body,
|
|
272
|
+
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
273
|
+
{
|
|
274
|
+
if (bSuccess && Response.IsValid())
|
|
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
|
+
}
|
|
282
|
+
|
|
283
|
+
void LoadCloudData(const FString& UserId)
|
|
284
|
+
{
|
|
285
|
+
TSharedRef<FJsonObject> Body = MakeShared<FJsonObject>();
|
|
286
|
+
Body->SetStringField(TEXT("userId"), UserId);
|
|
287
|
+
|
|
288
|
+
FHorizonAPI::Post(TEXT("/api/v1/app/cloud-save/load"), Body,
|
|
289
|
+
[](bool bSuccess, TSharedPtr<FJsonObject> Response)
|
|
290
|
+
{
|
|
291
|
+
if (bSuccess && Response.IsValid())
|
|
292
|
+
{
|
|
293
|
+
bool Found = Response->GetBoolField(TEXT("found"));
|
|
294
|
+
if (Found)
|
|
295
|
+
{
|
|
296
|
+
FString Data = Response->GetStringField(TEXT("saveData"));
|
|
297
|
+
UE_LOG(LogTemp, Log, TEXT("Loaded: %s"), *Data);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Get Remote Config
|
|
305
|
+
|
|
306
|
+
```cpp
|
|
307
|
+
void GetAllRemoteConfigs()
|
|
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
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## REST Examples (cURL)
|
|
329
|
+
|
|
330
|
+
These cURL examples show the raw HTTP requests. Translate them to your preferred Unreal HTTP method.
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
# Anonymous signup
|
|
334
|
+
curl -X POST https://horizon.pm/api/v1/app/user-management/signup \
|
|
335
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
336
|
+
-H "Content-Type: application/json" \
|
|
337
|
+
-d '{"type": "ANONYMOUS", "username": "Player1", "anonymousToken": "unique32chartoken"}'
|
|
338
|
+
|
|
339
|
+
# Submit score
|
|
340
|
+
curl -X POST https://horizon.pm/api/v1/app/leaderboard/submit \
|
|
341
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
342
|
+
-H "Content-Type: application/json" \
|
|
343
|
+
-d '{"userId": "user123", "score": 12500}'
|
|
344
|
+
|
|
345
|
+
# Get remote config
|
|
346
|
+
curl "https://horizon.pm/api/v1/app/remote-config/all" \
|
|
347
|
+
-H "X-API-Key: YOUR_API_KEY"
|
|
348
|
+
|
|
349
|
+
# Save cloud data
|
|
350
|
+
curl -X POST https://horizon.pm/api/v1/app/cloud-save/save \
|
|
351
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
352
|
+
-H "Content-Type: application/json" \
|
|
353
|
+
-d '{"userId": "user123", "saveData": "{\"level\":5}"}'
|
|
354
|
+
|
|
355
|
+
# Load cloud data
|
|
356
|
+
curl -X POST https://horizon.pm/api/v1/app/cloud-save/load \
|
|
357
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
358
|
+
-H "Content-Type: application/json" \
|
|
359
|
+
-d '{"userId": "user123"}'
|
|
360
|
+
|
|
361
|
+
# Get news
|
|
362
|
+
curl "https://horizon.pm/api/v1/app/news?limit=10&languageCode=en" \
|
|
363
|
+
-H "X-API-Key: YOUR_API_KEY"
|
|
364
|
+
|
|
365
|
+
# Redeem gift code
|
|
366
|
+
curl -X POST https://horizon.pm/api/v1/app/gift-codes/redeem \
|
|
367
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
368
|
+
-H "Content-Type: application/json" \
|
|
369
|
+
-d '{"code": "ABCD-1234", "userId": "user123"}'
|
|
370
|
+
|
|
371
|
+
# Submit feedback
|
|
372
|
+
curl -X POST https://horizon.pm/api/v1/app/user-feedback/submit \
|
|
373
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
374
|
+
-H "Content-Type: application/json" \
|
|
375
|
+
-d '{"title": "Bug Report", "message": "Description", "userId": "user123", "category": "BUG"}'
|
|
376
|
+
|
|
377
|
+
# Create user log
|
|
378
|
+
curl -X POST https://horizon.pm/api/v1/app/user-logs/create \
|
|
379
|
+
-H "X-API-Key: YOUR_API_KEY" \
|
|
380
|
+
-H "Content-Type: application/json" \
|
|
381
|
+
-d '{"message": "Level complete", "type": "INFO", "userId": "user123"}'
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Best Practices for Unreal
|
|
385
|
+
|
|
386
|
+
- **Cache API key** — Store the API key in a config file or game settings, not hardcoded.
|
|
387
|
+
- **Handle async properly** — All HTTP requests are asynchronous. Use delegates or lambdas for responses.
|
|
388
|
+
- **Rate limit** — 10 requests/minute. Cache responses and batch operations.
|
|
389
|
+
- **Error handling** — Always check HTTP status codes and handle 429 (rate limit) with exponential backoff.
|
|
390
|
+
- **Store session tokens** — After sign-in, cache the `accessToken` and `userId` for subsequent requests.
|