Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
ArkBaseApi.cpp
Go to the documentation of this file.
1#include "ArkBaseApi.h"
2#include "..\Private\PDBReader\PDBReader.h"
3#include "..\PluginManager\PluginManager.h"
4#include "..\Private\Offsets.h"
5#include "..\Private\Cache.h"
6#include "..\Hooks.h"
7#include "..\Commands.h"
8#include "Tools.h"
9#include <Logger/Logger.h>
10#include "HooksImpl.h"
11#include "ApiUtils.h"
12#include <filesystem>
13#include "Requests.h"
14#include <minizip/unzip.h>
15
16namespace API
17{
18 constexpr float api_version = 1.13f;
19
24 {
25 }
26
28 {
29 nlohmann::json apiConfig = ArkBaseApi::GetConfig();
30 const nlohmann::json autoCacheConfig = apiConfig.value("settings", nlohmann::json::object()).value("AutomaticCacheDownload", nlohmann::json::object());
31 namespace fs = std::filesystem;
32
33 Log::GetLog()->info("-----------------------------------------------");
34 Log::GetLog()->info("ARK:SA Api V{:.2f}", GetVersion());
35 Log::GetLog()->info("Loading...\n");
36
37 PdbReader pdb_reader;
38
39 std::unordered_map<std::string, intptr_t> offsets_dump;
40 std::unordered_map<std::string, BitField> bitfields_dump;
41
42 try
43 {
44 TCHAR buffer[MAX_PATH];
45 GetModuleFileName(NULL, buffer, sizeof(buffer));
46 fs::path exe_path = std::filesystem::path(buffer).parent_path();
47
48 const fs::path filepath = fs::path(exe_path).append("ArkAscendedServer.pdb");
49
50 if (!fs::exists(fs::path(exe_path).append(ArkBaseApi::GetApiName())))
51 fs::create_directory(fs::path(exe_path).append(ArkBaseApi::GetApiName()));
52
53 if (!fs::exists(fs::path(exe_path).append(ArkBaseApi::GetApiName() + "/Plugins")))
54 fs::create_directory(fs::path(exe_path).append(ArkBaseApi::GetApiName() + "/Plugins"));
55
56 if (!fs::exists(fs::path(exe_path).append(ArkBaseApi::GetApiName()+"/Cache")))
57 fs::create_directory(fs::path(exe_path).append(ArkBaseApi::GetApiName()+"/Cache"));
58
59 const fs::path pdbIgnoreFile = fs::path(exe_path).append(ArkBaseApi::GetApiName() + "/pdbignores.txt");
60 const fs::path keyCacheFile = fs::path(exe_path).append(ArkBaseApi::GetApiName()+"/Cache/cached_key.cache");
61 const fs::path offsetsCacheFile = fs::path(exe_path).append(ArkBaseApi::GetApiName()+"/Cache/cached_offsets.cache");
62 const fs::path bitfieldsCacheFile = fs::path(exe_path).append(ArkBaseApi::GetApiName()+"/Cache/cached_bitfields.cache");
63 const fs::path offsetsCacheFilePlain = fs::path(exe_path).append(ArkBaseApi::GetApiName() + "/Cache/cached_offsets.txt");
64 const std::string fileHash = Cache::calculateSHA256(filepath);
65 std::string storedHash = Cache::readFromFile(keyCacheFile);
66 std::unordered_set<std::string> pdbIgnoreSet = Cache::readFileIntoSet(pdbIgnoreFile);
67
68 if (autoCacheConfig.value("Enable", true)
69 && autoCacheConfig.value("DownloadCacheURL", "https://cdn.pelayori.com/cache/") != ""
70 && (fileHash != storedHash || !fs::exists(offsetsCacheFile) || !fs::exists(bitfieldsCacheFile)))
71 {
72 const fs::path downloadFile = autoCacheConfig.value("DownloadCacheURL", "") + fileHash + ".zip";
73 const fs::path localFile = fs::path(exe_path).append(ArkBaseApi::GetApiName() + "/Cache/" + fileHash + ".zip");
74
75 if (ArkBaseApi::DownloadCacheFiles(downloadFile, localFile))
76 storedHash = Cache::readFromFile(keyCacheFile);
77 else
78 Log::GetLog()->error("Failed to download the Cache files.");
79
80 if (fs::exists(localFile))
81 fs::remove(localFile);
82 }
83
84 if (fileHash != storedHash || !fs::exists(offsetsCacheFile) || !fs::exists(bitfieldsCacheFile))
85 {
86 Log::GetLog()->info("Cache refresh required this will take 10-20 minutes to complete");
87 pdb_reader.Read(filepath, &offsets_dump, &bitfields_dump, pdbIgnoreSet);
88
89 Log::GetLog()->info("Caching offsets for faster loading next time");
90 Cache::serializeMap(offsets_dump, offsetsCacheFile);
91
92 Log::GetLog()->info("Caching bitfields for faster loading next time");
93 Cache::serializeMap(bitfields_dump, bitfieldsCacheFile);
94 Cache::saveToFile(keyCacheFile, fileHash);
95 Cache::saveToFilePlain(offsetsCacheFilePlain, offsets_dump);
96 }
97 else
98 {
99 Log::GetLog()->info("Cache is still valid loading existing cache");
100 Log::GetLog()->info("Reading cached offsets");
101 offsets_dump = Cache::deserializeMap<intptr_t>(offsetsCacheFile);
102
103 Log::GetLog()->info("Reading cached bitfields");
104 bitfields_dump = Cache::deserializeMap<BitField>(bitfieldsCacheFile);
105 }
106 }
107 catch (const std::exception& error)
108 {
109 Log::GetLog()->critical("Failed to read pdb - {}", error.what());
110 return false;
111 }
112
113 Offsets::Get().Init(move(offsets_dump), move(bitfields_dump));
114 Sleep(10);
116 Log::GetLog()->info("API was successfully loaded");
117 Log::GetLog()->info("-----------------------------------------------\n");
118
119 return true;
120 }
121
123 {
124 const std::string config_path = AsaApi::Tools::GetCurrentDir() + "/config.json";
125 std::ifstream file{ config_path };
126 if (!file.is_open())
127 return false;
128
129 nlohmann::json config;
130 file >> config;
131 file.close();
132
133 return config;
134 }
135
136 bool ArkBaseApi::DownloadCacheFiles(const std::filesystem::path downloadFile, const std::filesystem::path localFile)
137 {
138 if (API::Requests::DownloadFile(downloadFile.string(), localFile.string()))
139 {
140 std::string outputFolder = localFile.parent_path().string();
141 unzFile zf = unzOpen(localFile.string().c_str());
142 if (zf == nullptr)
143 return false;
144
145 unz_global_info globalInfo;
146 if (unzGetGlobalInfo(zf, &globalInfo) != UNZ_OK)
147 {
148 unzClose(zf);
149 return false;
150 }
151
152 char readBuffer[8192];
153
154 for (uLong i = 0; i < globalInfo.number_entry; ++i)
155 {
156 unz_file_info fileInfo;
157 char filename[256];
158 if (unzGetCurrentFileInfo(zf, &fileInfo, filename, sizeof(filename), NULL, 0, NULL, 0) != UNZ_OK)
159 {
160 unzClose(zf);
161 return false;
162 }
163
164 const size_t filenameLength = strlen(filename);
165 if (filename[filenameLength - 1] == '/')
166 continue;
167 else
168 {
169 if (unzOpenCurrentFile(zf) != UNZ_OK)
170 {
171 unzClose(zf);
172 return false;
173 }
174
175 std::string fullPath = outputFolder + "/" + filename;
176 std::ofstream out(fullPath, std::ios::binary);
177
178 if (!out)
179 {
180 unzCloseCurrentFile(zf);
181 unzClose(zf);
182 return false;
183 }
184
185 int bytesRead;
186 do {
187 bytesRead = unzReadCurrentFile(zf, readBuffer, sizeof(readBuffer));
188 if (bytesRead < 0)
189 {
190 unzCloseCurrentFile(zf);
191 unzClose(zf);
192 return false;
193 }
194
195 if (bytesRead > 0)
196 out.write(readBuffer, bytesRead);
197 } while (bytesRead > 0);
198
199 unzCloseCurrentFile(zf);
200 out.close();
201 }
202
203 if ((i + 1) < globalInfo.number_entry)
204 {
205 if (unzGoToNextFile(zf) != UNZ_OK)
206 {
207 unzClose(zf);
208 return false;
209 }
210 }
211 }
212
213 unzClose(zf);
214 }
215 else
216 return false;
217
218 Log::GetLog()->info("Cache files downloaded and processed successfully");
219 return true;
220 }
221
223 {
224 return api_version;
225 }
226
228 {
229 return "ArkApi";
230 }
231
233 {
234 return hooks_;
235 }
236
238 {
239 return commands_;
240 }
241
243 {
244 return api_utils_;
245 }
246
248 {
249 GetCommands()->AddConsoleCommand("plugins.load", &LoadPluginCmd);
250 GetCommands()->AddConsoleCommand("plugins.unload", &UnloadPluginCmd);
251 GetCommands()->AddRconCommand("plugins.load", &LoadPluginRcon);
252 GetCommands()->AddRconCommand("plugins.unload", &UnloadPluginRcon);
253 GetCommands()->AddRconCommand("map.setserverid", &SetServerID);
254 }
255
257 {
258 TArray<FString> parsed;
259 cmd->ParseIntoArray(parsed, L" ", true);
260
261 if (parsed.IsValidIndex(1))
262 {
263 const std::string plugin_name = parsed[1].ToString();
264
265 try
266 {
268 }
269 catch (const std::exception& error)
270 {
271 Log::GetLog()->warn("({}) {}", __FUNCTION__, error.what());
272 return FString::Format("Failed to load plugin - {}", error.what());
273 }
274
275 Log::GetLog()->info("Loaded plugin - {}", plugin_name.c_str());
276
277 return "Successfully loaded plugin";
278 }
279
280 return "Plugin not found";
281 }
282
284 {
285 TArray<FString> parsed;
286 cmd->ParseIntoArray(parsed, L" ", true);
287
288 if (parsed.IsValidIndex(1))
289 {
290 const std::string plugin_name = parsed[1].ToString();
291
292 try
293 {
295 }
296 catch (const std::exception& error)
297 {
298 Log::GetLog()->warn("({}) {}", __FUNCTION__, error.what());
299 return *FString::Format("Failed to unload plugin - {}", error.what());
300 }
301
302 Log::GetLog()->info("Unloaded plugin - {}", plugin_name.c_str());
303
304 return L"Successfully unloaded plugin";
305 }
306
307 return L"Plugin not found";
308 }
309
310 // Command Callbacks
311 void ArkBaseApi::LoadPluginCmd(APlayerController* player_controller, FString* cmd, bool /*unused*/)
312 {
313 auto* shooter_controller = static_cast<AShooterPlayerController*>(player_controller);
314 AsaApi::GetApiUtils().SendServerMessage(shooter_controller, FColorList::Green, *LoadPlugin(cmd));
315 }
316
317 void ArkBaseApi::UnloadPluginCmd(APlayerController* player_controller, FString* cmd, bool /*unused*/)
318 {
319 auto* shooter_controller = static_cast<AShooterPlayerController*>(player_controller);
320 AsaApi::GetApiUtils().SendServerMessage(shooter_controller, FColorList::Green, *UnloadPlugin(cmd));
321 }
322
323 // RCON Command Callbacks
324 void ArkBaseApi::LoadPluginRcon(RCONClientConnection* rcon_connection, RCONPacket* rcon_packet, UWorld* /*unused*/)
325 {
326 FString reply = LoadPlugin(&rcon_packet->Body);
327 rcon_connection->SendMessageW(rcon_packet->Id, 0, &reply);
328 }
329
330 void ArkBaseApi::UnloadPluginRcon(RCONClientConnection* rcon_connection, RCONPacket* rcon_packet,
331 UWorld* /*unused*/)
332 {
333 FString reply = UnloadPlugin(&rcon_packet->Body);
334 rcon_connection->SendMessageW(rcon_packet->Id, 0, &reply);
335 }
336
337 void ArkBaseApi::SetServerID(RCONClientConnection* rcon_connection, RCONPacket* rcon_packet,
338 UWorld* /*unused*/)
339 {
340 FString reply = "Set new server id";
341 TArray<FString> parsed;
342 rcon_packet->Body.ParseIntoArray(parsed, L" ", true);
343
344 if (parsed.IsValidIndex(1))
345 {
346 int new_server_id = std::stoi(parsed[1].ToString());
347
348 try
349 {
350 const auto& actors = AsaApi::GetApiUtils().GetWorld()->PersistentLevelField().Get()->ActorsField();
351 for (auto actor : actors)
352 {
353 FString bp = AsaApi::GetApiUtils().GetBlueprint(actor);
354 if (bp.Equals("Blueprint'/Script/ShooterGame.PrimalPersistentWorldData'"))
355 {
356 actor->TargetingTeamField() = new_server_id;
357
358 AsaApi::GetApiUtils().GetShooterGameMode()->MyServerIdField() = FString(std::to_string(new_server_id));
359 AsaApi::GetApiUtils().GetShooterGameMode()->ServerIDField() = new_server_id;
360 Log::GetLog()->info("SERVER ID: {}", new_server_id);
361 Log::GetLog()->info("Forcing world save to lock-in new server id");
362 AsaApi::GetApiUtils().GetShooterGameMode()->SaveWorld(false, true);
363
364 break;
365 }
366 }
367 }
368 catch (const std::exception& error)
369 {
370 Log::GetLog()->warn("({}) {}", __FUNCTION__, error.what());
371 reply = FString::Format("Failed to set server id - {}", error.what());
372 }
373 }
374 else
375 reply = L"You must specify a unique server id.";
376
377
378 rcon_connection->SendMessageW(rcon_packet->Id, 0, &reply);
379 }
380} // namespace API
static void SetServerID(RCONClientConnection *, RCONPacket *, UWorld *)
std::string GetApiName() override
bool DownloadCacheFiles(const std::filesystem::path downloadFile, const std::filesystem::path localFile)
std::unique_ptr< AsaApi::IApiUtils > & GetApiUtils() override
static void LoadPluginCmd(APlayerController *, FString *, bool)
void RegisterCommands() override
float GetVersion() override
static FString LoadPlugin(FString *cmd)
static void UnloadPluginRcon(RCONClientConnection *, RCONPacket *, UWorld *)
nlohmann::json GetConfig()
bool Init() override
static void UnloadPluginCmd(APlayerController *, FString *, bool)
static void LoadPluginRcon(RCONClientConnection *, RCONPacket *, UWorld *)
static FString UnloadPlugin(FString *cmd)
std::unique_ptr< AsaApi::ICommands > & GetCommands() override
std::unique_ptr< AsaApi::IHooks > & GetHooks() override
static PluginManager & Get()
std::shared_ptr< Plugin > & LoadPlugin(const std::string &plugin_name) noexcept(false)
Load plugin by it's name.
void UnloadPlugin(const std::string &plugin_name) noexcept(false)
Unload plugin by it's name. Plugin must free all used resources.
static const FColor Green
Definition ColorList.h:24
UE_NODISCARD FORCEINLINE const TCHAR * operator*() const UE_LIFETIMEBOUND
Definition IBaseApi.h:9
constexpr float api_version
ARK_API std::string GetCurrentDir()
Definition Tools.cpp:8
IApiUtils & GetApiUtils()
Definition ApiUtils.cpp:206
void InitHooks()
Definition HooksImpl.cpp:43
Definition json.hpp:4518
void SendMessageW(int Id, int Type, FString *OutGoingMessage)
Definition Other.h:38
int Id
Definition Other.h:13