diff --git a/.gitignore b/.gitignore index e69de29..358eed5 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,3 @@ +metamod/.idea +metamod/build +metamod/cmake-build-debug \ No newline at end of file diff --git a/metamod/AMBuildScript b/metamod/AMBuildScript index 7447a45..8b3dda0 100644 --- a/metamod/AMBuildScript +++ b/metamod/AMBuildScript @@ -111,7 +111,7 @@ def ResolveEnvPath(env, folder): head, tail = os.path.split(head) return None -def Normalize(path): +def Normalize(path): return os.path.abspath(os.path.normpath(path)) class MMSPluginConfig(object): @@ -181,25 +181,25 @@ class MMSPluginConfig(object): if len(self.sdks) < 1 and len(sdk_list): raise Exception('No SDKs were found, nothing to build.') - + if len(self.sdks) > 1: raise Exception('Only one sdk at a time is supported, for multi-sdk approach use loader based solution.') - - if builder.options.mms_path: - self.mms_root = builder.options.mms_path - else: + + if builder.options.mms_path: + self.mms_root = builder.options.mms_path + else: self.mms_root = ResolveEnvPath('MMSOURCE20', 'mmsource-2.0') - if not self.mms_root: + if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12') - if not self.mms_root: + if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE111', 'mmsource-1.11') - if not self.mms_root: + if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10') - if not self.mms_root: + if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod-source') - if not self.mms_root: + if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'mmsource-central') - if not self.mms_root or not os.path.isdir(self.mms_root): + if not self.mms_root or not os.path.isdir(self.mms_root): raise Exception('Could not find a source copy of Metamod:Source') self.mms_root = Normalize(self.mms_root) @@ -239,19 +239,19 @@ class MMSPluginConfig(object): if cxx.version == 'apple-clang-6.0' or cxx.version == 'clang-3.4': cxx.cxxflags += ['-std=c++1y'] else: - cxx.cxxflags += ['-std=c++14'] + cxx.cxxflags += ['--std=c++2a', '-Wno-error'] if (cxx.version >= 'gcc-4.0') or cxx.family == 'clang': cxx.cflags += ['-fvisibility=hidden'] cxx.cxxflags += ['-fvisibility-inlines-hidden'] cxx.cxxflags += [ '-fno-exceptions', - '-fno-rtti', + #'-fno-rtti', '-fno-threadsafe-statics', '-Wno-non-virtual-dtor', - '-Wno-overloaded-virtual', + '-Wno-overloaded-virtual' ] if (cxx.version >= 'gcc-4.7' or cxx.family == 'clang'): - cxx.cxxflags += ['-Wno-delete-non-virtual-dtor'] + cxx.cxxflags += ['-Wno-delete-non-virtual-dtor', '-std=c++2a'] if cxx.family == 'gcc': cxx.cflags += ['-mfpmath=sse'] if cxx.family == 'clang': @@ -264,7 +264,7 @@ class MMSPluginConfig(object): cxx.cxxflags += ['-Wno-deprecated-register'] else: cxx.cxxflags += ['-Wno-deprecated'] - + # Work around SDK warnings. if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0': cxx.cflags += [ @@ -274,10 +274,11 @@ class MMSPluginConfig(object): elif cxx.like('msvc'): if builder.options.debug == '1': - cxx.cflags += ['/MTd'] + cxx.cflags += ['/MDd'] cxx.linkflags += ['/NODEFAULTLIB:libcmt'] else: cxx.cflags += ['/MT'] + cxx.linkflags += ['/NODEFAULTLIB:libcmt'] cxx.defines += [ '_CRT_SECURE_NO_DEPRECATE', '_CRT_SECURE_NO_WARNINGS', @@ -288,7 +289,7 @@ class MMSPluginConfig(object): '/Zi', ] cxx.cxxflags += ['/TP'] - + cxx.linkflags += [ '/SUBSYSTEM:WINDOWS', 'kernel32.lib', @@ -316,7 +317,7 @@ class MMSPluginConfig(object): # Debugging if builder.options.debug == '1': - cxx.defines += ['DEBUG', '_DEBUG'] + cxx.defines += ['DEBUG', '_DEBUG', '_ITERATOR_DEBUG_LEVEL=0'] if cxx.behavior == 'gcc': cxx.cflags += ['-g3'] elif cxx.behavior == 'msvc': @@ -354,7 +355,7 @@ class MMSPluginConfig(object): '-lc++', ] elif cxx.target.platform == 'windows': - cxx.defines += ['WIN32', '_WINDOWS'] + cxx.defines += ['WIN32', '_WINDOWS', 'SUBHOOK_STATIC'] # Finish up. # Custom defines here @@ -366,7 +367,7 @@ class MMSPluginConfig(object): compiler = cxx.clone() mms_core_path = os.path.join(self.mms_root, 'core') compiler.cxxincludes += [ - os.path.join(mms_core_path), + os.path.join(mms_core_path), os.path.join(mms_core_path, 'sourcehook'), os.path.join(context.currentSourcePath), ] @@ -385,6 +386,8 @@ class MMSPluginConfig(object): paths.append(['public', 'game', 'server']) paths.append(['game', 'shared']) paths.append(['common']) + paths.append(['public', 'entity2']) + paths.append(['game', 'server']) compiler.defines += ['SOURCE_ENGINE=' + sdk.code] if sdk.name in ['sdk2013', 'bms', 'pvkii'] and compiler.like('gcc'): @@ -400,16 +403,16 @@ class MMSPluginConfig(object): compiler.defines += ['COMPILER_MSVC32'] elif compiler.target.arch == 'x86_64': compiler.defines += ['COMPILER_MSVC64'] - + if compiler.version >= 1900: compiler.linkflags += ['legacy_stdio_definitions.lib'] else: compiler.defines += ['COMPILER_GCC'] - + if compiler.target.arch == 'x86_64': compiler.defines += ['X64BITS', 'PLATFORM_64BITS'] - if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota', 'cs2', 'pvkii']: + if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'pvkii']: if compiler.target.platform in ['linux', 'mac']: compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE'] @@ -434,7 +437,7 @@ class MMSPluginConfig(object): def HL2Library(self, context, compiler, name, sdk): compiler = self.HL2Compiler(context, compiler, sdk) - + if compiler.target.platform == 'linux': if sdk.name == 'episode1': lib_folder = os.path.join(sdk.path, 'linux_sdk') @@ -466,7 +469,7 @@ class MMSPluginConfig(object): compiler.postlink += [os.path.join(lib_folder, 'interfaces.a')] else: compiler.postlink += [os.path.join(lib_folder, 'interfaces_i486.a')] - + if sdk.name == 'bms': compiler.postlink += [os.path.join(lib_folder, 'mathlib.a')] @@ -525,4 +528,4 @@ BuildScripts = [ 'PackageScript', ] -builder.Build(BuildScripts, { 'MMSPlugin': MMSPlugin }) +builder.Build(BuildScripts, { 'MMSPlugin': MMSPlugin }) \ No newline at end of file diff --git a/metamod/AMBuilder b/metamod/AMBuilder index 598f306..d78759a 100644 --- a/metamod/AMBuilder +++ b/metamod/AMBuilder @@ -15,15 +15,33 @@ for sdk_name in MMSPlugin.sdks: binary = MMSPlugin.HL2Library(builder, cxx, MMSPlugin.plugin_name, sdk) + binary.compiler.cxxincludes += [ + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'include'), + ] + + if binary.compiler.target.platform == 'linux': + binary.compiler.postlink += [ + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'libfunchook.a'), + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'libdistorm.a'), + ] + elif binary.compiler.target.platform == 'windows': + binary.compiler.postlink += [ + os.path.join('psapi.lib'), + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'funchook.lib'), + os.path.join(builder.sourcePath, 'vendor', 'funchook', 'lib', 'distorm.lib'), + ] + binary.sources += [ - './src/wst.cpp', + 'src/wst.cpp', + 'src/core/schemasystem.cpp' ] if sdk_name in ['dota', 'cs2']: binary.sources += [ - os.path.join(sdk.path, 'tier1', 'convar.cpp'), - os.path.join(sdk.path, 'public', 'tier0', 'memoverride.cpp'), - ] + os.path.join(sdk.path, 'public', 'tier0', 'memoverride.cpp'), + os.path.join(sdk.path, 'tier1', 'convar.cpp') + ] + if cxx.target.arch == 'x86': binary.sources += ['sourcehook/sourcehook_hookmangen.cpp'] diff --git a/metamod/CMakeLists.txt b/metamod/CMakeLists.txt new file mode 100644 index 0000000..d17dca8 --- /dev/null +++ b/metamod/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.0) +project(wst) + +include_directories( + hl2sdk + hl2sdk/common + hl2sdk/game/shared + hl2sdk/game/server + hl2sdk/public + hl2sdk/public/engine + hl2sdk/public/mathlib + hl2sdk/public/tier0 + hl2sdk/public/tier1 + hl2sdk/public/entity2 + hl2sdk/public/game/server + metamod-source/core + metamod-source/core/sourcehook + vendor/funchook/include +) + +add_executable(wst + src/wst.cpp + src/wst.h + src/core/framework.h + src/core/module.h + src/core/virtual.h + src/core/schemasystem.cpp + src/core/schemasystem.h + src/core/cbaseentity.h +) + +# Dummy target to integrate custom build script +add_custom_target(MetamodBuild ALL + COMMAND PowerShell.exe -File ${CMAKE_SOURCE_DIR}/local_build.ps1 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + + + diff --git a/metamod/configure.py b/metamod/configure.py index 2ca9277..d5af115 100644 --- a/metamod/configure.py +++ b/metamod/configure.py @@ -1,39 +1,39 @@ # vim: set sts=2 ts=8 sw=2 tw=99 et: import sys try: - from ambuild2 import run, util + from ambuild2 import run, util except: - try: - import ambuild - sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n') - sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n') - except: - sys.stderr.write('AMBuild must be installed to build this project.\n') - sys.stderr.write('http://www.alliedmods.net/ambuild\n') - sys.exit(1) + try: + import ambuild + sys.stderr.write('It looks like you have AMBuild 1 installed, but this project uses AMBuild 2.\n') + sys.stderr.write('Upgrade to the latest version of AMBuild to continue.\n') + except: + sys.stderr.write('AMBuild must be installed to build this project.\n') + sys.stderr.write('http://www.alliedmods.net/ambuild\n') + sys.exit(1) # Hack to show a decent upgrade message, which wasn't done until 2.2. ambuild_version = getattr(run, 'CURRENT_API', '2.1') if ambuild_version.startswith('2.1'): - sys.stderr.write("AMBuild 2.2 or higher is required; please update\n") - sys.exit(1) + sys.stderr.write("AMBuild 2.2 or higher is required; please update\n") + sys.exit(1) parser = run.BuildParser(sourcePath=sys.path[0], api='2.2') parser.options.add_argument('-n', '--plugin-name', type=str, dest='plugin_name', default=None, - help='Plugin name') + help='Plugin name') parser.options.add_argument('-a', '--plugin-alias', type=str, dest='plugin_alias', default=None, - help='Plugin alias') + help='Plugin alias') parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, - help='Root search folder for HL2SDKs') + help='Root search folder for HL2SDKs') parser.options.add_argument('--mms_path', type=str, dest='mms_path', default=None, - help='Metamod:Source source tree folder') + help='Metamod:Source source tree folder') parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug', - help='Enable debugging symbols') + help='Enable debugging symbols') parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt', - help='Enable optimization') + help='Enable optimization') parser.options.add_argument('-s', '--sdks', default='all', dest='sdks', - help='Build against specified SDKs; valid args are "all", "present", or ' - 'comma-delimited list of engine names (default: "all")') + help='Build against specified SDKs; valid args are "all", "present", or ' + 'comma-delimited list of engine names (default: "all")') parser.options.add_argument('--targets', type=str, dest='targets', default=None, help="Override the target architecture (use commas to separate multiple targets).") -parser.Configure() +parser.Configure() \ No newline at end of file diff --git a/metamod/local_build.ps1 b/metamod/local_build.ps1 index 42ce2fe..2a52a6f 100644 --- a/metamod/local_build.ps1 +++ b/metamod/local_build.ps1 @@ -11,6 +11,7 @@ $packageDir = "$buildDir\package\addons\*" # Define the target directory for the plugin $targetDir = "C:\cs2\game\csgo\addons" +$command = "C:\cs2\game\bin\win64\cs2.exe" # Check if the build directory exists, and delete it if it does @@ -33,5 +34,8 @@ if (Test-Path $packageDir) { Copy-Item -Path $packageDir -Destination $targetDir -Recurse -Force } -# Return to the root directory -Set-Location -Path $currentDir \ No newline at end of file +# Spawn a process running $command with args -dedicated +map de_dust2 +Start-Process -FilePath $command -ArgumentList "-dedicated +map de_dust2" + + + diff --git a/metamod/src/core/cbaseentity.h b/metamod/src/core/cbaseentity.h new file mode 100644 index 0000000..e934c45 --- /dev/null +++ b/metamod/src/core/cbaseentity.h @@ -0,0 +1,33 @@ +#ifndef WST_CBASEENTITY_H +#define WST_CBASEENTITY_H + +#pragma once +#include +#include +#include "schemasystem.h" + +//inline CEntityInstance* UTIL_FindEntityByClassname(CEntityInstance* pStart, const char* name) +//{ +// extern CEntitySystem* g_pEntitySystem; +// CEntityIdentity* pEntity = pStart ? pStart->m_pEntity->m_pNext : g_pEntitySystem->m_EntityList.m_pFirstActiveEntity; +// +// for (; pEntity; pEntity = pEntity->m_pNext) +// { +// if (!strcmp(pEntity->m_designerName.String(), name)) +// return pEntity->m_pInstance; +// }; +// +// return nullptr; +//} + +class SC_CBaseEntity : public CBaseEntity +{ +public: + SCHEMA_FIELD(int32_t, CBaseEntity, m_iHealth); + SCHEMA_FIELD(int32_t, CBaseEntity, m_iMaxHealth); + SCHEMA_FIELD(LifeState_t, CBaseEntity, m_lifeState); + SCHEMA_FIELD(uint8_t, CBaseEntity, m_iTeamNum); + SCHEMA_FIELD(float, CBaseEntity, m_flGravityScale); +}; + +#endif //WST_CBASEENTITY_H diff --git a/metamod/src/core/framework.h b/metamod/src/core/framework.h new file mode 100644 index 0000000..8499ef7 --- /dev/null +++ b/metamod/src/core/framework.h @@ -0,0 +1,73 @@ +#ifndef WST_FRAMEWORK_H +#define WST_FRAMEWORK_H + +#include +#include "igameevents.h" +#include "module.h" +#include "schemasystem.h" + + +#pragma once + + + +class Framework { +public: + static ICvar*& CVar() { + static ICvar* wst_CVar = nullptr; + return wst_CVar; + } + + static IFileSystem*& FileSystem() { + static IFileSystem* wst_FullFileSystem = nullptr; + return wst_FullFileSystem; + } + + static INetworkServerService*& NetworkServerService() { + static INetworkServerService* wst_NetworkServerService = nullptr; + return wst_NetworkServerService; + } + + static ISource2Server*& Source2Server() { + static ISource2Server* wst_Source2Server = nullptr; + return wst_Source2Server; + } + + static IVEngineServer2*& EngineServer() { + static IVEngineServer2* wst_EngineServer = nullptr; + return wst_EngineServer; + } + + static IGameEventManager2*& GameEventManager() { + static IGameEventManager2* wst_GameEventManager = nullptr; + return wst_GameEventManager; + } + + // ServerCModule + static CModule Server() { + static CModule wst_ServerCModule(GAMEBIN, "server"); + return wst_ServerCModule; + } + + static CModule SchemaSystemModule() { +#ifdef WIN32 + static CModule wst_SchemaSystemCModule(ROOTBIN, "schemasystem"); +#else + static CModule wst_SchemaSystemCModule(ROOTBIN, "libschemasystem"); +#endif + return wst_SchemaSystemCModule; + } + + static CSchemaSystem*& SchemaSystem() { + static CSchemaSystem* wst_SchemaSystem = nullptr; + return wst_SchemaSystem; + } + + // Disable the creation of this class + Framework() = delete; + Framework(const Framework&) = delete; + Framework& operator=(const Framework&) = delete; +}; + + +#endif //WST_FRAMEWORK_H diff --git a/metamod/src/core/module.h b/metamod/src/core/module.h new file mode 100644 index 0000000..44bbf11 --- /dev/null +++ b/metamod/src/core/module.h @@ -0,0 +1,131 @@ +#ifndef WST_MODULE_H +#define WST_MODULE_H + +#pragma once + +#include "dbg.h" +#include "interface.h" +#include "strtools.h" +#include "metamod_oslink.h" + +#ifdef _WIN32 +#include +#endif + + +#ifdef _WIN32 +#define MODULE_PREFIX "" +#define MODULE_EXT ".dll" +#else +#define MODULE_PREFIX "lib" +#define MODULE_EXT ".so" +#endif + +#ifdef _WIN32 +#define ROOTBIN "/bin/win64/" +#define GAMEBIN "/csgo/bin/win64/" +#else +#define ROOTBIN "/bin/linuxsteamrt64/" +#define GAMEBIN "/csgo/bin/linuxsteamrt64/" +#endif + +class CModule +{ +public: + CModule(const char *path, const char *module) : + m_pszModule(module), m_pszPath(path) + { + char szModule[MAX_PATH]; + + V_snprintf(szModule, MAX_PATH, "%s%s%s%s%s", Plat_GetGameDirectory(), path, MODULE_PREFIX, m_pszModule, MODULE_EXT); + + m_hModule = dlmount(szModule); + + if (!m_hModule) + Error("Could not find %s\n", szModule); + +#ifdef _WIN32 + MODULEINFO m_hModuleInfo; + GetModuleInformation(GetCurrentProcess(), m_hModule, &m_hModuleInfo, sizeof(m_hModuleInfo)); + + m_base = (void *)m_hModuleInfo.lpBaseOfDll; + m_size = m_hModuleInfo.SizeOfImage; +#else + if (int e = GetModuleInformation(szModule, &m_base, &m_size)) + Error("Failed to get module info for %s, error %d\n", szModule, e); +#endif + } + + void *FindSignature(const byte *pData, size_t length) + { + unsigned char *pMemory; + void *return_addr = nullptr; + + pMemory = (byte *)m_base; + + for (size_t i = 0; i < m_size; i++) + { + size_t Matches = 0; + while (*(pMemory + i + Matches) == pData[Matches] || pData[Matches] == '\x2A') + { + Matches++; + if (Matches == length) + return_addr = (void *)(pMemory + i); + } + } + + return return_addr; + } + + // Will break with string containing \0 !!! + void *FindSignature(const byte *pData) + { + size_t iSigLength = V_strlen((const char *)pData); + + return FindSignature(pData, iSigLength); + } + + static byte *ConvertToByteArray(const char *str, size_t *outLength) { + size_t len = strlen(str) / 4; // Every byte is represented as \xHH + byte *result = (byte *)malloc(len); + + for (size_t i = 0, j = 0; i < len; ++i, j += 4) { + sscanf(str + j, "\\x%2hhx", &result[i]); + } + + *outLength = len; + return result; + } + + void *FindSignature(const char *pData) + { + size_t iSigLength; + byte *pByteArray = ConvertToByteArray(pData, &iSigLength); + + void *result = FindSignature(pByteArray, iSigLength); + + free(pByteArray); + + return result; + } + + void *FindInterface(const char *name) { + CreateInterfaceFn fn = (CreateInterfaceFn)dlsym(m_hModule, "CreateInterface"); + + if (!fn) Error("Could not find CreateInterface in %s\n", m_pszModule); + + void *pInterface = fn(name, nullptr); + + if (!pInterface) Error("Could not find %s in %s\n", name, m_pszModule); + + return pInterface; + } + + + const char *m_pszModule; + const char* m_pszPath; + HINSTANCE m_hModule; + void* m_base; + size_t m_size; +}; +#endif //WST_MODULE_H diff --git a/metamod/src/core/schemasystem.cpp b/metamod/src/core/schemasystem.cpp new file mode 100644 index 0000000..418463c --- /dev/null +++ b/metamod/src/core/schemasystem.cpp @@ -0,0 +1,48 @@ +#include "schemasystem.h" +#include "virtual.h" +#include + +void CSchemaSystemTypeScope::FindDeclaredClass(SchemaClassInfoData_t*& pClassInfo, const char* pszClassName) +{ +#if WIN32 + CallVFunc(this, pClassInfo, pszClassName); +#else + pClassInfo = CallVFunc(this, pszClassName); +#endif +} + +CSchemaSystemTypeScope* CSchemaSystem::FindTypeScopeForModule(const char* szpModuleName) +{ + return CallVFunc(this, szpModuleName, nullptr); +} + +CSchemaSystemTypeScope* CSchemaSystem::GetServerTypeScope() +{ +#if WIN32 + static CSchemaSystemTypeScope* pServerTypeScope = FindTypeScopeForModule("server.dll"); +#else + static CSchemaSystemTypeScope* pServerTypeScope = FindTypeScopeForModule("libserver.so")); +#endif + + return pServerTypeScope; +} + +int32_t CSchemaSystem::GetServerOffset(const char* pszClassName, const char* pszPropName) +{ + SchemaClassInfoData_t* pClassInfo = nullptr; + GetServerTypeScope()->FindDeclaredClass(pClassInfo, pszClassName); + if (pClassInfo) + { + for (int i = 0; i < pClassInfo->m_iFieldsCount; i++) + { + auto& pFieldData = pClassInfo->m_pFieldsData[i]; + + if (std::strcmp(pFieldData.m_pszName, pszPropName) == 0) + { + return pFieldData.m_iOffset; + } + } + } + + return -1; +} \ No newline at end of file diff --git a/metamod/src/core/schemasystem.h b/metamod/src/core/schemasystem.h new file mode 100644 index 0000000..26a0bf2 --- /dev/null +++ b/metamod/src/core/schemasystem.h @@ -0,0 +1,64 @@ +#ifndef WST_SCHEMASYSTEM_H +#define WST_SCHEMASYSTEM_H + +#ifdef _WIN32 +#pragma once +#endif + +#include +#include + +struct SchemaClassFieldData_t +{ + const char* m_pszName; // 0x0000 + void* m_pSchemaType; // 0x0008 + int32_t m_iOffset; // 0x0010 + int32_t m_iMetaDataSize; // 0x0014 + void* m_pMetaData; // 0x0018 +}; + +struct SchemaClassInfoData_t +{ + char pad_0x0000[0x8]; // 0x0000 + + const char* m_pszName; // 0x0008 + const char* m_pszModule; // 0x0010 + + int m_iSize; // 0x0018 + int16_t m_iFieldsCount; // 0x001C + + int16_t m_iStaticSize; // 0x001E + int16_t m_iMetadataSize; // 0x0020 + int16_t m_iUnk1; // 0x0022 + int16_t m_iUnk2; // 0x0024 + int16_t m_iUnk3; // 0x0026 + + SchemaClassFieldData_t* m_pFieldsData; // 0x0028 +}; + +class CSchemaSystemTypeScope +{ +public: + void FindDeclaredClass(SchemaClassInfoData_t*& pClassInfo, const char* pszClassName); +}; + +class CSchemaSystem +{ +public: + CSchemaSystemTypeScope* FindTypeScopeForModule(const char* szpModuleName); + CSchemaSystemTypeScope* GetServerTypeScope(); + int32_t GetServerOffset(const char* pszClassName, const char* pszPropName); +}; + + + +#define SCHEMA_FIELD(type, className, propName) \ + std::add_lvalue_reference_t propName() \ + { \ + static const int32_t offset = Framework::SchemaSystem()->GetServerOffset(#className, #propName); \ + return *reinterpret_cast>(reinterpret_cast(this) + offset); \ + } + + + +#endif //WST_SCHEMASYSTEM_H diff --git a/metamod/src/core/virtual.h b/metamod/src/core/virtual.h new file mode 100644 index 0000000..25e11ad --- /dev/null +++ b/metamod/src/core/virtual.h @@ -0,0 +1,53 @@ +#ifndef WST_VIRTUAL_H +#define WST_VIRTUAL_H + +#include "platform.h" + +#define CALL_VIRTUAL(retType, idx, ...) \ + CallVirtual(idx, __VA_ARGS__) + + +template +constexpr T CallVFunc(void* pThis, Args... args) noexcept +{ + return reinterpret_cast (reinterpret_cast(pThis)[0][index])(pThis, args...); +} + +template +inline T GetVMethod(uint32 uIndex, void *pClass) +{ + if (!pClass) + { + Warning("Tried getting virtual function from a null class.\n"); + return T(); + } + + void **pVTable = *static_cast(pClass); + if (!pVTable) + { + Warning("Tried getting virtual function from a null vtable.\n"); + return T(); + } + + return reinterpret_cast(pVTable[uIndex]); +} + +template +inline T CallVirtual(uint32 uIndex, void *pClass, Args... args) +{ +#ifdef _WIN32 + auto pFunc = GetVMethod(uIndex, pClass); +#else + auto pFunc = GetVMethod(uIndex, pClass); +#endif + if (!pFunc) + { + Warning("Tried calling a null virtual function.\n"); + return T(); + } + + return pFunc(pClass, args...); +} + + +#endif //WST_VIRTUAL_H diff --git a/metamod/src/wst.cpp b/metamod/src/wst.cpp index 552a800..95cef42 100644 --- a/metamod/src/wst.cpp +++ b/metamod/src/wst.cpp @@ -1,5 +1,19 @@ +// Hack to get my IDE to work remove this before compiling +//#define COMPILER_MSVC + #include "wst.h" +#include +#include +#include +#include + +#include "core/framework.h" +#include "core/module.h" +#include "core/virtual.h" +#include "core/cbaseentity.h" + + void Message(const char *msg, ...) { va_list args; @@ -96,25 +110,70 @@ CON_COMMAND_F(wst_mm_save_record, "Save a record to disk", FCVAR_GAMEDLL | FCVAR } } +typedef void (*HostSay)(CBaseEntity*, CCommand&, bool, int, const char*); +static HostSay m_pHostSay = nullptr; + +void DetourHostSay(SC_CBaseEntity* pController, CCommand& args, bool teamonly, int unk1, +const char* unk2) +{ + IGameEvent *pEvent = Framework::GameEventManager()->CreateEvent("player_chat", true); + + if (pEvent) + { + pEvent->SetBool("teamonly", teamonly); + pEvent->SetInt("userid", pController->m_pEntity->m_EHandle.GetEntryIndex()); + pEvent->SetString("text", args[1]); + + Framework::GameEventManager()->FireEvent(pEvent, true); + } + + // if the text starts with ! or / then we don't call m_pHostSay + // check args length to prevent crash + if (*args[1] == '/' || *args[1] == '!') { + return; + } + + m_pHostSay(pController, args, teamonly, unk1, unk2); +} + bool WSTPlugin::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) { PLUGIN_SAVEVARS(); Message("Plugin Loaded\n"); - // This cursed shit is required to register console commands, yay we have to set some rando ass global with some magical incarntation - // see hl2sdk/tier1/convar.cpp - GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); - GET_V_IFACE_ANY(GetFileSystemFactory, g_pFullFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetEngineFactory, Framework::CVar(), ICvar, CVAR_INTERFACE_VERSION); + GET_V_IFACE_ANY(GetFileSystemFactory, Framework::FileSystem(), IFileSystem, FILESYSTEM_INTERFACE_VERSION); + GET_V_IFACE_ANY(GetEngineFactory, Framework::NetworkServerService(), INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION); + GET_V_IFACE_ANY(GetServerFactory, Framework::Source2Server(), ISource2Server, SOURCE2SERVER_INTERFACE_VERSION); + GET_V_IFACE_CURRENT(GetEngineFactory, Framework::EngineServer(), IVEngineServer2, INTERFACEVERSION_VENGINESERVER); + + Framework::GameEventManager() = (IGameEventManager2 *)(CALL_VIRTUAL(uintptr_t, 91, Framework::Source2Server()) - 8); + Framework::SchemaSystem() = (CSchemaSystem*)Framework::SchemaSystemModule().FindInterface(SCHEMASYSTEM_INTERFACE_VERSION); + + // https://github.com/Source2ZE/CS2Fixes/blob/main/gamedata/cs2fixes.games.txt + // For now only detour HostSay on windows, for linux tell people to use CounterStrikeSharp +#ifdef WIN32 + m_pHostSay = (HostSay)Framework::Server().FindSignature(R"(\x44\x89\x4C\x24\x2A\x44\x88\x44\x24\x2A\x55\x53\x56\x57\x41\x54\x41\x55)"); + auto m_hook = funchook_create(); + funchook_prepare(m_hook, (void**)&m_pHostSay, (void*)&DetourHostSay); + funchook_install(m_hook, 0); +#else + // m_pHostSay = (HostSay)Framework::Server().FindSignature(R"(\x55\x48\x89\xE5\x41\x57\x49\x89\xFF\x41\x56\x41\x55\x41\x54\x4D\x89\xC4)"); +#endif // Call register convars + g_pCVar = Framework::CVar(); // set magic global for metamod ConVar_Register(FCVAR_GAMEDLL | FCVAR_HIDDEN); return true; } + bool WSTPlugin::Unload(char *error, size_t maxlen) { + ConVar_Unregister(); + return true; } @@ -128,8 +187,10 @@ bool WSTPlugin::Unpause(char *error, size_t maxlen) return true; } + void WSTPlugin::AllPluginsLoaded() { + } const char *WSTPlugin::GetLicense() diff --git a/metamod/src/wst.h b/metamod/src/wst.h index 2b6c612..efed027 100644 --- a/metamod/src/wst.h +++ b/metamod/src/wst.h @@ -1,24 +1,29 @@ #pragma once #include +#include +#include "igameevents.h" + class WSTPlugin : public ISmmPlugin, public IMetamodListener { public: - bool Load(PluginId id, ISmmAPI* ismm, char* error, size_t maxlen, bool late); - bool Unload(char* error, size_t maxlen); - bool Pause(char* error, size_t maxlen); - bool Unpause(char* error, size_t maxlen); + bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late); + bool Unload(char *error, size_t maxlen); + bool Pause(char *error, size_t maxlen); + bool Unpause(char *error, size_t maxlen); void AllPluginsLoaded(); +// void Hook_StartupServer(const GameSessionConfiguration_t &config, ISource2WorldSession *, const char *); + public: - const char* GetAuthor(); - const char* GetName(); - const char* GetDescription(); - const char* GetURL(); - const char* GetLicense(); - const char* GetVersion(); - const char* GetDate(); - const char* GetLogTag(); + const char *GetAuthor(); + const char *GetName(); + const char *GetDescription(); + const char *GetURL(); + const char *GetLicense(); + const char *GetVersion(); + const char *GetDate(); + const char *GetLogTag(); }; extern WSTPlugin g_WSTPlugin; \ No newline at end of file diff --git a/metamod/vendor/funchook/LICENSE b/metamod/vendor/funchook/LICENSE new file mode 100644 index 0000000..2da840d --- /dev/null +++ b/metamod/vendor/funchook/LICENSE @@ -0,0 +1,360 @@ +Funchook is distributed under the terms of the GNU General Public +License version 2 or later with the following clarification and +special exception. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you must extend this +exception to your version of the library. + +===================================================================== + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/metamod/vendor/funchook/README.md b/metamod/vendor/funchook/README.md new file mode 100644 index 0000000..419021f --- /dev/null +++ b/metamod/vendor/funchook/README.md @@ -0,0 +1,273 @@ +Funchook - an API hook library +============================== + +[![tests](https://github.com/kubo/funchook/actions/workflows/run-tests.yml/badge.svg?branch=master)](https://github.com/kubo/funchook/actions/workflows/run-tests.yml) + +This library depends on one of the following disassemblers. + +On x86_64 and x86 +* [diStorm3][] (default) +* [zydis][] (when `-DFUNCHOOK_DISASM=zydis` is passed to the `cmake` command) +* [capstone][] (when `-DFUNCHOOK_DISASM=capstone` is passed to the `cmake` command) + +On arm64 +* [capstone][] + +TODO +---- + +* write documents. + +News +---- + +### 2.0.0 (20XX-XX-XX) + +* Add `funchook_prepare_with_params()` to support prehook. +* Add `funchook_get_arg()` to get arguments in prehook. + +### 1.1.1 (2022-10-02) + +* More permissive check for page allocation mmap ([#25][]) +* Flush instruction cache for arm64. It does nothing for intel CPU. +* Disassember engine + * Upgrade capstone to 4.0.2 + * Upgrade distorm to 3.5.2 +* CMake + * Allow user to specify FUNCHOOK_CPU explicitly ([#19][]) + * Avoid polluting global include and link dirs ([#20][]) + * Use target based compile options for gcc's -Wall ([#21][]) + * Use ExternalProject_add to download captone only ([#30][]) + * Add option FUNCHOOK_INSTALL ([#31][]) + * Use "FUNCHOOK_CPU MATCHES " ([#32][]) +* Documentation + * added example usage from python ([#22][]) +* Fix tests on Android ([#29][]) + +### 1.1.0 (2020-03-22) + +* Arm64 Linux support. [capstone][] is used as the disassembler library on arm64. +* Options to use [zydis][] and [capstone][] as a disassembler library on x86_64 and x86. +* `extern "C"` was added in funchook.h for C++. ([#15][]) +* Libc-compatible functions were removed to simplify code. + +### 1.0.0 (2020-01-19) + +* [diStorm3][] is used as the disassembler library. +* Libc-compatible functions were implemented on Linux in order not to hook function calls issued by funchook itself. + +Supported Platforms +------------------- + +* Linux x86_64 +* Linux x86 +* Linux arm64 (since 1.1.0) +* macOS x86_64 (Functions in executables cannot be hooked when Xcode version >= 11.0. (*1)) +* Windows x64 (except C-runtime functions under [Wine][]) +* Windows 32-bit + +*1 [`mprotect`] fails with EACCES. + +Unsupported Platforms +--------------------- + +* macOS arm64 (*1) + +*1 I received a mail that [`mprotect`] failed with `EINVAL`. Apple seems to prevent executable memory regions from being writable. + +Compilation and installation +----------- + +### Unix + +```shell +$ git clone --recursive https://github.com/kubo/funchook.git +$ mkdir build +$ cd build +$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/path/to/install/directory ../funchook +$ make +$ make install +``` + +* Available [`CMAKE_BUILD_TYPE`][] values are empty(default), `Debug`, `Release`, `RelWithDebInfo`(release build with debug information) and `MinSizeRel`. +* When [`CMAKE_INSTALL_PREFIX`][] isn't set, funchook is installed at `/usr/local`. + + installed files: + * `${CMAKE_INSTALL_PREFIX}/include/funchook.h` (header file) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.so` (symbolic link to `libfunchook.so.1`) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.so.1` ([soname][]; symbolic link to `libfunchook.so.1.1.0`) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.so.1.1.0` (shared library) + * `${CMAKE_INSTALL_PREFIX}/lib/libfunchook.a` (static library) + +### Windows + +Here is an example to compile funchook with Visual Studio 2017 Win64. +Change the argument of `-G` to use other compilers. + +```shell +$ git clone --recursive https://github.com/kubo/funchook.git +$ mkdir build +$ cd build +$ cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX=c:\path\to\install\directory ..\funchook +$ cmake --build . --config Release --target INSTALL +``` + +* Available `-G` arguments (generators) are listed in the output of `cmake --help`. +* Available `--config` arguments are `Debug`(default), `Release`, `RelWithDebInfo` and `MinSizeRel`. +* When [`CMAKE_INSTALL_PREFIX`][] isn't set, funchook is installed at `c:\Program Files\funchook`. + + installed files: + * `${CMAKE_INSTALL_PREFIX}\include\funchook.h` (header file) + * `${CMAKE_INSTALL_PREFIX}\bin\funchook.dll` (shared library) + * `${CMAKE_INSTALL_PREFIX}\bin\funchook.pdb` (debug file for `funchook.dll` when `--config` is `Debug` or `RelWithDebInfo`) + * `${CMAKE_INSTALL_PREFIX}\lib\funchook.lib` (static library) + * `${CMAKE_INSTALL_PREFIX}\lib\funchook_dll.lib` (import library for `funchook.dll`) + +Example +------- + +```c +static ssize_t (*send_func)(int sockfd, const void *buf, size_t len, int flags); +static ssize_t (*recv_func)(int sockfd, void *buf, size_t len, int flags); + +static ssize_t send_hook(int sockfd, const void *buf, size_t len, int flags); +{ + ssize_t rv; + + ... do your task: logging, etc. ... + rv = send_func(sockfd, buf, len, flags); /* call the original send(). */ + ... do your task: logging, checking the return value, etc. ... + return rv; +} + +static ssize_t recv_hook(int sockfd, void *buf, size_t len, int flags); +{ + ssize_t rv; + + ... do your task: logging, etc. ... + rv = recv_func(sockfd, buf, len, flags); /* call the original recv(). */ + ... do your task: logging, checking received data, etc. ... + return rv; +} + +int install_hooks() +{ + funchook_t *funchook = funchook_create(); + int rv; + + /* Prepare hooking. + * The return value is used to call the original send function + * in send_hook. + */ + send_func = send; + rv = funchook_prepare(funchook, (void**)&send_func, send_hook); + if (rv != 0) { + /* error */ + ... + } + + /* ditto */ + recv_func = recv; + rv = funchook_prepare(funchook, (void**)&recv_func, recv_hook); + if (rv != 0) { + /* error */ + ... + } + + /* Install hooks. + * The first 5-byte code of send() and recv() are changed respectively. + */ + rv = funchook_install(funchook, 0); + if (rv != 0) { + /* error */ + ... + } +} + +``` + +Example - Using Python ctypes +----------------------------- +```python +# should work on python 2.7/3 windows/linux + +# load funchook +import ctypes +fh_lib = ctypes.cdll.LoadLibrary('/path/to/funchook/dll/or/so') + +# define signatures +funchook_create = fh_lib.funchook_create +funchook_create.restype = ctypes.c_void_p +funchook_create.argtypes = [] + +funchook_prepare = fh_lib.funchook_prepare +funchook_prepare.restype = ctypes.c_ssize_t +funchook_prepare.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] + +funchook_install = fh_lib.funchook_install +funchook_install.restype = ctypes.c_ssize_t +funchook_install.argtypes = [ctypes.c_void_p, ctypes.c_int] + +PySys_WriteStdout = ctypes.pythonapi.PySys_WriteStdout +PySys_WriteStdout.restype = None +PySys_WriteStdout.argtypes=[ctypes.c_char_p] + +# must keep those references alive, or stuff will be GC'd and weird errors will occur +global orig_write, hook, orig_write_ptr + +# create hook (this function will replace the original function) +hook_type = ctypes.PYFUNCTYPE(None, ctypes.c_char_p) +orig_write = None +def hook_impl(msg): + print('about to write: ' + str(msg)) # do what we want + orig_write(msg) # call the original function + +hook = hook_type(hook_impl) + +fh = funchook_create() +# create a pointer object with the function address +orig_write_ptr = ctypes.c_void_p(ctypes.c_void_p.from_address(ctypes.addressof(PySys_WriteStdout)).value) +# orig_write_ptr.value will get a ptr to the original PySys_WriteStdout and PySys_WriteStdout will now point to the hook +ret = funchook_prepare(fh, ctypes.addressof(orig_write_ptr), hook) +assert not ret, 'ret is ' + str(ret) +ret = funchook_install(fh, 0) +assert not ret, 'ret is ' + str(ret) +orig_write = hook_type.from_address(ctypes.addressof(orig_write_ptr)) +PySys_WriteStdout(b'hi there\n') +``` + +License +------- + +GPLv2 or later with a [GPL linking exception][]. + +You can use funchook in any software. Though funchook is licensed under +the GPL, it doesn't affect outside of funchook due to the linking exception. +You have no need to open your souce code under the GPL except funchook itself. + +If you modify funchook itself and release it, the modifed part must be +open under the GPL with or without the linking exception because funchook +itself is under the GPL. + +[diStorm3][] and [capstone][] are released under the 3-clause BSD license. +[zydis][] is released under the MIT license. They are compatible with the GPL. + +[GPL linking exception]: https://en.wikipedia.org/wiki/GPL_linking_exception +[diStorm3]: https://github.com/gdabah/distorm/ +[zydis]: https://github.com/zyantific/zydis +[capstone]: https://github.com/aquynh/capstone +[Wine]: https://www.winehq.org/ +[`CMAKE_BUILD_TYPE`]: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html +[`CMAKE_INSTALL_PREFIX`]: https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html +[soname]: https://en.wikipedia.org/wiki/Soname +[#15]: https://github.com/kubo/funchook/issues/15 +[#19]: https://github.com/kubo/funchook/pull/19 +[#20]: https://github.com/kubo/funchook/pull/20 +[#21]: https://github.com/kubo/funchook/pull/21 +[#22]: https://github.com/kubo/funchook/pull/22 +[#25]: https://github.com/kubo/funchook/pull/25 +[#29]: https://github.com/kubo/funchook/pull/29 +[#30]: https://github.com/kubo/funchook/pull/30 +[#31]: https://github.com/kubo/funchook/pull/31 +[#32]: https://github.com/kubo/funchook/pull/32 +[`mprotect`]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mprotect.2.html diff --git a/metamod/vendor/funchook/include/funchook.h b/metamod/vendor/funchook/include/funchook.h new file mode 100644 index 0000000..5c6ed34 --- /dev/null +++ b/metamod/vendor/funchook/include/funchook.h @@ -0,0 +1,167 @@ +/* + * This file is part of Funchook. + * https://github.com/kubo/funchook + * + * Funchook 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. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under + * terms of your choice, provided that you also meet, for each linked + * independent module, the terms and conditions of the license of that + * module. An independent module is a module which is not derived from or + * based on this library. If you modify this library, you may extend this + * exception to your version of the library, but you are not obliged to + * do so. If you do not wish to do so, delete this exception statement + * from your version. + * + * Funchook 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 Funchook. If not, see . + */ +#ifndef FUNCHOOK_H +#define FUNCHOOK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Only functions with FUNCHOOK_EXPORT are visible from outside of funchook.dll + * or libfunchook.so. Others are invisible. + */ +#ifdef FUNCHOOK_EXPORTS +#if defined(_WIN32) +#define FUNCHOOK_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define FUNCHOOK_EXPORT __attribute__((visibility("default"))) +#endif +#endif /* FUNCHOOK_EXPORTS */ +#ifndef FUNCHOOK_EXPORT +#define FUNCHOOK_EXPORT +#endif + +typedef struct funchook funchook_t; + +#define FUNCHOOK_ERROR_INTERNAL_ERROR -1 +#define FUNCHOOK_ERROR_SUCCESS 0 +#define FUNCHOOK_ERROR_OUT_OF_MEMORY 1 +#define FUNCHOOK_ERROR_ALREADY_INSTALLED 2 +#define FUNCHOOK_ERROR_DISASSEMBLY 3 +#define FUNCHOOK_ERROR_IP_RELATIVE_OFFSET 4 +#define FUNCHOOK_ERROR_CANNOT_FIX_IP_RELATIVE 5 +#define FUNCHOOK_ERROR_FOUND_BACK_JUMP 6 +#define FUNCHOOK_ERROR_TOO_SHORT_INSTRUCTIONS 7 +#define FUNCHOOK_ERROR_MEMORY_ALLOCATION 8 /* memory allocation error */ +#define FUNCHOOK_ERROR_MEMORY_FUNCTION 9 /* other memory function errors */ +#define FUNCHOOK_ERROR_NOT_INSTALLED 10 +#define FUNCHOOK_ERROR_NO_AVAILABLE_REGISTERS 11 +#define FUNCHOOK_ERROR_NO_SPACE_NEAR_TARGET_ADDR 12 + +#define FUNCHOOK_FLAG_THISCALL (1u << 0) +#define FUNCHOOK_FLAG_FASTCALL (1u << 1) + +typedef struct funchook_arg_handle funchook_arg_handle_t; + +typedef struct funchook_info { + void *original_target_func; + void *target_func; + void *trampoline_func; + void *hook_func; + void *user_data; + funchook_arg_handle_t *arg_handle; +} funchook_info_t; + +typedef void (*funchook_hook_t)(funchook_info_t *fi); + +typedef struct { + void *hook_func; + funchook_hook_t prehook; + void *user_data; + unsigned int flags; +} funchook_params_t; + +/** + * Create a funchook handle + * + * @return allocated funchook handle. NULL when out-of-memory. + */ +FUNCHOOK_EXPORT funchook_t *funchook_create(void); + +/** + * Prepare hooking + * + * @param funchook a funchook handle created by funchook_create() + * @param target_func function pointer to be intercepted. The pointer to trampoline function is set on success. + * @param hook_func function pointer which is called istead of target_func + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_prepare(funchook_t *funchook, void **target_func, void *hook_func); + +FUNCHOOK_EXPORT int funchook_prepare_with_params(funchook_t *funchook, + void **target_func, const funchook_params_t *params); + +/** + * Install hooks prepared by funchook_prepare(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_install(funchook_t *funchook, int flags); + +/** + * Uninstall hooks installed by funchook_install(). + * + * @param funchook a funchook handle created by funchook_create() + * @param flags reserved. Set zero. + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_uninstall(funchook_t *funchook, int flags); + +/** + * Destroy a funchook handle + * + * @param funchook a funchook handle created by funchook_create() + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_destroy(funchook_t *funchook); + +/** + * Get error message + * + * @param funchook a funchook handle created by funchook_create() + * @return pointer to buffer containing error message + */ +FUNCHOOK_EXPORT const char *funchook_error_message(const funchook_t *funchook); + +/** + * Set log file name to debug funchook itself. + * + * @param name log file name + * @return error code. one of FUNCHOOK_ERROR_*. + */ +FUNCHOOK_EXPORT int funchook_set_debug_file(const char *name); + +/* This function is under developemnt. It will be used by C++ template functions later. */ +FUNCHOOK_EXPORT void *funchook_arg_get_int_reg_addr(const funchook_arg_handle_t *arg_handle, int pos); + +/* This function is under developemnt. It will be used by C++ template functions later. */ +FUNCHOOK_EXPORT void *funchook_arg_get_flt_reg_addr(const funchook_arg_handle_t *arg_handle, int pos); + +/* This function is under developemnt. It will be used by C++ template functions later. */ +FUNCHOOK_EXPORT void *funchook_arg_get_stack_addr(const funchook_arg_handle_t *arg_handle, int pos); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/metamod/vendor/funchook/lib/distorm.lib b/metamod/vendor/funchook/lib/distorm.lib new file mode 100644 index 0000000..746006f Binary files /dev/null and b/metamod/vendor/funchook/lib/distorm.lib differ diff --git a/metamod/vendor/funchook/lib/funchook.lib b/metamod/vendor/funchook/lib/funchook.lib new file mode 100644 index 0000000..c321ae5 Binary files /dev/null and b/metamod/vendor/funchook/lib/funchook.lib differ diff --git a/metamod/vendor/funchook/lib/libdistorm.a b/metamod/vendor/funchook/lib/libdistorm.a new file mode 100644 index 0000000..7094a7b Binary files /dev/null and b/metamod/vendor/funchook/lib/libdistorm.a differ diff --git a/metamod/vendor/funchook/lib/libfunchook.a b/metamod/vendor/funchook/lib/libfunchook.a new file mode 100644 index 0000000..1b25984 Binary files /dev/null and b/metamod/vendor/funchook/lib/libfunchook.a differ diff --git a/scripts/vscripts/wst.lua b/scripts/vscripts/wst.lua index 71e33f5..68031f5 100644 --- a/scripts/vscripts/wst.lua +++ b/scripts/vscripts/wst.lua @@ -300,7 +300,7 @@ Convars:RegisterCommand("wst_top", function() for i, p in ipairs(topPlayers) do local position, total_players = getPlayerPosition(p.steam_id) - SendTextToClient(player, position .. "/" .. total_players .. " " .. p.name .. " " .. p.time) + SendTextToClient(player, position .. "/" .. total_players .. " " .. p.name .. " " .. FormatTime(p.time)) end end, nil, 0) @@ -426,6 +426,30 @@ ListenToGameEvent("player_spawn", function(event) end end, nil) +ListenToGameEvent("player_chat", function(event) + -- This is off by 1 from the userid we get in lua + -- IE first player.user_id to join is 1 in lua but 0 from C++ + -- Unsure if s2ze just sending it wrong or not + local userid = event.userid - 1 + + local chatPlayer = nil + local players = Entities:FindAllByClassname("player") + for i, player in ipairs(players) + do + if player.user_id == userid then + chatPlayer = players[i] + break + end + end + if (chatPlayer == nil) then + return + end + if event.text == "!r" or event.text == "/r" then + TeleportToStartZone(chatPlayer) + return + end +end, nil) + function ServerMessage() ScriptPrintMessageChatAll(ConvertTextToColoredChatString( "Type wst_help in console (~) for surf timer commands"))