From 08b897327664230be65bf8e7db21451150fe5de0 Mon Sep 17 00:00:00 2001 From: wootguy Date: Fri, 8 Nov 2024 18:11:44 -0800 Subject: [PATCH] add rehlds as a submodule and initialize its API my fork of rehlds is also required for some maps. The api can be used to call or hook engine functions. --- .github/workflows/build.yml | 12 ++ .gitmodules | 3 + cl_dll/cdll_int.cpp | 6 +- cl_dll/input.cpp | 2 +- common/com_model.h | 12 +- common/{interface.h => interface_hlsdk.h} | 0 common/rehlds_interface.cpp | 244 ++++++++++++++++++++++ dlls/CMakeLists.txt | 7 + dlls/game.cpp | 3 + dlls/mstream.cpp | 7 +- dlls/rehlds.cpp | 109 ++++++++++ dlls/rehlds.h | 4 + public/cl_dll/IGameClientExports.h | 2 +- public/interface.cpp | 2 +- public/{interface.h => interface_hlsdk.h} | 0 public/particleman.h | 2 +- rehlds | 1 + 17 files changed, 401 insertions(+), 15 deletions(-) rename common/{interface.h => interface_hlsdk.h} (100%) create mode 100644 common/rehlds_interface.cpp create mode 100644 dlls/rehlds.cpp create mode 100644 dlls/rehlds.h rename public/{interface.h => interface_hlsdk.h} (100%) create mode 160000 rehlds diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a829f94..0cf036d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,12 @@ jobs: with: submodules: 'false' + - name: Checkout rehlds + uses: actions/checkout@v4 + with: + repository: wootguy/rehlds + path: ./rehlds + - name: Checkout plugins uses: actions/checkout@v4 with: @@ -45,6 +51,12 @@ jobs: with: submodules: 'false' + - name: Checkout rehlds + uses: actions/checkout@v4 + with: + repository: wootguy/rehlds + path: ./rehlds + - name: Checkout plugins uses: actions/checkout@v4 with: diff --git a/.gitmodules b/.gitmodules index a2eb8b88..1821f1f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "sevenkewp"] path = sevenkewp url = https://github.com/wootguy/SevenKewp_data.git +[submodule "rehlds"] + path = rehlds + url = https://github.com/wootguy/rehlds diff --git a/cl_dll/cdll_int.cpp b/cl_dll/cdll_int.cpp index e0854bbf..6934ec0c 100644 --- a/cl_dll/cdll_int.cpp +++ b/cl_dll/cdll_int.cpp @@ -22,7 +22,7 @@ #include "cl_util.h" #include "netadr.h" #undef INTERFACE_H -#include "../public/interface.h" +#include "../public/interface_hlsdk.h" //#include "vgui_schememanager.h" #include "pm_shared.h" @@ -30,7 +30,7 @@ #include #include "hud_servers.h" #include "vgui_int.h" -#include "interface.h" +#include "interface_hlsdk.h" #ifdef _WIN32 #include "winsani_in.h" @@ -41,7 +41,7 @@ # #include "tri.h" #include "vgui_TeamFortressViewport.h" -#include "../public/interface.h" +#include "../public/interface_hlsdk.h" cl_enginefunc_t gEngfuncs; CHud gHUD; diff --git a/cl_dll/input.cpp b/cl_dll/input.cpp index ee99435b..2f121a4b 100644 --- a/cl_dll/input.cpp +++ b/cl_dll/input.cpp @@ -1020,7 +1020,7 @@ void ShutdownInput (void) KB_Shutdown(); } -#include "interface.h" +#include "../common/interface_hlsdk.h" void CL_UnloadParticleMan( void ); #if defined( _TFC ) diff --git a/common/com_model.h b/common/com_model.h index c014a399..4b395e37 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -40,6 +40,10 @@ #define CACHE_SIZE 32 // used to align key data structures +// must match definition in modelgen.h +#ifndef SYNCTYPE_T +#define SYNCTYPE_T + typedef enum { mod_brush, @@ -48,18 +52,12 @@ typedef enum mod_studio } modtype_t; -// must match definition in modelgen.h -#ifndef SYNCTYPE_T -#define SYNCTYPE_T - typedef enum { ST_SYNC=0, ST_RAND } synctype_t; -#endif - typedef struct { float mins[3], maxs[3]; @@ -348,4 +346,6 @@ typedef struct player_info_s customization_t customdata; } player_info_t; +#endif + #endif // #define COM_MODEL_H diff --git a/common/interface.h b/common/interface_hlsdk.h similarity index 100% rename from common/interface.h rename to common/interface_hlsdk.h diff --git a/common/rehlds_interface.cpp b/common/rehlds_interface.cpp new file mode 100644 index 00000000..b8a3aef1 --- /dev/null +++ b/common/rehlds_interface.cpp @@ -0,0 +1,244 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#define EXT_FUNC + +#include "rehlds/public/interface.h" + +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include "windows.h" + #include "Platform.h" +#else + #include + #include // getcwd + #include // sprintf + #include +#endif // _WIN32 + +// InterfaceReg +InterfaceReg *InterfaceReg::s_pInterfaceRegs = nullptr; + +InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char *pName) : m_pName(pName) +{ + m_CreateFn = fn; + m_pNext = s_pInterfaceRegs; + s_pInterfaceRegs = this; +} + +// This is the primary exported function by a dll, referenced by name via dynamic binding +// that exposes an opqaue function pointer to the interface. +// +// We have the Internal variant so Sys_GetFactoryThis() returns the correct internal +// symbol under GCC/Linux/Mac as CreateInterface is DLL_EXPORT so its global so the loaders +// on those OS's pick exactly 1 of the CreateInterface symbols to be the one that is process wide and +// all Sys_GetFactoryThis() calls find that one, which doesn't work. Using the internal walkthrough here +// makes sure Sys_GetFactoryThis() has the dll specific symbol and GetProcAddress() returns the module specific +// function for CreateInterface again getting the dll specific symbol we need. +EXPORT_FUNCTION IBaseInterface *CreateInterface(const char *pName, int *pReturnCode) +{ + InterfaceReg *pCur; + for (pCur = InterfaceReg::s_pInterfaceRegs; pCur; pCur = pCur->m_pNext) + { + if (strcmp(pCur->m_pName, pName) == 0) + { + if (pReturnCode) + { + *pReturnCode = IFACE_OK; + } + + return pCur->m_CreateFn(); + } + } + + if (pReturnCode) + { + *pReturnCode = IFACE_FAILED; + } + + return nullptr; +} + +#ifndef _WIN32 +// Linux doesn't have this function so this emulates its functionality +void *GetModuleHandle(const char *name) +{ + void *handle; + if (name == nullptr) + { + // hmm, how can this be handled under linux.... + // is it even needed? + return nullptr; + } + + if ((handle = dlopen(name, RTLD_NOW)) == nullptr) + { + //printf("Error:%s\n",dlerror()); + // couldn't open this file + return nullptr; + } + + // read "man dlopen" for details + // in short dlopen() inc a ref count + // so dec the ref count by performing the close + dlclose(handle); + return handle; +} +#endif // _WIN32 + +// Purpose: returns a pointer to a function, given a module +// Input : pModuleName - module name +// *pName - proc name +//static hlds_run wants to use this function +void *Sys_GetProcAddress(const char *pModuleName, const char *pName) +{ + return GetProcAddress(GetModuleHandle(pModuleName), pName); +} + +// Purpose: returns a pointer to a function, given a module +// Input : pModuleName - module name +// *pName - proc name +// hlds_run wants to use this function +void *Sys_GetProcAddress(void *pModuleHandle, const char *pName) +{ + return GetProcAddress((HMODULE)pModuleHandle, pName); +} + +// Purpose: Loads a DLL/component from disk and returns a handle to it +// Input : *pModuleName - filename of the component +// Output : opaque handle to the module (hides system dependency) +CSysModule *Sys_LoadModule(const char *pModuleName) +{ +#ifdef _WIN32 + HMODULE hDLL = LoadLibrary(pModuleName); +#else + HMODULE hDLL = nullptr; + char szAbsoluteModuleName[2048]; + if (pModuleName[0] != '/') + { + char szCwd[1024]; + getcwd(szCwd, sizeof(szCwd)); + if (szCwd[strlen(szCwd) - 1] == '/') + szCwd[strlen(szCwd) - 1] = '\0'; + + _snprintf(szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s/%s", szCwd, pModuleName); + hDLL = dlopen(szAbsoluteModuleName, RTLD_NOW); + } + else + { + _snprintf(szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s", pModuleName); + hDLL = dlopen(pModuleName, RTLD_NOW); + } +#endif // _WIN32 + + if (!hDLL) + { + char str[2048+6]; // room for extension string + +#if defined(_WIN32) + snprintf(str, sizeof(str), "%s.dll", pModuleName); + hDLL = LoadLibrary(str); +#elif defined(OSX) + printf("Error: %s\n", dlerror()); + _snprintf(str, sizeof(str), "%s.dylib", szAbsoluteModuleName); + hDLL = dlopen(str, RTLD_NOW); +#else + printf("Error: %s\n", dlerror()); + _snprintf(str, sizeof(str), "%s.so", szAbsoluteModuleName); + hDLL = dlopen(str, RTLD_NOW); +#endif + } + + return reinterpret_cast(hDLL); +} + +// Purpose: Unloads a DLL/component from +// Input : *pModuleName - filename of the component +// Output : opaque handle to the module (hides system dependency) +void Sys_UnloadModule(CSysModule *pModule) +{ + if (!pModule) + return; + + HMODULE hDLL = reinterpret_cast(pModule); + +#ifdef _WIN32 + FreeLibrary(hDLL); +#else + dlclose(hDLL); +#endif // _WIN32 +} + +// Purpose: returns a pointer to a function, given a module +// Input : module - windows HMODULE from Sys_LoadModule() +// *pName - proc name +// Output : factory for this module +CreateInterfaceFn Sys_GetFactory(CSysModule *pModule) +{ + if (!pModule) + return nullptr; + + return reinterpret_cast(Sys_GetProcAddress(pModule, CREATEINTERFACE_PROCNAME)); +} + +// Purpose: returns the instance of this module +// Output : CreateInterfaceFn +CreateInterfaceFn Sys_GetFactoryThis() +{ + return CreateInterface; +} + +// Purpose: returns the instance of the named module +// Input : *pModuleName - name of the module +// Output : CreateInterfaceFn - instance of that module +CreateInterfaceFn Sys_GetFactory(const char *pModuleName) +{ + return reinterpret_cast(Sys_GetProcAddress(pModuleName, CREATEINTERFACE_PROCNAME)); +} + +// Purpose: finds a particular interface in the factory set +void *InitializeInterface(char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories) +{ + void *retval; + + for (int i = 0; i < numFactories; i++) + { + CreateInterfaceFn factory = factoryList[ i ]; + if (!factory) + continue; + + retval = factory(interfaceName, nullptr); + if (retval) + return retval; + } + + // No provider for requested interface!!! + // assert(!"No provider for requested interface!!!"); + + return nullptr; +} diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index 528e0c89..2f8aa51e 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -429,6 +429,7 @@ set(UTIL_HDR ThreadSafeInt.h ThreadSafeQueue.h wav.h + rehlds.h ../game_shared/shared_util.h ) set(UTIL_SRC @@ -449,6 +450,8 @@ set(UTIL_SRC ThreadSafeInt.cpp ThreadSafeQueue.cpp wav.cpp + rehlds.cpp + ../common/rehlds_interface.cpp ../pm_shared/pm_debug.cpp ../pm_shared/pm_math.cpp ../pm_shared/pm_shared.cpp @@ -604,6 +607,10 @@ set(ALL_SRC # don't compile individual entity files set_source_files_properties(${ENT_SRC} PROPERTIES HEADER_FILE_ONLY TRUE) +include_directories(${CMAKE_SOURCE_DIR}/rehlds) +include_directories(${CMAKE_SOURCE_DIR}/rehlds/rehlds/public) +include_directories(${CMAKE_SOURCE_DIR}/rehlds/rehlds/common) + add_library(${SERVER_DLL_NAME} SHARED ${ALL_SRC}) target_include_directories(${SERVER_DLL_NAME} PRIVATE monster) diff --git a/dlls/game.cpp b/dlls/game.cpp index cecfafa0..42bafe2b 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -20,6 +20,7 @@ #include "CBaseMonster.h" #include "skill.h" #include "PluginManager.h" +#include "rehlds.h" cvar_t displaysoundlist = {"displaysoundlist","0", 0, 0, 0}; @@ -339,5 +340,7 @@ void GameDLLInit( void ) // END REGISTER CVARS FOR SKILL LEVEL STUFF SERVER_COMMAND( "exec skill.cfg\n" ); + + RehldsApi_Init(); } diff --git a/dlls/mstream.cpp b/dlls/mstream.cpp index 43707167..9239a40c 100644 --- a/dlls/mstream.cpp +++ b/dlls/mstream.cpp @@ -186,10 +186,13 @@ bool mstream::endBitWriting() { currentBit = 0; pos++; - if (pos + 1 >= end) { + if (pos >= end) { eomFlag = true; - return true; + pos = end; + return false; } + + return true; } bool mstream::writeBitCoord(const float f) { diff --git a/dlls/rehlds.cpp b/dlls/rehlds.cpp new file mode 100644 index 00000000..50df4e02 --- /dev/null +++ b/dlls/rehlds.cpp @@ -0,0 +1,109 @@ +typedef enum server_state_e +{ + ss_dead = 0, + ss_loading = 1, + ss_active = 2, +} server_state_t; + +typedef float vec_t; +typedef vec_t vec3_t[3]; + +typedef struct cache_user_s +{ + void* data; +} cache_user_t; + +using cvar_callback_t = void (*)(const char* pszNewValue); + +#define EXT_FUNC +#define SYNCTYPE_T + +#include "rehlds/public/interface.h" +#include "rehlds/public/rehlds/custom.h" +#include "rehlds/public/FileSystem.h" +#include "rehlds/public/rehlds/rehlds_api.h" + +IRehldsApi* g_RehldsApi; +const RehldsFuncs_t* g_RehldsFuncs; +IRehldsServerData* g_RehldsData; +IRehldsHookchains* g_RehldsHookchains; +IRehldsServerStatic* g_RehldsSvs; + +#include "rehlds.h" + +extern void DEBUG_MSG(ALERT_TYPE target, const char* format, ...); +#define ALERT(...) DEBUG_MSG(__VA_ARGS__) + +bool RehldsApi_Init() +{ +#ifdef WIN32 + // Find the most appropriate module handle from a list of DLL candidates + // Notes: + // - "swds.dll" is the library Dedicated Server + // + // Let's also attempt to locate the ReHLDS API in the client's library + // - "sw.dll" is the client library for Software render, with a built-in listenserver + // - "hw.dll" is the client library for Hardware render, with a built-in listenserver + const char* dllNames[] = { "swds.dll", "sw.dll", "hw.dll" }; // List of DLL candidates to lookup for the ReHLDS API + CSysModule* engineModule = NULL; // The module handle of the selected DLL + for (const char* dllName : dllNames) + { + if (engineModule = Sys_LoadModule(dllName)) + break; // gotcha + } + +#else + CSysModule* engineModule = Sys_LoadModule("engine_i486.so"); +#endif + + if (!engineModule) + return false; + + CreateInterfaceFn ifaceFactory = Sys_GetFactory(engineModule); + if (!ifaceFactory) + return false; + + int retCode = 0; + g_RehldsApi = (IRehldsApi*)ifaceFactory(VREHLDS_HLDS_API_VERSION, &retCode); + + if (!g_RehldsApi) + return false; + + int majorVersion = g_RehldsApi->GetMajorVersion(); + int minorVersion = g_RehldsApi->GetMinorVersion(); + + if (majorVersion != REHLDS_API_VERSION_MAJOR) + { + ALERT(at_error, "ReHLDS API major version mismatch; expected %d, real %d\n", REHLDS_API_VERSION_MAJOR, majorVersion); + + // need to notify that it is necessary to update the ReHLDS. + if (majorVersion < REHLDS_API_VERSION_MAJOR) + { + ALERT(at_error, "Please update the ReHLDS up to a major version API >= %d\n", REHLDS_API_VERSION_MAJOR); + } + + // need to notify that it is necessary to update the module. + else if (majorVersion > REHLDS_API_VERSION_MAJOR) + { + ALERT(at_error, "Please update the mod up to a major version API >= %d\n", majorVersion); + } + + return false; + } + + if (minorVersion < REHLDS_API_VERSION_MINOR) + { + ALERT(at_error, "ReHLDS API minor version mismatch; expected at least %d, real %d\n", REHLDS_API_VERSION_MINOR, minorVersion); + ALERT(at_error, "Please update the ReHLDS up to a minor version API >= %d\n", REHLDS_API_VERSION_MINOR); + return false; + } + + g_RehldsFuncs = g_RehldsApi->GetFuncs(); + g_RehldsData = g_RehldsApi->GetServerData(); + g_RehldsHookchains = g_RehldsApi->GetHookchains(); + g_RehldsSvs = g_RehldsApi->GetServerStatic(); + + ALERT(at_console, "ReHLDS API version %d.%d initialized\n", REHLDS_API_VERSION_MAJOR, REHLDS_API_VERSION_MINOR); + + return true; +} diff --git a/dlls/rehlds.h b/dlls/rehlds.h new file mode 100644 index 00000000..2493d764 --- /dev/null +++ b/dlls/rehlds.h @@ -0,0 +1,4 @@ +#pragma once + + +bool RehldsApi_Init(); \ No newline at end of file diff --git a/public/cl_dll/IGameClientExports.h b/public/cl_dll/IGameClientExports.h index d45d53ac..f9f57d4d 100644 --- a/public/cl_dll/IGameClientExports.h +++ b/public/cl_dll/IGameClientExports.h @@ -11,7 +11,7 @@ #pragma once #endif -#include "interface.h" +#include "interface_hlsdk.h" //----------------------------------------------------------------------------- // Purpose: Exports a set of functions for the GameUI interface to interact with the game client diff --git a/public/interface.cpp b/public/interface.cpp index dad4ed54..7c18cb44 100644 --- a/public/interface.cpp +++ b/public/interface.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "interface.h" +#include "interface_hlsdk.h" #if !defined ( _WIN32 ) #include diff --git a/public/interface.h b/public/interface_hlsdk.h similarity index 100% rename from public/interface.h rename to public/interface_hlsdk.h diff --git a/public/particleman.h b/public/particleman.h index 0f9b7024..fd94c40c 100644 --- a/public/particleman.h +++ b/public/particleman.h @@ -1,7 +1,7 @@ #ifndef PARTICLEMAN_H #define PARTICLEMAN_H -#include "interface.h" +#include "interface_hlsdk.h" #include "pman_triangleffect.h" #define PARTICLEMAN_INTERFACE "create_particleman" diff --git a/rehlds b/rehlds new file mode 160000 index 00000000..2d857fc3 --- /dev/null +++ b/rehlds @@ -0,0 +1 @@ +Subproject commit 2d857fc38623d4667273d55f5bb606fe6f4660b7