Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f046ab7
Headers updates for 89.31 and various other things
Lethalinjectionx Jul 4, 2026
8515267
Quick dirty fixes for headers and TSoftObjectPtr ctor
Pelayori Jul 10, 2026
0f5ac40
Revert array inline changes
Pelayori Jul 10, 2026
b583eea
Minor changes for headers
Lethalinjectionx Jul 10, 2026
8611bcf
Static Class changes
Pelayori Jul 10, 2026
5b1a2c3
Resolve merge
Pelayori Jul 10, 2026
b4fc5ef
header update
Lethalinjectionx Jul 10, 2026
10d5d53
fixed offset issue
Lethalinjectionx Jul 11, 2026
5893929
StaticStruct fixes
Lethalinjectionx Jul 11, 2026
2c7aa94
StaticStruct update in headers
Pelayori Jul 11, 2026
c12b0a3
header clean up
Lethalinjectionx Jul 11, 2026
9cb7075
header fixes
Lethalinjectionx Jul 11, 2026
38168fd
header fixes
Lethalinjectionx Jul 11, 2026
c6ff9c7
cache changes
Lethalinjectionx Jul 11, 2026
692415a
Add HTTP fallback to prevent 126
salmonrider Jul 12, 2026
4bcfaf0
Merge pull request #62 from salmonrider/89.31_Changes
Pelayori Jul 12, 2026
ec98910
Bump version
Pelayori Jul 12, 2026
42e12eb
AShooterPlayerState.GetPlayerName() no longer available
Lethalinjectionx Jul 12, 2026
67fa176
oddball fix for AShooterCharacter.GetPrivateStaticClass missing
Lethalinjectionx Jul 12, 2026
fba0c72
Fix APrimalStructureUnderwaterBase Static Class and example of how to…
Pelayori Jul 13, 2026
d8938e6
Fix sparse class methods
Pelayori Jul 13, 2026
98a4f59
Update API headers
RIO-Alex Jul 13, 2026
72a0bfe
Merge pull request #63 from RIO-Alex/89.31_Changes
Pelayori Jul 13, 2026
9e9b962
Update summon
Pelayori Jul 13, 2026
7223fb9
Merge branch '89.31_Changes' of https://github.com/ArkServerApi/AsaAp…
Pelayori Jul 13, 2026
7956bd0
Added GetNumLevelUpsAvailable and BPAllowAddInventoryItem
ohmcodes Jul 13, 2026
2973ffb
Deprecated bool IsTargeting()
ohmcodes Jul 14, 2026
acb214d
Merge pull request #67 from ohmcodes/89.31_Changes
Pelayori Jul 14, 2026
1d4f7b0
header cleanup
Lethalinjectionx Jul 15, 2026
c9d58c7
cached_offsets.txt will be extracted from the zip now
Lethalinjectionx Jul 15, 2026
d3f03ba
Update IsDead offset
Pelayori Jul 18, 2026
1a438cf
Merge branch '89.31_Changes' of https://github.com/ArkServerApi/AsaAp…
Pelayori Jul 18, 2026
353d8ee
Bump version
Pelayori Jul 18, 2026
dc5bab8
Updated ServerID generation to hopefully give the best possible chanc…
Lethalinjectionx Jul 18, 2026
0c5ab85
Added a fallback instead of throwing if the new number generation fai…
Lethalinjectionx Jul 18, 2026
582e438
Add missing math functions and update APrimalStructure offset
RIO-Alex Jul 20, 2026
aa61bd0
Multiple failover URLs in case main one is blocked for cache
Pelayori Jul 23, 2026
2699871
Merge pull request #75 from RIO-Alex/89.31_Changes
Pelayori Jul 23, 2026
12e650d
Merge branch '89.31_Changes' of https://github.com/ArkServerApi/AsaAp…
Pelayori Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
744 changes: 633 additions & 111 deletions AsaApi/Core/Private/Ark/ArkBaseApi.cpp

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion AsaApi/Core/Private/Ark/ArkBaseApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ namespace API

nlohmann::json GetConfig();
private:
bool DownloadCacheFiles(const std::filesystem::path downloadFile, const std::filesystem::path localFile);
bool DownloadCacheFiles(
const std::filesystem::path downloadFile,
const std::filesystem::path localFile,
std::filesystem::path& extractedCacheDirectory,
std::string& downloadedTimestamp);

// Callbacks
static FString LoadPlugin(FString* cmd);
Expand Down
48 changes: 37 additions & 11 deletions AsaApi/Core/Private/Ark/HooksImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,32 @@ namespace AsaApi
Log::GetLog()->info("Initialized hooks\n");
}

std::uint32_t GenerateServerId(uint32_t originalFallbackNumber)
{
constexpr std::uint32_t MinId = 100'000'000;
constexpr std::uint64_t IdCount = 900'000'000ULL;
constexpr std::uint64_t RandomValueCount = 1ULL << 32;
constexpr std::uint64_t AcceptLimit = (RandomValueCount / IdCount) * IdCount;

while (true)
{
std::uint32_t randomValue = 0;

const NTSTATUS status = BCryptGenRandom(
nullptr,
reinterpret_cast<PUCHAR>(&randomValue),
sizeof(randomValue),
BCRYPT_USE_SYSTEM_PREFERRED_RNG
);

if (!BCRYPT_SUCCESS(status))
return originalFallbackNumber; // Fallback to the original number if random generation fails

if (static_cast<std::uint64_t>(randomValue) < AcceptLimit)
return MinId + static_cast<std::uint32_t>(static_cast<std::uint64_t>(randomValue) % IdCount);
}
}

// Hooks

void Hook_UEngine_Init(DWORD64 _this, DWORD64 InEngineLoop)
Expand Down Expand Up @@ -100,11 +126,11 @@ namespace AsaApi
if (bp.Equals("Blueprint'/Script/ShooterGame.PrimalPersistentWorldData'"))
{
if (actor->TargetingTeamField() == 0)
actor->TargetingTeamField() = a_shooter_game_mode->ServerIDField();
actor->TargetingTeamField() = GenerateServerId(a_shooter_game_mode->ServerIDField()); // Use the original ServerIDField as a fallback if random generation fails

a_shooter_game_mode->MyServerIdField() = FString(std::to_string(actor->TargetingTeamField()));
a_shooter_game_mode->ServerIDField() = actor->TargetingTeamField();
Log::GetLog()->info("SERVER ID: {}", a_shooter_game_mode->ServerIDField());
Log::GetLog()->info("SERVER ID: {}", actor->TargetingTeamField());

break;
}
Expand All @@ -130,7 +156,7 @@ namespace AsaApi

if (command_executed || prevent_default)
return;

AShooterPlayerController_ServerSendChatMessage_Impl_original(player_controller, message, mode, senderPlatform);
}

Expand Down Expand Up @@ -161,17 +187,17 @@ namespace AsaApi
Commands* command = dynamic_cast<Commands*>(API::game_api->GetCommands().get());
if (command)
command->CheckOnTimerCallbacks();

API::PluginManager::DetectPluginChangesTimerCallback(); // We call this here to avoid UnknownModule crashes

AGameState_DefaultTimer_original(_this);
}

void Hook_AShooterGameMode_BeginPlay(AShooterGameMode* _AShooterGameMode)
{
AShooterGameMode_BeginPlay_original(_AShooterGameMode);
dynamic_cast<ApiUtils&>(*API::game_api->GetApiUtils()).SetStatus(ServerStatus::Ready);
void Hook_AShooterGameMode_BeginPlay(AShooterGameMode* _AShooterGameMode)
{
AShooterGameMode_BeginPlay_original(_AShooterGameMode);
dynamic_cast<ApiUtils&>(*API::game_api->GetApiUtils()).SetStatus(ServerStatus::Ready);

std::uint64_t mask = 0;

LPWSTR* argv;
Expand Down Expand Up @@ -226,7 +252,7 @@ namespace AsaApi
SetProcessAffinityMask(GetCurrentProcess(), mask);
Log::GetLog()->info("Set process affinity mask to 0x{:016X}", mask);
}
}
}

bool Hook_URCONServer_Init(URCONServer* _this, FString* Password, unsigned int InPort, UShooterCheatManager* SCheatManager)
{
Expand Down
9 changes: 9 additions & 0 deletions AsaApi/Core/Private/Ark/HooksImpl.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
#pragma once
#include <Windows.h>
#include <bcrypt.h>

#include <cstdint>
#include <stdexcept>

#pragma comment(lib, "bcrypt.lib")

namespace AsaApi
{
void InitHooks();

std::uint32_t GenerateServerId();
} // namespace AsaApi
100 changes: 81 additions & 19 deletions AsaApi/Core/Private/Cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "Cache.h"

#include <array>
#include <unordered_map>
#include <string>
#include <cstdint>
Expand All @@ -18,14 +19,6 @@ namespace Cache
return "";
}

const auto fileSize = std::filesystem::file_size(filename);
std::vector<char> buffer(fileSize);

if (!file.read(buffer.data(), fileSize)) {
Log::GetLog()->error("Error reading file for SHA-256 calculation: " + filename.string());
return "";
}

std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> mdctx(EVP_MD_CTX_new(), &EVP_MD_CTX_free);
if (mdctx == nullptr) {
Log::GetLog()->error("Error creating EVP_MD_CTX");
Expand All @@ -37,8 +30,21 @@ namespace Cache
return "";
}

if (EVP_DigestUpdate(mdctx.get(), buffer.data(), fileSize) != 1) {
Log::GetLog()->error("Error updating SHA-256 context");
std::array<char, 64 * 1024> buffer{};
while (file)
{
file.read(buffer.data(), static_cast<std::streamsize>(buffer.size()));
const std::streamsize bytesRead = file.gcount();
if (bytesRead > 0 && EVP_DigestUpdate(mdctx.get(), buffer.data(), static_cast<std::size_t>(bytesRead)) != 1)
{
Log::GetLog()->error("Error updating SHA-256 context");
return "";
}
}

if (!file.eof())
{
Log::GetLog()->error("Error reading file for SHA-256 calculation: " + filename.string());
return "";
}

Expand All @@ -60,16 +66,71 @@ namespace Cache
return result;
}

void saveToFile(const std::filesystem::path& filename, const std::string& content)
bool saveToFile(const std::filesystem::path& filename, const std::string& content)
{
std::ofstream file(filename, std::ios::binary | std::ios::trunc);
if (file.is_open()) {
file.write(content.data(), content.size());
file.close();
return;
std::filesystem::path temporaryFile = filename;
temporaryFile += ".tmp";

std::error_code error;
std::filesystem::remove(temporaryFile, error);

std::ofstream file(temporaryFile, std::ios::binary | std::ios::trunc);
if (!file.is_open())
{
Log::GetLog()->error("Error opening file for writing: " + temporaryFile.string());
return false;
}

Log::GetLog()->error("Error opening file for writing: " + filename.string());
file.write(content.data(), static_cast<std::streamsize>(content.size()));
file.flush();
file.close();
const bool writeSucceeded = !file.fail();

if (!writeSucceeded)
{
Log::GetLog()->error("Error writing file: " + temporaryFile.string());
std::filesystem::remove(temporaryFile, error);
return false;
}

HANDLE temporaryFileHandle = CreateFileW(
temporaryFile.c_str(),
GENERIC_WRITE,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (temporaryFileHandle == INVALID_HANDLE_VALUE)
{
Log::GetLog()->error("Error opening temporary metadata for flushing: " + temporaryFile.string());
std::filesystem::remove(temporaryFile, error);
return false;
}

const bool flushSucceeded = FlushFileBuffers(temporaryFileHandle) != FALSE;
const DWORD flushError = flushSucceeded ? ERROR_SUCCESS : GetLastError();
CloseHandle(temporaryFileHandle);
if (!flushSucceeded)
{
Log::GetLog()->error(
"Error flushing temporary metadata: " + temporaryFile.string() + " (" + std::to_string(flushError) + ")");
std::filesystem::remove(temporaryFile, error);
return false;
}

if (!MoveFileExW(
temporaryFile.c_str(),
filename.c_str(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
{
Log::GetLog()->error(
"Error replacing file: " + filename.string() + " (" + std::to_string(GetLastError()) + ")");
std::filesystem::remove(temporaryFile, error);
return false;
}

return true;
}

std::string readFromFile(const std::filesystem::path& filename)
Expand All @@ -80,7 +141,8 @@ namespace Cache
file.seekg(0, std::ios::end);
content.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(&content[0], content.size());
if (!content.empty())
file.read(content.data(), static_cast<std::streamsize>(content.size()));
return content;
}

Expand Down Expand Up @@ -127,4 +189,4 @@ namespace Cache
file.close();
return set;
}
}
}
Loading