1010#include " HooksImpl.h"
1111#include " ApiUtils.h"
1212#include < filesystem>
13+ #include " Requests.h"
14+ #include < minizip/unzip.h>
1315
1416namespace API
1517{
16- constexpr float api_version = 1 .12f ;
18+ constexpr float api_version = 1 .13f ;
1719
1820 ArkBaseApi::ArkBaseApi ()
1921 : commands_(std::make_unique<AsaApi::Commands>()),
@@ -24,8 +26,10 @@ namespace API
2426
2527 bool ArkBaseApi::Init ()
2628 {
29+ nlohmann::json apiConfig = ArkBaseApi::GetConfig ();
30+ const nlohmann::json autoCacheConfig = apiConfig.value (" settings" , nlohmann::json::object ()).value (" AutomaticCacheDownload" , nlohmann::json::object ());
2731 namespace fs = std::filesystem;
28-
32+
2933 Log::GetLog ()->info (" -----------------------------------------------" );
3034 Log::GetLog ()->info (" ARK:SA Api V{:.2f}" , GetVersion ());
3135 Log::GetLog ()->info (" Loading...\n " );
@@ -58,14 +62,28 @@ namespace API
5862 const fs::path bitfieldsCacheFile = fs::path (exe_path).append (ArkBaseApi::GetApiName ()+" /Cache/cached_bitfields.cache" );
5963 const fs::path offsetsCacheFilePlain = fs::path (exe_path).append (ArkBaseApi::GetApiName () + " /Cache/cached_offsets.txt" );
6064 const std::string fileHash = Cache::calculateSHA256 (filepath);
61- const std::string storedHash = Cache::readFromFile (keyCacheFile);
65+ std::string storedHash = Cache::readFromFile (keyCacheFile);
6266 std::unordered_set<std::string> pdbIgnoreSet = Cache::readFileIntoSet (pdbIgnoreFile);
6367
64- if (fileHash != storedHash
65- || ! fs::exists (offsetsCacheFile)
66- || !fs::exists (bitfieldsCacheFile))
68+ if (autoCacheConfig. value ( " Enable " , true )
69+ && autoCacheConfig. value ( " DownloadCacheURL " , " https://cdn.pelayori.com/cache/ " ) != " "
70+ && (fileHash != storedHash || !fs::exists (offsetsCacheFile) || ! fs::exists ( bitfieldsCacheFile) ))
6771 {
68- Log::GetLog ()->info (" Cache refresh required this will take several minutes to complete" );
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" );
6987 pdb_reader.Read (filepath, &offsets_dump, &bitfields_dump, pdbIgnoreSet);
7088
7189 Log::GetLog ()->info (" Caching offsets for faster loading next time" );
@@ -92,8 +110,6 @@ namespace API
92110 return false ;
93111 }
94112
95-
96-
97113 Offsets::Get ().Init (move (offsets_dump), move (bitfields_dump));
98114 Sleep (10 );
99115 AsaApi::InitHooks ();
@@ -103,6 +119,106 @@ namespace API
103119 return true ;
104120 }
105121
122+ nlohmann::json ArkBaseApi::GetConfig ()
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+
106222 float ArkBaseApi::GetVersion ()
107223 {
108224 return api_version;
@@ -134,6 +250,7 @@ namespace API
134250 GetCommands ()->AddConsoleCommand (" plugins.unload" , &UnloadPluginCmd);
135251 GetCommands ()->AddRconCommand (" plugins.load" , &LoadPluginRcon);
136252 GetCommands ()->AddRconCommand (" plugins.unload" , &UnloadPluginRcon);
253+ GetCommands ()->AddRconCommand (" map.setserverid" , &SetServerID);
137254 }
138255
139256 FString ArkBaseApi::LoadPlugin (FString* cmd)
@@ -216,4 +333,48 @@ namespace API
216333 FString reply = UnloadPlugin (&rcon_packet->Body );
217334 rcon_connection->SendMessageW (rcon_packet->Id , 0 , &reply);
218335 }
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+ }
219380} // namespace API
0 commit comments