diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b9a417..574afe0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: branches: - main - master + - dev tags: - '*' paths-ignore: @@ -34,11 +35,11 @@ jobs: - windows-latest mm_version: - - "1.12" + - "master" include: - - mm_version: "1.12" - mm_branch: "1.12-dev" + - mm_version: "master" + mm_branch: "master" - os: ubuntu-latest os_short: linux @@ -105,6 +106,7 @@ jobs: uses: actions/checkout@v7 with: path: src + submodules: recursive - name: Compiling ${{ github.event.repository.name }}-l4d files working-directory: src diff --git a/AMBuildScript b/AMBuildScript index 0e55380..1a23052 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -22,14 +22,6 @@ def ResolveEnvPath(env, folder): def Normalize(path): return os.path.abspath(os.path.normpath(path)) -def SetArchFlags(compiler): - if compiler.behavior == 'gcc': - if compiler.target.arch == 'x86_64': - compiler.cflags += ['-fPIC'] - elif compiler.like('msvc'): - if compiler.target.arch == 'x86_64': - compiler.defines += ['WIN64'] - hl2sdk_manifests_path = None # First we check if the manifest exists in the current path @@ -61,6 +53,8 @@ class ExtensionConfig(object): self.mms_root = None self.all_targets = [] self.target_archs = set() + self.libsafetyhook = {} + self.libkhook = {} if builder.options.targets: target_archs = builder.options.targets.split(',') @@ -112,7 +106,7 @@ class ExtensionConfig(object): if builder.options.mms_path: self.mms_root = builder.options.mms_path else: - self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12') + self.mms_root = ResolveEnvPath('MMSOURCE20', 'mmsource-2.0') if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE', 'metamod-source') if not self.mms_root: @@ -186,12 +180,12 @@ class ExtensionConfig(object): '-Wno-unknown-pragmas', '-Wno-dangling-else', '-fvisibility=hidden', + '-msse', + '-fPIC', ] - if cxx.target.arch in ['x86', 'x86_64']: - cxx.cflags += ['-msse'] - cxx.cxxflags += [ + '-fno-rtti', '-fno-threadsafe-statics', '-Wno-non-virtual-dtor', '-Wno-overloaded-virtual', @@ -306,6 +300,7 @@ class ExtensionConfig(object): cxx.linkflags += ['-static-libgcc'] elif cxx.family == 'clang': cxx.linkflags += ['-lgcc_eh'] + cxx.linkflags += ['-Wl,--exclude-libs,ALL'] def configure_mac(self, cxx): cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL'] @@ -343,38 +338,34 @@ class ExtensionConfig(object): def Library(self, context, compiler, name): compiler = compiler.clone() - SetArchFlags(compiler) return self.LibraryBuilder(compiler, name) def Program(self, context, compiler, name): compiler = compiler.clone() - SetArchFlags(compiler) return self.ProgramBuilder(compiler, name) def StaticLibrary(self, context, compiler, name): compiler = compiler.clone() - SetArchFlags(compiler) return self.StaticLibraryBuilder(compiler, name) def ConfigureForExtension(self, context, compiler): compiler.cxxincludes += [ os.path.join(context.currentSourcePath), + os.path.join(self.sm_root, 'core', 'logic'), ] return compiler def ExtLibrary(self, context, compiler, name): binary = self.Library(context, compiler, name) - SetArchFlags(compiler) self.ConfigureForExtension(context, binary.compiler) return binary def ConfigureForHL2(self, context, binary, sdk): compiler = binary.compiler - SetArchFlags(compiler) compiler.cxxincludes += [ os.path.join(self.mms_root, 'core'), - os.path.join(self.mms_root, 'core', 'sourcehook'), + os.path.join(context.currentSourcePath, 'khook', 'include'), ] for other_sdk in self.sdk_manifests: @@ -417,4 +408,5 @@ if builder.backend == 'amb2': 'PackageScript', ] +builder.Build(['khook/AMBuilder', 'khook/third_party/safetyhook/AMBuilder'], { 'KHook': Extension, 'SafetyHook': Extension }) builder.Build(BuildScripts, { 'Extension': Extension }) diff --git a/AMBuilder b/AMBuilder index b30ffc3..3dd62bc 100644 --- a/AMBuilder +++ b/AMBuilder @@ -6,7 +6,7 @@ project.sources += [ 'l4dtoolz_mm.cpp', 'memutils.cpp', ] - + for sdk_name in Extension.sdks: sdk = Extension.sdks[sdk_name] if sdk['name'] in ['mock']: @@ -17,5 +17,14 @@ for sdk_name in Extension.sdks: continue binary = Extension.HL2Config(project, builder, cxx, 'l4dtoolz_mm', sdk) + binary.compiler.cxxincludes += [os.path.join(builder.sourcePath, 'khook', 'include')] + + binary.compiler.defines += ['KHOOK_STANDALONE', 'KHOOK_EXPORT'] + for task in Extension.libkhook: + if task.target.arch == binary.compiler.target.arch: + binary.compiler.linkflags += [task.binary] + for task in Extension.libsafetyhook: + if task.target.arch == binary.compiler.target.arch: + binary.compiler.linkflags += [task.binary] Extension.extensions += builder.Add(project) diff --git a/game_offsets.h b/game_offsets.h index c8dab39..75327a4 100644 --- a/game_offsets.h +++ b/game_offsets.h @@ -1,7 +1,7 @@ #ifndef _INCLUDE_GAME_OFFSETS_ #define _INCLUDE_GAME_OFFSETS_ -#if SH_SYS == SH_SYS_WIN32 +#if defined WIN32 #if SOURCE_ENGINE == SE_LEFT4DEAD const int sv_offs = 6; // IServer pointer (in IVEngineServer::CreateFakeClient) const int maxhuman_idx = 131; // CTerrorGameRules::GetMaxHumanPlayers vtable diff --git a/l4dtoolz_mm.cpp b/l4dtoolz_mm.cpp index c5d1fa2..8f68bd9 100644 --- a/l4dtoolz_mm.cpp +++ b/l4dtoolz_mm.cpp @@ -1,11 +1,9 @@ #include "l4dtoolz_mm.h" #include "game_offsets.h" -#include "baseserver.h" #include "memutils.h" #include "icommandline.h" #include "server_class.h" -#include "sourcehook.h" -#include "matchmaking/imatchframework.h" +#include "khook.hpp" l4dtoolz g_l4dtoolz; IServerGameDLL* gamedll = NULL; @@ -14,19 +12,24 @@ IVEngineServer* engine = NULL; IMatchFramework* g_pMatchFramework = NULL; ICvar* g_pCVar = NULL; CBaseServer* g_pGameIServer = NULL; -void* g_pGameRules = nullptr; +CGameRules* g_pGameRules = nullptr; int g_nGameSlots = -1; -SH_DECL_HOOK1_void(IServerGameDLL, ApplyGameSettings, SH_NOATTRIB, 0, KeyValues*); -SH_DECL_HOOK0(IMatchTitle, GetTotalNumPlayersSupported, SH_NOATTRIB, 0, int); -SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, char const *, char const *, char const *, char const *, bool, bool); -SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, 0); -SH_DECL_MANUALHOOK0(CTerrorGameRules_GetMaxHumanPlayers, maxhuman_idx, 0, 0, int); -SH_DECL_HOOK2_void(CBaseServer, ReplyReservationRequest, SH_NOATTRIB, 0, netadr_t&, bf_read&); - ConVar sv_maxplayers("sv_maxplayers", "-1", FCVAR_SPONLY|FCVAR_NOTIFY, "Max Human Players", true, -1, true, 32, l4dtoolz::OnChangeMaxplayers); ConVar sv_force_unreserved("sv_force_unreserved", "0", FCVAR_SPONLY|FCVAR_NOTIFY, "Disallow lobby reservation cookie", true, 0, true, 1, l4dtoolz::OnChangeUnreserved); + +l4dtoolz::l4dtoolz() : + m_ApplyGameSettings(&IServerGameDLL::ApplyGameSettings, this, nullptr, &l4dtoolz::ApplyGameSettings), + m_GetTotalNumPlayersSupported(&IMatchTitle::GetTotalNumPlayersSupported, this, &l4dtoolz::GetTotalNumPlayersSupported, nullptr), + m_LevelInit(&IServerGameDLL::LevelInit, this, nullptr, &l4dtoolz::LevelInit), + m_LevelShutdown(&IServerGameDLL::LevelShutdown, this, &l4dtoolz::LevelShutdown, nullptr), + m_ReplyReservationRequest(&CBaseServer::ReplyReservationRequest, this, &l4dtoolz::ReplyReservationRequest, nullptr) +{ + g_HookGetMaxHumanPlayers.Configure(maxhuman_idx); + g_HookGetMaxHumanPlayers.AddContext(this, &l4dtoolz::GetMaxHumanPlayers, nullptr); +} + void l4dtoolz::OnChangeMaxplayers(IConVar *var, const char *pOldValue, float flOldValue) { int new_value = ((ConVar*)var)->GetInt(); @@ -60,36 +63,45 @@ void l4dtoolz::OnChangeUnreserved(IConVar *var, const char *pOldValue, float flO } } -void Hook_ApplyGameSettings(KeyValues *pKV) +KHook::Return l4dtoolz::ApplyGameSettings(IServerGameDLL*, KeyValues *pKV) { if (!pKV) { - return; + return { KHook::Action::Ignore }; } g_nGameSlots = sv_maxplayers.GetInt(); if (g_nGameSlots == -1) { - return; + return { KHook::Action::Ignore }; } pKV->SetInt("members/numSlots", g_nGameSlots); + return { KHook::Action::Ignore }; } -void Hook_ReplyReservationRequest(netadr_t& adr, bf_read& inmsg) +KHook::Return l4dtoolz::ReplyReservationRequest(CBaseServer*, netadr_t& adr, bf_read& inmsg) { if (sv_force_unreserved.GetInt()) { if (g_pGameIServer != NULL) { if (g_pGameIServer->m_nReservationCookie != 0) - RETURN_META(MRES_IGNORED); + return { KHook::Action::Ignore }; } - RETURN_META(MRES_SUPERCEDE); + return { KHook::Action::Supersede }; } - RETURN_META(MRES_IGNORED); + return { KHook::Action::Ignore }; } -int Hook_GetMaxHumanPlayers() +KHook::Return l4dtoolz::GetTotalNumPlayersSupported(IMatchTitle*) { if (g_nGameSlots > 0) { - RETURN_META_VALUE(MRES_SUPERCEDE, g_nGameSlots); + return { KHook::Action::Supersede, g_nGameSlots }; } - RETURN_META_VALUE(MRES_IGNORED, g_nGameSlots); + return { KHook::Action::Ignore, g_nGameSlots }; +} + +KHook::Return l4dtoolz::GetMaxHumanPlayers(CGameRules*) +{ + if (g_nGameSlots > 0) { + return { KHook::Action::Supersede, g_nGameSlots }; + } + return { KHook::Action::Ignore, g_nGameSlots }; } PLUGIN_EXPOSE(l4dtoolz, g_l4dtoolz); @@ -105,8 +117,8 @@ bool l4dtoolz::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); void* handle = NULL; -#if SH_SYS == SH_SYS_WIN32 - if (!(handle=SH_GET_ORIG_VFNPTR_ENTRY(engine, &IVEngineServer::CreateFakeClient))) { +#if defined WIN32 + if (!(handle=KHook::FindOriginalVirtual(*(void***)engine, KHook::GetVtableIndex(&IVEngineServer::CreateFakeClient)))) { Warning("Failed to get address 'IVEngineServer::CreateFakeClient'\n"); } else { g_pGameIServer = *reinterpret_cast(reinterpret_cast(handle)+sv_offs); @@ -125,13 +137,13 @@ bool l4dtoolz::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool } } - SH_ADD_HOOK(IServerGameDLL, ApplyGameSettings, gamedll, SH_STATIC(Hook_ApplyGameSettings), true); - SH_ADD_HOOK(IMatchTitle, GetTotalNumPlayersSupported, g_pMatchFramework->GetMatchTitle(), SH_STATIC(Hook_GetMaxHumanPlayers), false); - SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &l4dtoolz::LevelInit, true); - SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &l4dtoolz::LevelShutdown, false); + m_ApplyGameSettings.Add(gamedll); + m_GetTotalNumPlayersSupported.Add(g_pMatchFramework->GetMatchTitle()); + m_LevelInit.Add(gamedll); + m_LevelShutdown.Add(gamedll); if (g_pGameIServer) { - SH_ADD_HOOK(CBaseServer, ReplyReservationRequest, g_pGameIServer, SH_STATIC(Hook_ReplyReservationRequest), false); + m_ReplyReservationRequest.Add(g_pGameIServer); } else { Warning("g_pGameIServer pointer is not available\n"); } @@ -143,22 +155,22 @@ bool l4dtoolz::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool bool l4dtoolz::Unload(char *error, size_t maxlen) { - SH_REMOVE_HOOK(IServerGameDLL, ApplyGameSettings, gamedll, SH_STATIC(Hook_ApplyGameSettings), true); - SH_REMOVE_HOOK(IMatchTitle, GetTotalNumPlayersSupported, g_pMatchFramework->GetMatchTitle(), SH_STATIC(Hook_GetMaxHumanPlayers), false); - SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &l4dtoolz::LevelInit, true); - SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &l4dtoolz::LevelShutdown, false); + m_ApplyGameSettings.Remove(gamedll); + m_GetTotalNumPlayersSupported.Remove(g_pMatchFramework->GetMatchTitle()); + m_LevelInit.Remove(gamedll); + m_LevelShutdown.Remove(gamedll); if (g_pGameIServer) { - SH_REMOVE_HOOK(CBaseServer, ReplyReservationRequest, g_pGameIServer, SH_STATIC(Hook_ReplyReservationRequest), false); + m_ReplyReservationRequest.Remove(g_pGameIServer); } - LevelShutdown(); + LevelShutdown(gamedll); ConVar_Unregister(); return true; } -bool l4dtoolz::LevelInit(const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) +KHook::Return l4dtoolz::LevelInit(IServerGameDLL*, const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) { g_pGameRules = nullptr; @@ -168,27 +180,29 @@ bool l4dtoolz::LevelInit(const char *pMapName, char const *pMapEntities, char co iCount = pServerClass->m_pTable->GetNumProps(); for (i = 0; i < iCount; i++) { if (stricmp(pServerClass->m_pTable->GetProp(i)->GetName(), "terror_gamerules_data") == 0) { - g_pGameRules = (*pServerClass->m_pTable->GetProp(i)->GetDataTableProxyFn())(NULL, NULL, NULL, NULL, 0); + g_pGameRules = reinterpret_cast((*pServerClass->m_pTable->GetProp(i)->GetDataTableProxyFn())(NULL, NULL, NULL, NULL, 0)); break; } } } if (g_pGameRules) { - SH_ADD_MANUALHOOK(CTerrorGameRules_GetMaxHumanPlayers, g_pGameRules, SH_STATIC(Hook_GetMaxHumanPlayers), false); + g_HookGetMaxHumanPlayers.Add(g_pGameRules); } else { Warning("g_pGameRules pointer is not available\n"); } - return true; + return { KHook::Action::Ignore, true }; } -void l4dtoolz::LevelShutdown() +KHook::Return l4dtoolz::LevelShutdown(IServerGameDLL*) { if (g_pGameRules) { - SH_REMOVE_MANUALHOOK(CTerrorGameRules_GetMaxHumanPlayers, g_pGameRules, SH_STATIC(Hook_GetMaxHumanPlayers), false); + g_HookGetMaxHumanPlayers.Remove(g_pGameRules); g_pGameRules = nullptr; } + + return { KHook::Action::Ignore }; } ServerClass *UTIL_FindServerClass(const char *classname) diff --git a/l4dtoolz_mm.h b/l4dtoolz_mm.h index abc0790..850da8b 100644 --- a/l4dtoolz_mm.h +++ b/l4dtoolz_mm.h @@ -2,14 +2,23 @@ #define _INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_ #include +#include "baseserver.h" +#include "matchmaking/imatchframework.h" + +class CGameRules; class l4dtoolz : public ISmmPlugin, public IConCommandBaseAccessor { public: + l4dtoolz(); bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late); bool Unload(char *error, size_t maxlen); - bool LevelInit(const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); - void LevelShutdown(); + KHook::Return ApplyGameSettings(IServerGameDLL*, KeyValues *pKV); + KHook::Return GetTotalNumPlayersSupported(IMatchTitle*); + KHook::Return LevelInit(IServerGameDLL*, const char *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); + KHook::Return LevelShutdown(IServerGameDLL*); + KHook::Return ReplyReservationRequest(CBaseServer*, netadr_t& adr, bf_read& inmsg); + KHook::Return GetMaxHumanPlayers(CGameRules*); public: const char *GetAuthor(); const char *GetName(); @@ -19,6 +28,13 @@ public: const char *GetVersion(); const char *GetDate(); const char *GetLogTag(); + + KHook::Virtual m_ApplyGameSettings; + KHook::Virtual m_GetTotalNumPlayersSupported; + KHook::Virtual m_LevelInit; + KHook::Virtual m_LevelShutdown; + KHook::Virtual m_ReplyReservationRequest; + KHook::Virtual g_HookGetMaxHumanPlayers; public: //IConCommandBaseAccessor bool RegisterConCommandBase(ConCommandBase *pVar); public: diff --git a/memutils.cpp b/memutils.cpp index bf323bf..24d159e 100644 --- a/memutils.cpp +++ b/memutils.cpp @@ -30,8 +30,9 @@ #include "memutils.h" #include +#include -#if SH_SYS == SH_SYS_LINUX +#if defined LINUX #include #include #include @@ -40,10 +41,9 @@ #define PAGE_SIZE 4096 #define PAGE_ALIGN_UP(x) ((x + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) #define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1)) -#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC #endif -#if SH_SYS == SH_SYS_APPLE +#if defined OSX #include #include #include @@ -61,13 +61,13 @@ typedef struct task_dyld_info task_dyld_info_data_t; #define TASK_DYLD_INFO 17 #define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) #endif // MAC_OS_X_VERSION_10_6 -#endif // SH_SYS_APPLE +#endif // OSX MemoryUtils g_MemUtils; MemoryUtils::MemoryUtils() { -#if SH_SYS == SH_SYS_APPLE +#if defined OSX Gestalt(gestaltSystemVersionMajor, &m_OSXMajor); Gestalt(gestaltSystemVersionMinor, &m_OSXMinor); @@ -94,7 +94,7 @@ MemoryUtils::MemoryUtils() MemoryUtils::~MemoryUtils() { -#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE +#if defined LINUX || defined OSX for (size_t i = 0; i < m_SymTables.size(); i++) { delete m_SymTables[i]; @@ -105,11 +105,11 @@ MemoryUtils::~MemoryUtils() void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol) { -#if SH_SYS == SH_SYS_WIN32 +#if defined WIN32 return GetProcAddress((HMODULE)handle, symbol); -#elif SH_SYS == SH_SYS_LINUX +#elif defined LINUX struct link_map *dlmap; struct stat dlstat; @@ -243,7 +243,7 @@ void *MemoryUtils::ResolveSymbol(void *handle, const char *symbol) munmap(file_hdr, dlstat.st_size); return symbol_entry ? symbol_entry->address : NULL; -#elif SH_SYS == SH_SYS_APPLE +#elif defined OSX uintptr_t dlbase, linkedit_addr; uint32_t image_count; diff --git a/memutils.h b/memutils.h index b0bec24..0997e77 100644 --- a/memutils.h +++ b/memutils.h @@ -34,20 +34,19 @@ #include #include -#include "sourcehook.h" -#include "sh_memory.h" +#include "khook/memory.hpp" +#include "khook.hpp" -#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE -#include +#if defined LINUX || defined OSX +#include #include "sm_symtable.h" -using SourceHook::CVector; #endif -#if SH_SYS == SH_SYS_APPLE +#if defined OSX #include #endif -#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE +#if defined LINUX || defined OSX struct LibSymbolTable { SymbolTable table; @@ -63,10 +62,10 @@ public: ~MemoryUtils(); void *ResolveSymbol(void *handle, const char *symbol); -#if SH_SYS == SH_SYS_LINUX || SH_SYS == SH_SYS_APPLE +#if defined LINUX || defined OSX private: - CVector m_SymTables; -#if SH_SYS == SH_SYS_APPLE + std::vector m_SymTables; +#if defined OSX struct dyld_all_image_infos *m_ImageList; SInt32 m_OSXMajor; SInt32 m_OSXMinor;