diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7b2f5ed..a5f94f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,10 @@ jobs: sm: - label: "1.12" branch: "1.12-dev" + mms_branch: "1.12-dev" - label: "1.13" branch: "master" + mms_branch: "1.12-dev" target: - os: ubuntu-latest os_name: linux @@ -91,7 +93,7 @@ jobs: uses: actions/checkout@v7 with: repository: alliedmodders/metamod-source - ref: 1.12-dev + ref: ${{ matrix.sm.mms_branch }} path: mmsource submodules: recursive diff --git a/AMBuildScript b/AMBuildScript index 1d81797..d605ec9 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -1,5 +1,6 @@ # vim: set ts=2 sw=2 tw=99 noet ft=python: import os +import re import platform def ResolveEnvPath(env, folder): @@ -400,10 +401,15 @@ class ExtensionConfig(object): SetArchFlags(compiler) compiler.cxxincludes += [ - os.path.join(self.mms_root, 'core'), - os.path.join(self.mms_root, 'core', 'sourcehook'), + os.path.join(self.mms_root, 'core') ] + mms_major = int(self.get_mms_version()[0]) + if mms_major >= 2: + compiler.cxxincludes += [os.path.join(self.mms_root, 'third_party', 'khook', 'include')] + else: + compiler.cxxincludes += [os.path.join(self.mms_root, 'core', 'sourcehook')] + for other_sdk in self.sdk_manifests: compiler.defines += [f'SE_{other_sdk["define"]}={other_sdk["code"]}'] @@ -447,6 +453,17 @@ class ExtensionConfig(object): else: raise Exception("Could not find a suitable copy of spcomp in SourceMod binary root") + def get_mms_version(self): + mms_version_file = os.path.join(self.mms_root, "product.version") + if not os.path.isfile(mms_version_file): + raise Exception("Could not find product.version under Metamod:Source root") + with open(mms_version_file, "r") as f: + version = f.read().strip() + match = re.match(r'([0-9]+).([0-9]+).([0-9]+).*', version) + if match: + return match.groups() + raise Exception("Could not parse product.version from Metamod:Source") + Extension = ExtensionConfig() Extension.detectSDKs() Extension.find_spcomp() diff --git a/cssdm_events.cpp b/cssdm_events.cpp index 8561cd8..eec5a42 100644 --- a/cssdm_events.cpp +++ b/cssdm_events.cpp @@ -144,14 +144,20 @@ void DM_SchedRespawn(int client) player->respawn_timer = timersys->CreateTimer(&s_PlayerSpawner, DM_GetRespawnWait(), data, 0); } +#if METAMOD_PLAPI_VERSION < 18 void OnClientCommand_Post(edict_t *edict, const CCommand &args) +#else +KHook::Return OnClientCommand_Post(IServerGameClients *gameClients, edict_t *edict, const CCommand &args) +#endif { + const char *cmd; + if (!g_IsRunning) { - return; + goto end; } - const char *cmd = args.Arg(0); + cmd = args.Arg(0); if (strcmp(cmd, "joinclass") == 0) { @@ -159,17 +165,17 @@ void OnClientCommand_Post(edict_t *edict, const CCommand &args) dm_player_t *player = DM_GetPlayer(client); if (!player || !player->pEntity) { - return; + goto end; } if (DM_IsPlayerAlive(client)) { - return; + goto end; } if (!player->will_respawn_on_class) { - return; + goto end; } player->will_respawn_on_class = false; @@ -177,6 +183,13 @@ void OnClientCommand_Post(edict_t *edict, const CCommand &args) /* Respawn! */ DM_SchedRespawn(client); } + +end: +#if METAMOD_PLAPI_VERSION < 18 + RETURN_META(MRES_IGNORED); +#else + return { KHook::Action::Ignore }; +#endif } #define IMPLEMENT_EVENT(name) \ diff --git a/cssdm_events.h b/cssdm_events.h index 22321a8..6ce8324 100644 --- a/cssdm_events.h +++ b/cssdm_events.h @@ -60,7 +60,11 @@ DECLARE_EVENT(round_end); class CCommand; void DM_ClearRagdollTimers(); +#if METAMOD_PLAPI_VERSION < 18 void OnClientCommand_Post(edict_t *edict, const CCommand &args); +#else +KHook::Return OnClientCommand_Post(IServerGameClients *gameClients, edict_t *edict, const CCommand &args); +#endif class DMData { diff --git a/cssdm_ffa.cpp b/cssdm_ffa.cpp index 56154de..d44397b 100644 --- a/cssdm_ffa.cpp +++ b/cssdm_ffa.cpp @@ -32,7 +32,11 @@ // Don't patch anything on CSGO #if SOURCE_ENGINE != SE_CSGO -SH_DECL_MANUALHOOK2(CGameRules_IPointsForKill, 62+EXTRA_VTBL_OFFSET, 0, 0, int, CBasePlayer *, CBasePlayer *); +#if METAMOD_PLAPI_VERSION < 18 +SH_DECL_MANUALHOOK2(CGameRules_IPointsForKill, 80+EXTRA_VTBL_OFFSET, 0, 0, int, CBasePlayer *, CBasePlayer *); +#else +class CGameRules; +#endif #if defined PLATFORM_64BITS #define PLATFORM_ARCH_SUFFIX "64" @@ -72,12 +76,24 @@ static void *g_domrev_addr = NULL; static dmpatch_t g_domrev_patch; static dmpatch_t g_domrev_restore; +#if METAMOD_PLAPI_VERSION < 18 int OnIPointsForKill(CBasePlayer *pl1, CBasePlayer *pl2) +#else +KHook::Return OnIPointsForKill(CGameRules *gamerules, CBasePlayer *pl1, CBasePlayer *pl2) +#endif { /* If we're hooked, FFA is always on. */ +#if METAMOD_PLAPI_VERSION < 18 RETURN_META_VALUE(MRES_SUPERCEDE, 1); +#else + return { KHook::Action::Supersede, 1 }; +#endif } +#if METAMOD_PLAPI_VERSION >= 18 +KHook::Virtual Hook_IPointsForKill(80 + EXTRA_VTBL_OFFSET, OnIPointsForKill, nullptr); +#endif + bool DM_FFA_LoadPatch(const char *name, dmpatch_t *patch, char *error, size_t maxlength) { char fullname[255]; @@ -168,7 +184,11 @@ bool DM_Prepare_FFA(char *error, size_t maxlength) snprintf(error, maxlength, "Could not find IPointsForKills offset"); return false; } +#if METAMOD_PLAPI_VERSION < 18 SH_MANUALHOOK_RECONFIGURE(CGameRules_IPointsForKill, offset, 0, 0); +#else + Hook_IPointsForKill.Configure(offset); +#endif g_FFA_Prepared = true; @@ -205,7 +225,11 @@ bool DM_Patch_FFA() // needs a new gamerules address on every map load if (!g_FFA_PointsHooked && LoadGameRulesAddress()) { +#if METAMOD_PLAPI_VERSION < 18 SH_ADD_MANUALHOOK_STATICFUNC(CGameRules_IPointsForKill, g_gamerules_addr, OnIPointsForKill, false); +#else + Hook_IPointsForKill.Add(reinterpret_cast(g_gamerules_addr)); +#endif g_FFA_PointsHooked = true; } @@ -224,7 +248,11 @@ bool DM_Unpatch_FFA() // g_gamerules_addr won't be null if we're already hooked if (!g_IsInGlobalShutdown && g_FFA_PointsHooked) { +#if METAMOD_PLAPI_VERSION < 18 SH_REMOVE_MANUALHOOK_STATICFUNC(CGameRules_IPointsForKill, g_gamerules_addr, OnIPointsForKill, false); +#else + Hook_IPointsForKill.Remove(reinterpret_cast(g_gamerules_addr)); +#endif g_FFA_PointsHooked = false; } diff --git a/cssdm_main.cpp b/cssdm_main.cpp index 75d4c0c..f094b4a 100644 --- a/cssdm_main.cpp +++ b/cssdm_main.cpp @@ -36,8 +36,10 @@ #include "cssdm_weapons.h" #include "cssdm_version.h" +#if METAMOD_PLAPI_VERSION < 18 SH_DECL_HOOK0_void(IServerGameDLL, DLLShutdown, SH_NOATTRIB, false); SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, false, edict_t *, const CCommand &); +#endif Deathmatch g_DM; IGameEventManager2 *gameevents = NULL; @@ -122,12 +124,26 @@ bool Deathmatch::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bo return true; } +#if METAMOD_PLAPI_VERSION < 18 void OnDLLShutdown() +#else +KHook::Return OnDLLShutdown(IServerGameDLL *server) +#endif { g_IsInGlobalShutdown = true; +#if METAMOD_PLAPI_VERSION < 18 RETURN_META(MRES_IGNORED); +#else + return { KHook::Action::Ignore }; +#endif } +#if METAMOD_PLAPI_VERSION >= 18 +KHook::Virtual Hook_DLLShutdown(&IServerGameDLL::DLLShutdown, OnDLLShutdown, nullptr); +KHook::Virtual Hook_ClientCommand( + &IServerGameClients::ClientCommand, nullptr, OnClientCommand_Post); +#endif + bool Startup(char *error, size_t maxlength) { playerhelpers->AddClientListener(&g_ClientListener); @@ -141,8 +157,13 @@ bool Startup(char *error, size_t maxlength) g_Startup = true; +#if METAMOD_PLAPI_VERSION < 18 SH_ADD_HOOK_STATICFUNC(IServerGameDLL, DLLShutdown, gamedll, OnDLLShutdown, false); SH_ADD_HOOK_STATICFUNC(IServerGameClients, ClientCommand, gameclients, OnClientCommand_Post, true); +#else + Hook_DLLShutdown.Add(gamedll); + Hook_ClientCommand.Add(gameclients); +#endif DM_InitCallbacks(); @@ -184,8 +205,13 @@ void Shutdown() ShutdownUtils(); /* Unhook everything from SourceHook */ +#if METAMOD_PLAPI_VERSION < 18 SH_REMOVE_HOOK_STATICFUNC(IServerGameClients, ClientCommand, gameclients, OnClientCommand_Post, true); SH_REMOVE_HOOK_STATICFUNC(IServerGameDLL, DLLShutdown, gamedll, OnDLLShutdown, false); +#else + Hook_ClientCommand.Remove(gameclients); + Hook_DLLShutdown.Remove(gamedll); +#endif } void Deathmatch::SDK_OnAllLoaded() diff --git a/cssdm_utils.cpp b/cssdm_utils.cpp index 9189906..91f9e07 100644 --- a/cssdm_utils.cpp +++ b/cssdm_utils.cpp @@ -30,7 +30,11 @@ #include "cssdm_players.h" #include "cssdm_utils.h" #include "cssdm_includesdk.h" +#if METAMOD_PLAPI_VERSION < 18 #include +#else +#include +#endif std::list g_CallWrappers; ICallWrapper *g_pRoundRespawn = NULL; @@ -39,8 +43,6 @@ ICallWrapper *g_pGiveAmmo = NULL; int g_RagdollOffset = 0; int g_LifeStateOffset = 0; -void DM_ProtectMemory(void *addr, int length, int prot); - CBaseEntity *DM_GetBaseEntity(int index) { edict_t *pEdict = gamehelpers->EdictOfIndex(index); @@ -221,7 +223,7 @@ void DM_ApplyPatch(void *address, int offset, const dmpatch_t *patch, dmpatch_t { unsigned char *addr = (unsigned char *)address + offset; - DM_SetMemPatchable(addr, 20); + DM_SetMemPatchable(addr, patch->bytes); if (restore) { @@ -240,7 +242,11 @@ void DM_ApplyPatch(void *address, int offset, const dmpatch_t *patch, dmpatch_t void DM_SetMemPatchable(void *address, size_t size) { +#if METAMOD_PLAPI_VERSION < 18 SourceHook::SetMemAccess(address, size, SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); +#else + KHook::Memory::SetAccess(address, size, KHook::Memory::Flags::READ | KHook::Memory::Flags::WRITE | KHook::Memory::EXECUTE); +#endif } #define GET_PROPERTY(cls, name, var) \