Skip to content

Commit

Permalink
Add support for other engine binaries in game configs (#1414). (#1626)
Browse files Browse the repository at this point in the history
* Add support for other engine binaries in game configs (#1414).

* Add engine bin path for CRC bin lookup and filter out addons from GAMEBIN.

* MAX_PATH -> PLATFORM_MAX_PATH.

* Fix library lookup on Linux.

Before this, there was a bad assumption that, like on Windows, POSIX module
handle pointers were within the module's address space (and thus usable
with dladdr). That's not true!

Instead, to get a usable address on all platforms, we'll do a lookup of the
CreateInterface function that exists in all modules. This also has the
(arguable) benefit of further locking this implementation to modules owned
by the game.

To get a valid address inside the module now on both p
  • Loading branch information
psychonic committed Dec 28, 2022
1 parent 7e94bfb commit ecb707e
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 84 deletions.
5 changes: 0 additions & 5 deletions bridge/include/CoreProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,6 @@ class CoreProvider
const char *gamesuffix;
/* Data */
ServerGlobals *serverGlobals;
void * serverFactory;
void * engineFactory;
void * matchmakingDSFactory;
void * soundemittersystemFactory;
void * vscriptFactory;
SMGlobalClass * listeners;

// ConVar functions.
Expand Down
1 change: 1 addition & 0 deletions bridge/include/IFileSystemBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class IFileSystemBridge
virtual void RenameFile(char const *pOldPath, char const *pNewPath, const char *pathID = 0) = 0;
virtual bool IsDirectory(const char *pFileName, const char *pathID = 0) = 0;
virtual void CreateDirHierarchy(const char *path, const char *pathID = 0) = 0;
virtual int GetSearchPath(const char* pathID, bool bGetPackFiles, char* pPath, int nMaxLen) = 0;
};

} // namespace SourceMod
Expand Down
156 changes: 115 additions & 41 deletions core/logic/GameConfigs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "common_logic.h"
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include <sh_list.h>
#include <sh_string.h>
#include "GameConfigs.h"
Expand All @@ -46,6 +47,7 @@
#include <am-string.h>
#include <bridge/include/ILogger.h>
#include <bridge/include/CoreProvider.h>
#include <bridge/include/IFileSystemBridge.h>

#if defined PLATFORM_POSIX
#include <dlfcn.h>
Expand Down Expand Up @@ -103,8 +105,6 @@ struct TempSigInfo
char sig[1024];
char library[64];
} s_TempSig;
unsigned int s_ServerBinCRC;
bool s_ServerBinCRC_Ok = false;

static bool DoesGameMatch(const char *value)
{
Expand Down Expand Up @@ -303,38 +303,21 @@ SMCResult CGameConfig::ReadSMC_NewSection(const SMCStates *states, const char *n
{
char error[255];
error[0] = '\0';
if (strcmp(name, "server") != 0)
GameBinaryInfo binInfo;
if (!g_GameConfigs.TryGetGameBinaryInfo(name, &binInfo))
{
ke::SafeSprintf(error, sizeof(error), "Unrecognized library \"%s\"", name);
}
else if (!s_ServerBinCRC_Ok)
else if (!binInfo.m_crcOK)
{
FILE *fp;
char path[PLATFORM_MAX_PATH];

char binName[64];
bridge->FormatSourceBinaryName(name, binName, sizeof(binName));

g_pSM->BuildPath(Path_Game, path, sizeof(path), "bin/%s", binName);
if ((fp = fopen(path, "rb")) == NULL)
{
ke::SafeSprintf(error, sizeof(error), "Could not open binary: %s", path);
} else {
size_t size;
void *buffer;

fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);

buffer = malloc(size);
fread(buffer, size, 1, fp);
s_ServerBinCRC = UTIL_CRC32(buffer, size);
free(buffer);
s_ServerBinCRC_Ok = true;
fclose(fp);
}
ke::SafeSprintf(error, sizeof(error), "Could not get CRC for binary: %s", name);
}
else
{
bCurrentBinCRC_Ok = binInfo.m_crcOK;
bCurrentBinCRC = binInfo.m_crc;
}

if (error[0] != '\0')
{
m_IgnoreLevel = 1;
Expand Down Expand Up @@ -461,12 +444,12 @@ SMCResult CGameConfig::ReadSMC_KeyValue(const SMCStates *states, const char *key
}
} else if (m_ParseState == PSTATE_GAMEDEFS_CRC_BINARY) {
if (DoesPlatformMatch(key)
&& s_ServerBinCRC_Ok
&& bCurrentBinCRC_Ok
&& !bShouldBeReadingDefault)
{
unsigned int crc = 0;
sscanf(value, "%08X", &crc);
if (s_ServerBinCRC == crc)
if (bCurrentBinCRC == crc)
{
bShouldBeReadingDefault = true;
}
Expand Down Expand Up @@ -603,18 +586,12 @@ SMCResult CGameConfig::ReadSMC_LeavingSection(const SMCStates *states)
strncopy(s_TempSig.library, "server", sizeof(s_TempSig.library));
}
void *addrInBase = NULL;
if (strcmp(s_TempSig.library, "server") == 0)
GameBinaryInfo binInfo;
if (g_GameConfigs.TryGetGameBinaryInfo(s_TempSig.library, &binInfo))
{
addrInBase = bridge->serverFactory;
} else if (strcmp(s_TempSig.library, "engine") == 0) {
addrInBase = bridge->engineFactory;
} else if (strcmp(s_TempSig.library, "matchmaking_ds") == 0) {
addrInBase = bridge->matchmakingDSFactory;
} else if (strcmp(s_TempSig.library, "soundemittersystem") == 0) {
addrInBase = bridge->soundemittersystemFactory;
} else if (strcmp(s_TempSig.library, "vscript") == 0) {
addrInBase = bridge->vscriptFactory;
addrInBase = binInfo.m_pAddr;
}

void *final_addr = NULL;
if (addrInBase == NULL)
{
Expand Down Expand Up @@ -1116,6 +1093,30 @@ GameConfigManager::~GameConfigManager()

void GameConfigManager::OnSourceModStartup(bool late)
{
char search_path[PLATFORM_MAX_PATH * 8];
bridge->filesystem->GetSearchPath("GAMEBIN", false, search_path, sizeof(search_path));

char addons_folder[12];
ke::SafeSprintf(addons_folder, sizeof(addons_folder), "%caddons%c", PLATFORM_SEP_CHAR, PLATFORM_SEP_CHAR);

std::istringstream iss(search_path);
for (std::string path; std::getline(iss, path, ';');)
{
if (path.length() > 0
&& path.find(addons_folder) == std::string::npos
&& m_gameBinDirectories.find(path.c_str()) == m_gameBinDirectories.cend()
)
m_gameBinDirectories.insert(path);
}

bridge->filesystem->GetSearchPath("EXECUTABLE_PATH", false, search_path, sizeof(search_path));
std::istringstream iss2(search_path);
for (std::string path; std::getline(iss2, path, ';');)
{
if (m_gameBinDirectories.find(path.c_str()) == m_gameBinDirectories.cend())
m_gameBinDirectories.insert(path);
}

LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);

strncopy(g_Game, g_pSM->GetGameFolderName(), sizeof(g_Game));
Expand Down Expand Up @@ -1238,3 +1239,76 @@ void GameConfigManager::RemoveCachedConfig(CGameConfig *config)
{
m_Lookup.remove(config->m_File);
}

void GameConfigManager::CacheGameBinaryInfo(const char* pszName)
{
GameBinaryInfo info;

char name[64];
bridge->FormatSourceBinaryName(pszName, name, sizeof(name));

bool binary_found = false;
char binary_path[PLATFORM_MAX_PATH];
for (auto it = m_gameBinDirectories.begin(); it != m_gameBinDirectories.end(); ++it)
{
ke::SafeSprintf(binary_path, sizeof(binary_path), "%s%s%s", it->c_str(), it->back() == PLATFORM_SEP_CHAR ? "" : PLATFORM_SEP, name);
#if defined PLATFORM_WINDOWS
HMODULE hModule = LoadLibraryA(binary_path);
if (hModule)
{
info.m_pAddr = GetProcAddress(hModule, "CreateInterface");
FreeLibrary(hModule);
}
#else
void *pHandle = dlopen(binary_path, RTLD_NOW);
if (pHandle)
{
info.m_pAddr = dlsym(pHandle, "CreateInterface");
dlclose(pHandle);
}
#endif

if (info.m_pAddr)
break;
}

// Don't bother trying to get CRC if we couldn't find the bin loaded
if (info.m_pAddr)
{
FILE *fp;

if ((fp = fopen(binary_path, "rb")) == 0)
{
info.m_crc = 0;
}
else
{
size_t size;
void* buffer;

fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);

buffer = malloc(size);
fread(buffer, size, 1, fp);
info.m_crc = UTIL_CRC32(buffer, size);
free(buffer);
info.m_crcOK = true;
fclose(fp);
}
}

// But insert regardless, to cache the first lookup (even as failed)
m_gameBinInfos.insert(pszName, info);
}

bool GameConfigManager::TryGetGameBinaryInfo(const char* pszName, GameBinaryInfo* pDest)
{
if (m_gameBinInfos.retrieve(pszName, pDest))
return pDest->m_pAddr != nullptr;

CacheGameBinaryInfo(pszName);

return m_gameBinInfos.retrieve(pszName, pDest);
}
15 changes: 15 additions & 0 deletions core/logic/GameConfigs.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <am-refcounting.h>
#include <sm_stringhashmap.h>
#include <sm_namehashset.h>
#include <set>

using namespace SourceMod;

Expand Down Expand Up @@ -91,6 +92,8 @@ class CGameConfig :
std::string m_offset;
std::string m_Game;
std::string m_Key;
unsigned int bCurrentBinCRC;
bool bCurrentBinCRC_Ok = false;
bool bShouldBeReadingDefault;
bool had_game;
bool matched_game;
Expand Down Expand Up @@ -126,6 +129,13 @@ class CGameConfig :
time_t m_ModTime;
};

struct GameBinaryInfo
{
void *m_pAddr = nullptr;
uint32_t m_crc = 0;
bool m_crcOK = false;
};

class GameConfigManager :
public IGameConfigManager,
public SMGlobalClass
Expand All @@ -148,9 +158,14 @@ class GameConfigManager :
void OnSourceModAllInitialized();
void OnSourceModAllShutdown();
public:
bool TryGetGameBinaryInfo(const char* pszName, GameBinaryInfo* pDest);
void RemoveCachedConfig(CGameConfig *config);
private:
void CacheGameBinaryInfo(const char* pszName);
private:
NameHashSet<CGameConfig *> m_Lookup;
StringHashMap<GameBinaryInfo> m_gameBinInfos;
std::set<std::string> m_gameBinDirectories;
public:
StringHashMap<ITextListener_SMC *> m_customHandlers;
};
Expand Down
Loading

0 comments on commit ecb707e

Please sign in to comment.