Skip to content

Commit e5b889f

Browse files
Cache Updates (#34)
* Header updates Automated cache downloading Added new RCON command `map.setserverid <id>` to allow changing the ServerID * Updated SaveWorld * Made auto cache config more readable, fixed save world bool missing * Set cdn enabled by default * Updated cache refresh message for a realistic timeframe if cache is being built by the server itself. * Removed debug processor used for detecting bad offsets in favor of always showing bad offsets instead of just mysteriously crashing with no info. --------- Co-authored-by: Pelayori <[email protected]>
1 parent 1b10222 commit e5b889f

File tree

13 files changed

+680
-36
lines changed

13 files changed

+680
-36
lines changed

AsaApi/AsaApi.vcxproj

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ copy /Y "$(SolutionDir)AsaApi\Core\Private\PDBReader\pdbignores.txt" "E:\ASA\Te
324324
<FunctionLevelLinking>true</FunctionLevelLinking>
325325
<IntrinsicFunctions>true</IntrinsicFunctions>
326326
<SDLCheck>true</SDLCheck>
327-
<PreprocessorDefinitions>API_DEBUG;NDEBUG;ASAAPI_EXPORTS;ARK_EXPORTS;_WINDOWS;_USRDLL;POCO_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
327+
<PreprocessorDefinitions>NDEBUG;ASAAPI_EXPORTS;ARK_EXPORTS;_WINDOWS;_USRDLL;POCO_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
328328
<ConformanceMode>true</ConformanceMode>
329329
<PrecompiledHeader>NotUsing</PrecompiledHeader>
330330
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
@@ -343,9 +343,7 @@ copy /Y "$(SolutionDir)AsaApi\Core\Private\PDBReader\pdbignores.txt" "E:\ASA\Te
343343
<AdditionalDependencies>$(CoreLibraryDependencies);crypt32.lib;Ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
344344
</Link>
345345
<PostBuildEvent>
346-
<Command>xcopy /I /Y "$(TargetDir)$(ProjectName).lib" "$(SolutionDir)out_lib\"
347-
xcopy /I /Y "$(TargetDir)$(ProjectName).lib" "$(SolutionDir)..\ARK-API-Stable-Libs\"
348-
xcopy /I /Y "$(TargetDir)$(ProjectName).lib" "$(SolutionDir)..\ARK-API-Stable-Libs\1.02\"
346+
<Command>xcopy /I /Y "$(TargetDir)$(ProjectName).lib" "$(SolutionDir)..\ARK-API-Stable-Libs\dev-latest\"
349347

350348
copy "$(SolutionDir)$(PlatformName)\$(ConfigurationName)\$(ProjectName).dll" "F:\ASA-Dedicated\asa-server\ShooterGame\Binaries\Win64\ArkApi\" /y
351349
copy "$(SolutionDir)$(PlatformName)\$(ConfigurationName)\$(ProjectName).pdb" "F:\ASA-Dedicated\asa-server\ShooterGame\Binaries\Win64\ArkApi\" /y</Command>

AsaApi/Core/Private/Ark/ArkBaseApi.cpp

Lines changed: 170 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
#include "HooksImpl.h"
1111
#include "ApiUtils.h"
1212
#include <filesystem>
13+
#include "Requests.h"
14+
#include <minizip/unzip.h>
1315

1416
namespace 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

AsaApi/Core/Private/Ark/ArkBaseApi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "../IBaseApi.h"
44
#include <IApiUtils.h>
55
#include "Containers/UnrealString.h"
6+
#include <filesystem>
7+
#include <json.hpp>
68

79
namespace API
810
{
@@ -22,6 +24,9 @@ namespace API
2224
std::unique_ptr<AsaApi::IApiUtils>& GetApiUtils() override;
2325

2426
private:
27+
nlohmann::json GetConfig();
28+
bool DownloadCacheFiles(const std::filesystem::path downloadFile, const std::filesystem::path localFile);
29+
2530
// Callbacks
2631
static FString LoadPlugin(FString* cmd);
2732
static FString UnloadPlugin(FString* cmd);
@@ -34,6 +39,9 @@ namespace API
3439
static void UnloadPluginRcon(RCONClientConnection* /*rcon_connection*/, RCONPacket* /*rcon_packet*/,
3540
UWorld* /*unused*/);
3641

42+
static void SetServerID(RCONClientConnection* /*rcon_connection*/, RCONPacket* /*rcon_packet*/,
43+
UWorld* /*unused*/);
44+
3745
std::unique_ptr<AsaApi::ICommands> commands_;
3846
std::unique_ptr<AsaApi::IHooks> hooks_;
3947
std::unique_ptr<AsaApi::IApiUtils> api_utils_;

AsaApi/Core/Private/Offsets.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,45 +49,39 @@ namespace API
4949

5050
DWORD64 Offsets::GetAddress(const void* base, const std::string& name)
5151
{
52-
#ifdef API_DEBUG
5352
if (!offsets_dump_.contains(name))
5453
{
5554
Log::GetLog()->critical("Failed to get the offset of {}.", name);
5655
Log::GetLog()->flush();
5756
Sleep(10000);
5857
throw;
5958
}
60-
#endif
6159

6260
return reinterpret_cast<DWORD64>(base) + static_cast<DWORD64>(offsets_dump_[name]);
6361
}
6462

6563
LPVOID Offsets::GetAddress(const std::string& name)
6664
{
67-
#ifdef API_DEBUG
6865
if (!offsets_dump_.contains(name))
6966
{
7067
Log::GetLog()->critical("Failed to get the offset of {}.", name);
7168
Log::GetLog()->flush();
7269
Sleep(10000);
7370
throw;
7471
}
75-
#endif
7672

7773
return reinterpret_cast<LPVOID>(module_base_ + static_cast<DWORD64>(offsets_dump_[name]));
7874
}
7975

8076
LPVOID Offsets::GetDataAddress(const std::string& name)
8177
{
82-
#ifdef API_DEBUG
8378
if (!offsets_dump_.contains(name))
8479
{
8580
Log::GetLog()->critical("Failed to get the offset of {}.", name);
8681
Log::GetLog()->flush();
8782
Sleep(10000);
8883
throw;
8984
}
90-
#endif
9185

9286
return reinterpret_cast<LPVOID>(data_base_ + static_cast<DWORD64>(offsets_dump_[name]));
9387
}
@@ -104,15 +98,13 @@ namespace API
10498

10599
BitField Offsets::GetBitFieldInternal(const void* base, const std::string& name)
106100
{
107-
#ifdef API_DEBUG
108101
if (!bitfields_dump_.contains(name))
109102
{
110103
Log::GetLog()->critical("Failed to get the bitfield address of {}.", name);
111104
Log::GetLog()->flush();
112105
Sleep(10000);
113106
throw;
114107
}
115-
#endif
116108

117109
const auto bf = bitfields_dump_[name];
118110
auto cf = BitField();

AsaApi/Core/Private/PluginManager/PluginManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ namespace API
297297
if (save_world)
298298
{
299299
Log::GetLog()->info("Saving world before reloading plugins");
300-
AsaApi::GetApiUtils().GetShooterGameMode()->SaveWorld(true);
300+
AsaApi::GetApiUtils().GetShooterGameMode()->SaveWorld(true, true);
301301
Log::GetLog()->info("World saved.");
302302

303303
save_world = false; // do not save again if multiple plugins are reloaded in this loop

0 commit comments

Comments
 (0)