diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..2b8abaf --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,135 @@ +name: build + +on: + push: + paths-ignore: + - LICENSE + - README.md + pull_request: + paths-ignore: + - LICENSE + - README.md + +jobs: + build: + name: build ${{ matrix.engine }} with sm${{ matrix.sm_version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-20.04 + - windows-latest + + engine: + - "css" + - "csgo" + - "tf2" + - "l4d2" + + sm_version: + - "1.10" + + include: + - sm_version: "1.10" + branch: "1.10-dev" + + - engine: "css" + + - engine: "csgo" + + - engine: "tf2" + + - engine: "l4d2" + + - os: ubuntu-20.04 + + - os: windows-latest + + steps: + - name: Prepare env + shell: bash + run: | + echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_ENV + + - name: Install (Linux) + if: runner.os == 'Linux' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y clang g++-multilib + echo "CC=clang" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + + - name: Add msbuild to PATH (Windows) + if: runner.os == 'Windows' + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Install (Windows) + if: runner.os == 'Windows' + shell: cmd + run: | + :: See https://github.com/microsoft/vswhere/wiki/Find-VC + for /f "usebackq delims=*" %%i in (`vswhere -latest -property installationPath`) do ( + call "%%i"\Common7\Tools\vsdevcmd.bat -arch=x86 -host_arch=x64 + ) + + :: Loop over all environment variables and make them global. + for /f "delims== tokens=1,2" %%a in ('set') do ( + echo>>"%GITHUB_ENV%" %%a=%%b + ) + + - name: Checking out HL2SDK + uses: actions/checkout@v2 + with: + repository: alliedmodders/hl2sdk + ref: ${{ matrix.engine }} + path: hl2sdk-${{ matrix.engine }} + submodules: recursive + + - name: Checking out SourceMod + uses: actions/checkout@v2 + with: + repository: alliedmodders/sourcemod + ref: ${{ matrix.branch }} + path: sourcemod-${{ matrix.sm_version }} + submodules: recursive + + - name: Checking out MM:Source + uses: actions/checkout@v2 + with: + repository: alliedmodders/metamod-source + ref: ${{ matrix.branch }} + path: metamod-${{ matrix.sm_version }} + + - name: Checking out AMBuild + uses: actions/checkout@v2 + with: + repository: alliedmodders/ambuild + path: ambuild + + - name: Setting up Python + uses: actions/setup-python@v2 + + - name: Setting up ambuild + working-directory: ambuild + run: python setup.py install + + - name: Checking out own repository + uses: actions/checkout@v2 + with: + path: sendproxy + + - name: Compiling sendproxy files + working-directory: sendproxy + run: | + mkdir build + cd build + python ../configure.py --enable-optimize --sm-path="${{ github.workspace }}/sourcemod-${{ matrix.sm_version }}" --mms-path="${{ github.workspace }}/metamod-${{ matrix.sm_version }}" --sdks=${{ matrix.engine }} + ambuild + + - name: Uploading package + uses: actions/upload-artifact@v2 + with: + name: sendproxy-${{ matrix.engine }}-sm${{ matrix.sm_version }}-${{ matrix.os }}-${{ env.GITHUB_SHA_SHORT }} + path: sendproxy/build/package \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..666972f --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +build/ +build_*/ diff --git a/AMBuildScript b/AMBuildScript new file mode 100644 index 0000000..c30ff9a --- /dev/null +++ b/AMBuildScript @@ -0,0 +1,433 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os, sys + +# Simple extensions do not need to modify this file. + +class SDK(object): + def __init__(self, sdk, ext, aDef, name, platform, dir): + self.folder = 'hl2sdk-' + dir + self.envvar = sdk + self.ext = ext + self.code = aDef + self.define = name + self.platform = platform + self.name = dir + self.path = None + +WinOnly = ['windows'] +WinLinux = ['windows', 'linux'] +WinLinuxMac = ['windows', 'linux', 'mac'] + +PossibleSDKs = { + 'episode1': SDK('HL2SDK', '1.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'), + 'ep2': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'), + 'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinuxMac, 'css'), + 'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinuxMac, 'hl2dm'), + 'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinuxMac, 'dods'), + 'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'), + 'tf2': SDK('HL2SDKTF2', '2.tf2', '11', 'TF2', WinLinuxMac, 'tf2'), + 'l4d': SDK('HL2SDKL4D', '2.l4d', '12', 'LEFT4DEAD', WinLinuxMac, 'l4d'), + 'nucleardawn': SDK('HL2SDKND', '2.nd', '13', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'), + 'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '15', 'LEFT4DEAD2', WinLinuxMac, 'l4d2'), + 'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'), + 'swarm': SDK('HL2SDK-SWARM', '2.swarm', '16', 'ALIENSWARM', WinOnly, 'swarm'), + 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'), + 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'), + 'csgo': SDK('HL2SDKCSGO', '2.csgo', '20', 'CSGO', WinLinuxMac, 'csgo'), + 'dota': SDK('HL2SDKDOTA', '2.dota', '21', 'DOTA', WinLinuxMac, 'dota'), + 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'), + 'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinLinux, 'blade'), + 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'), + 'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'), + 'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'), +} + +def ResolveEnvPath(env, folder): + if env in os.environ: + path = os.environ[env] + if os.path.isdir(path): + return path + return None + + head = os.getcwd() + oldhead = None + while head != None and head != oldhead: + path = os.path.join(head, folder) + if os.path.isdir(path): + return path + oldhead = head + head, tail = os.path.split(head) + + return None + +def Normalize(path): + return os.path.abspath(os.path.normpath(path)) + +class ExtensionConfig(object): + def __init__(self): + self.sdks = {} + self.binaries = [] + self.extensions = [] + self.generated_headers = None + self.mms_root = None + self.sm_root = None + + @property + def tag(self): + if builder.options.debug == '1': + return 'Debug' + return 'Release' + + def detectSDKs(self): + sdk_list = builder.options.sdks.split(',') + use_all = sdk_list[0] == 'all' + use_present = sdk_list[0] == 'present' + + for sdk_name in PossibleSDKs: + sdk = PossibleSDKs[sdk_name] + if builder.target_platform in sdk.platform: + if builder.options.hl2sdk_root: + sdk_path = os.path.join(builder.options.hl2sdk_root, sdk.folder) + else: + sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder) + if sdk_path is None or not os.path.isdir(sdk_path): + if use_all or sdk_name in sdk_list: + raise Exception('Could not find a valid path for {0}'.format(sdk.envvar)) + continue + if use_all or use_present or sdk_name in sdk_list: + sdk.path = Normalize(sdk_path) + self.sdks[sdk_name] = sdk + + if len(self.sdks) < 1: + raise Exception('At least one SDK must be available.') + + if builder.options.sm_path: + self.sm_root = builder.options.sm_path + else: + self.sm_root = ResolveEnvPath('SOURCEMOD18', 'sourcemod-1.8') + if not self.sm_root: + self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod') + if not self.sm_root: + self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central') + + if not self.sm_root or not os.path.isdir(self.sm_root): + raise Exception('Could not find a source copy of SourceMod') + self.sm_root = Normalize(self.sm_root) + + if builder.options.mms_path: + self.mms_root = builder.options.mms_path + else: + self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10') + if not self.mms_root: + self.mms_root = ResolveEnvPath('MMSOURCE', 'metamod-source') + 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): + raise Exception('Could not find a source copy of Metamod:Source') + self.mms_root = Normalize(self.mms_root) + + def configure(self): + cxx = builder.DetectCompilers() + + if cxx.like('gcc'): + cxx.defines += [ + 'stricmp=strcasecmp', + '_stricmp=strcasecmp', + '_snprintf=snprintf', + '_vsnprintf=vsnprintf', + 'HAVE_STDINT_H', + 'GNUC', + ] + cxx.cflags += [ + '-pipe', + '-fno-strict-aliasing', + #'-Wall', + #'-Werror', + '-Wno-unused', + '-Wno-switch', + '-Wno-array-bounds', + '-msse', + '-m32', + ] + cxx.cxxflags += [ + '-std=c++14', + '-fpermissive', + ] + + have_gcc = cxx.vendor == 'gcc' + have_clang = cxx.vendor == 'clang' + if have_clang or (have_gcc and cxx.version >= '4'): + cxx.cflags += ['-fvisibility=hidden'] + cxx.cxxflags += ['-fvisibility-inlines-hidden', '-fno-rtti'] + if have_clang or (have_gcc and cxx.version >= '4.6'): + cxx.cflags += ['-Wno-narrowing'] + if (have_gcc and cxx.version >= '4.7') or (have_clang and cxx.version >= '3'): + cxx.cxxflags += ['-Wno-delete-non-virtual-dtor'] + if have_gcc and cxx.version >= '4.8': + cxx.cflags += ['-Wno-unused-result'] + if have_clang: + cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch'] + if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4': + cxx.cxxflags += ['-Wno-deprecated-register'] + else: + cxx.cxxflags += ['-Wno-deprecated'] + cxx.cflags += ['-Wno-sometimes-uninitialized'] + + cxx.linkflags += ['-m32'] + cxx.cxxflags += [ + '-fno-exceptions', + '-fno-threadsafe-statics', + '-Wno-non-virtual-dtor', + '-Wno-overloaded-virtual', + ] + + if have_gcc: + cxx.cflags += ['-mfpmath=sse'] + elif cxx.vendor == 'msvc': + if builder.options.debug == '1': + cxx.cflags += ['/MTd'] + cxx.linkflags += ['/NODEFAULTLIB:libcmt'] + else: + cxx.cflags += ['/MT'] + cxx.defines += [ + '_CRT_SECURE_NO_DEPRECATE', + '_CRT_SECURE_NO_WARNINGS', + '_CRT_NONSTDC_NO_DEPRECATE', + '_ITERATOR_DEBUG_LEVEL=0', + ] + cxx.cflags += [ + '/W3', + ] + cxx.cxxflags += [ + '/EHsc', + '/GR-', + '/TP', + ] + cxx.linkflags += [ + '/MACHINE:X86', + 'kernel32.lib', + 'user32.lib', + 'gdi32.lib', + 'winspool.lib', + 'comdlg32.lib', + 'advapi32.lib', + 'shell32.lib', + 'ole32.lib', + 'oleaut32.lib', + 'uuid.lib', + 'odbc32.lib', + 'odbccp32.lib', + ] + + # Optimization + if builder.options.opt == '1': + cxx.defines += ['NDEBUG'] + if cxx.like('gcc'): + cxx.cflags += ['-O3'] + elif cxx.like('msvc'): + cxx.cflags += ['/Ox', '/Zo', '/Oi', '/GL', '/Ot'] + cxx.linkflags += ['/OPT:ICF', '/OPT:REF'] + + # Debugging + if builder.options.debug == '1': + cxx.defines += ['DEBUG', '_DEBUG'] + if cxx.like('msvc'): + cxx.cflags += ['/Od', '/RTC1'] + + # This needs to be after our optimization flags which could otherwise disable it. + if cxx.vendor == 'msvc': + # Don't omit the frame pointer. + cxx.cflags += ['/Oy-'] + + # Platform-specifics + if builder.target_platform == 'linux': + cxx.defines += ['_LINUX', 'POSIX'] + cxx.linkflags += ['-lm'] + if cxx.vendor == 'gcc': + cxx.linkflags += ['-static-libgcc'] + elif cxx.vendor == 'clang': + cxx.linkflags += ['-lgcc_eh', '-s'] + elif builder.target_platform == 'mac': + cxx.defines += ['OSX', '_OSX', 'POSIX'] + cxx.cflags += ['-mmacosx-version-min=10.5'] + cxx.linkflags += [ + '-mmacosx-version-min=10.5', + '-arch', 'i386', + '-lstdc++', + '-stdlib=libstdc++', + ] + cxx.cxxflags += ['-stdlib=libstdc++'] + elif builder.target_platform == 'windows': + cxx.defines += ['WIN32', '_WINDOWS'] + + # Finish up. + cxx.includes += [ + os.path.join(self.sm_root, 'public'), + ] + + def ConfigureForHL2(self, binary, sdk): + compiler = binary.compiler + + if sdk.name == 'episode1': + mms_path = os.path.join(self.mms_root, 'core-legacy') + else: + mms_path = os.path.join(self.mms_root, 'core') + + compiler.cxxincludes += [ + os.path.join(mms_path), + os.path.join(mms_path, 'sourcehook'), + ] + + defines = ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs] + compiler.defines += defines + + paths = [ + ['public'], + ['public', 'engine'], + ['public', 'mathlib'], + ['public', 'vstdlib'], + ['public', 'tier0'], + ['public', 'tier1'] + ] + if sdk.name == 'episode1' or sdk.name == 'darkm': + paths.append(['public', 'dlls']) + paths.append(['game_shared']) + else: + paths.append(['public', 'game', 'server']) + paths.append(['public', 'toolframework']) + paths.append(['game', 'shared']) + paths.append(['common']) + + compiler.defines += ['SOURCE_ENGINE=' + sdk.code] + + if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'): + # The 2013 SDK already has these in public/tier0/basetypes.h + compiler.defines.remove('stricmp=strcasecmp') + compiler.defines.remove('_stricmp=strcasecmp') + compiler.defines.remove('_snprintf=snprintf') + compiler.defines.remove('_vsnprintf=vsnprintf') + + if compiler.like('msvc'): + if compiler.version >= 1900: + compiler.linkflags += ['legacy_stdio_definitions.lib'] + compiler.defines += ['COMPILER_MSVC', 'COMPILER_MSVC32'] + else: + compiler.defines += ['COMPILER_GCC'] + + # For everything after Swarm, this needs to be defined for entity networking + # to work properly with sendprop value changes. + if sdk.name in ['blade', 'insurgency', 'csgo', 'dota']: + compiler.defines += ['NETWORK_VARS_ENABLED'] + + if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota']: + if builder.target_platform in ['linux', 'mac']: + compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE'] + + if sdk.name == 'csgo' and builder.target_platform == 'linux': + compiler.linkflags += ['-lstdc++'] + + for path in paths: + compiler.cxxincludes += [os.path.join(sdk.path, *path)] + + if builder.target_platform == 'linux': + if sdk.name == 'episode1': + lib_folder = os.path.join(sdk.path, 'linux_sdk') + elif sdk.name in ['sdk2013', 'bms']: + lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32') + else: + lib_folder = os.path.join(sdk.path, 'lib', 'linux') + elif builder.target_platform == 'mac': + if sdk.name in ['sdk2013', 'bms']: + lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32') + else: + lib_folder = os.path.join(sdk.path, 'lib', 'mac') + + if builder.target_platform in ['linux', 'mac']: + if sdk.name in ['sdk2013', 'bms']: + compiler.postlink += [ + compiler.Dep(os.path.join(lib_folder, 'tier1.a')), + compiler.Dep(os.path.join(lib_folder, 'mathlib.a')) + ] + else: + compiler.postlink += [ + compiler.Dep(os.path.join(lib_folder, 'tier1_i486.a')), + compiler.Dep(os.path.join(lib_folder, 'mathlib_i486.a')) + ] + + if sdk.name in ['blade', 'insurgency', 'csgo', 'dota']: + compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))] + + dynamic_libs = [] + if builder.target_platform == 'linux': + if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2']: + dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so'] + elif sdk.name in ['l4d', 'blade', 'insurgency', 'csgo', 'dota']: + dynamic_libs = ['libtier0.so', 'libvstdlib.so'] + else: + dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so'] + elif builder.target_platform == 'mac': + compiler.linkflags.append('-liconv') + dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib'] + elif builder.target_platform == 'windows': + libs = ['tier0', 'tier1', 'vstdlib', 'mathlib'] + if sdk.name in ['swarm', 'blade', 'insurgency', 'csgo', 'dota']: + libs.append('interfaces') + for lib in libs: + lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib' + compiler.linkflags.append(compiler.Dep(lib_path)) + + for library in dynamic_libs: + source_path = os.path.join(lib_folder, library) + output_path = os.path.join(binary.localFolder, library) + + def make_linker(source_path, output_path): + def link(context, binary): + cmd_node, (output,) = context.AddSymlink(source_path, output_path) + return output + return link + + linker = make_linker(source_path, output_path) + compiler.linkflags[0:0] = [compiler.Dep(library, linker)] + + return binary + + def ConfigureForExtension(self, context, compiler): + compiler.cxxincludes += [ + os.path.join(context.currentSourcePath), + os.path.join(context.currentSourcePath, 'sdk'), + os.path.join(self.sm_root, 'public'), + os.path.join(self.sm_root, 'public', 'extensions'), + os.path.join(self.sm_root, 'sourcepawn', 'include'), + os.path.join(self.sm_root, 'public', 'amtl', 'amtl'), + os.path.join(self.sm_root, 'public', 'amtl'), + + # Remove when upgraded to SM 1.8 + os.path.join(self.sm_root, 'public', 'sourcepawn'), + ] + return compiler + + def HL2Project(self, context, name): + project = context.compiler.LibraryProject(name) + self.ConfigureForExtension(context, project.compiler) + return project + + def HL2Config(self, project, name, sdk): + binary = project.Configure(name, '{0} - {1}'.format(self.tag, sdk.name)) + return self.ConfigureForHL2(binary, sdk) + +Extension = ExtensionConfig() +Extension.detectSDKs() +Extension.configure() + +# Add additional buildscripts here +BuildScripts = [ + 'AMBuilder', +] + +if builder.backend == 'amb2': + BuildScripts += [ + 'PackageScript', + ] + +builder.RunBuildScripts(BuildScripts, { 'Extension': Extension}) diff --git a/AMBuilder b/AMBuilder new file mode 100644 index 0000000..908e7b9 --- /dev/null +++ b/AMBuilder @@ -0,0 +1,41 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os, sys + +projectName = 'sendproxy' + +# smsdk_ext.cpp will be automatically added later +sourceFiles = [ + 'extension.cpp', + 'natives.cpp', + 'interfaceimpl.cpp', + os.path.join(Extension.sm_root, 'public', 'CDetour', 'detours.cpp'), + os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c'), + os.path.join(Extension.sm_root, 'public', 'libudis86/decode.c'), + os.path.join(Extension.sm_root, 'public', 'libudis86/itab.c'), + os.path.join(Extension.sm_root, 'public', 'libudis86/syn-att.c'), + os.path.join(Extension.sm_root, 'public', 'libudis86/syn-intel.c'), + os.path.join(Extension.sm_root, 'public', 'libudis86/syn.c'), + os.path.join(Extension.sm_root, 'public', 'libudis86/udis86.c'), +] + +############### +# Make sure to edit PackageScript, which copies your files to their appropriate locations +# Simple extensions do not need to modify past this point. + +project = Extension.HL2Project(builder, projectName + '.ext') + +if os.path.isfile(os.path.join(builder.currentSourcePath, 'sdk', 'smsdk_ext.cpp')): + # Use the copy included in the project + project.sources += [os.path.join('sdk', 'smsdk_ext.cpp')] +else: + # Use the copy included with SM 1.6 and newer + project.sources += [os.path.join(Extension.sm_root, 'public', 'smsdk_ext.cpp')] + +project.sources += sourceFiles + +for sdk_name in Extension.sdks: + sdk = Extension.sdks[sdk_name] + + binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk) + +Extension.extensions = builder.Add(project) diff --git a/ISendProxy.h b/ISendProxy.h new file mode 100644 index 0000000..22b6dac --- /dev/null +++ b/ISendProxy.h @@ -0,0 +1,433 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_ISENDPROXY_ +#define _INCLUDE_ISENDPROXY_ + +//WARNING! Interface not tested yet, but you can test it by yourself and report about any errors to github: https://github.com/TheByKotik/sendproxy + +#include +#include +#include "dt_send.h" +#include "server_class.h" + +#define SMINTERFACE_SENDPROXY_NAME "ISendProxyInterface134" +#define SMINTERFACE_SENDPROXY_VERSION 0x134 + +class CBaseEntity; +class CBasePlayer; +class ISendProxyUnhookListener; + +using namespace SourceMod; + +enum class PropType : uint8_t +{ + Prop_Int = 0, + Prop_Float, + Prop_String, + Prop_Vector = 4, + Prop_Max +}; + +enum class CallBackType : uint8_t +{ + Callback_PluginFunction = 1, + Callback_CPPCallbackInterface //see ISendProxyCallbacks & ISendProxyChangeCallbacks +}; + +class ISendProxyUnhookListener +{ +public: + /* + * Calls when hook of the entity prop is removed + * + * @param pEntity Pointer to CBaseEntity object that was hooked + * @param pProp Pointer to SendProp that was hooked + * @param iType PropType of the prop + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @noreturn + */ + virtual void OnEntityPropHookRemoved(const CBaseEntity * pEntity, const SendProp * pProp, const PropType iType, const CallBackType iCallbackType, const void * pCallback) = 0; + /* + * Calls when hook of the gamerules prop is removed + * + * @param pProp Pointer to SendProp that was hooked + * @param iType PropType of the prop + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @noreturn + */ + virtual void OnGamerulesPropHookRemoved(const SendProp * pProp, const PropType iType, const CallBackType iCallbackType, const void * pCallback) = 0; +}; + +class ISendProxyCallbacks +{ +public: + /* + * Calls when proxy function of entity prop is called + * + * @param pEntity Pointer to CBaseEntity object that hooked + * @param pProp Pointer to SendProp that hooked + * @param pPlayer Pointer to CBasePlayer object of the client that should receive the changed value + * @param pValue Pointer to value of prop + * @param iType PropType of the prop + * @param iElement Element number + * + * @return true, to use changed value, false, to use original + */ + virtual bool OnEntityPropProxyFunctionCalls(const CBaseEntity * pEntity, const SendProp * pProp, const CBasePlayer * pPlayer, void * pValue, const PropType iType, const int iElement) = 0; + virtual bool OnEntityPropProxyFunctionCalls(const CBaseEntity * pEntity, const SendProp * pProp, void * pValue, const PropType iType, const int iElement) = 0; + + /* + * Calls when proxy function of gamerules prop is called + * + * @param pProp Pointer to SendProp that hooked + * @param pPlayer Pointer to CBasePlayer object of the client that should receive the changed value + * @param pValue Pointer to value of prop + * @param iType PropType of the prop + * @param iElement Element number + * + * @return true, to use changed value, false, to use original + */ + virtual bool OnGamerulesPropProxyFunctionCalls(const SendProp * pProp, const CBasePlayer * pPlayer, void * pValue, const PropType iType, const int iElement) = 0; + virtual bool OnGamerulesPropProxyFunctionCalls(const SendProp * pProp, void * pValue, const PropType iType, const int iElement) = 0; +}; + +class ISendProxyChangeCallbacks +{ +public: + /* + * Calls when prop of entity is changed + * + * @param pEntity Pointer to CBaseEntity object that hooked + * @param pProp Pointer to SendProp that hooked + * @param pNewValue Pointer to new value of prop + * @param pOldValue Pointer to old value of prop + * @param iType PropType of the prop + * @param iElement Element number + * + * @noreturn + */ + virtual void OnEntityPropChange(const CBaseEntity * pEntity, const SendProp * pProp, const void * pNewValue, const void * pOldValue, const PropType iType, const int iElement) = 0; + /* + * Calls when prop of gamerules is changed + * + * @param pProp Pointer to SendProp that hooked + * @param pNewValue Pointer to new value of prop + * @param pOldValue Pointer to old value of prop + * @param iType PropType of the prop + * @param iElement Element number + * + * @noreturn + */ + virtual void OnGamerulesPropChange(const SendProp * pProp, const void * pNewValue, const void * pOldValue, const PropType iType, const int iElement) = 0; +}; + +class ISendProxyManager : public SMInterface +{ +public: //SMInterface + virtual const char * GetInterfaceName() = 0; + virtual unsigned int GetInterfaceVersion() = 0; + +public: //ISendProxyManager + /* + * Hooks SendProp of entity, this hook removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be hooked + * @param pEntity Pointer to CBaseEntity object that should be hooked + * @param iType PropType of the prop + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop hooked, false otherwise + */ + virtual bool HookProxy(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client = false) = 0; + virtual bool HookProxy(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client = false) = 0; + /* + * Hooks gamerules SendProp, this hook removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be hooked + * @param iType PropType of the prop + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop hooked, false otherwise + */ + virtual bool HookProxyGamerules(IExtension * pMyself, SendProp * pProp, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client = false) = 0; + virtual bool HookProxyGamerules(IExtension * pMyself, const char * pProp, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client = false) = 0; + /* + * Unhooks SendProp of entity + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be unhooked + * @param pEntity Pointer to CBaseEntity object that should be unhooked + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop unhooked, false otherwise + * P.S. This function will trigger unhook listeners + */ + virtual bool UnhookProxy(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback) = 0; + virtual bool UnhookProxy(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback) = 0; + /* + * Unhooks gamerules SendProp + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be unhooked + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop unhooked, false otherwise + * P.S. This function will trigger unhook listeners + */ + virtual bool UnhookProxyGamerules(IExtension * pMyself, SendProp * pProp, CallBackType iCallbackType, void * pCallback) = 0; + virtual bool UnhookProxyGamerules(IExtension * pMyself, const char * pProp, CallBackType iCallbackType, void * pCallback) = 0; + /* + * Adds unhook listener to entity hook, so, when hook will be removed listener callback is called. This listener removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be listen + * @param pEntity Pointer to CBaseEntity object that should be listen + * @param iCallbackType Type of callback of entity hook + * @param pCallback Pointer to callback function / class of entity hook + * @param pListener Pointer to listener callback + * + * @return true, if listener installed, false otherwise + */ + virtual bool AddUnhookListener(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool AddUnhookListener(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Adds unhook listener to gamerules hook, so, when hook will removed listener callback is called. This listener removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be listen + * @param iCallbackType Type of callback of gamerules hook + * @param pCallback Pointer to callback function / class of gamerules hook + * @param pListener Pointer to listener callback + * + * @return true, if listener installed, false otherwise + */ + virtual bool AddUnhookListenerGamerules(IExtension * pMyself, SendProp * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool AddUnhookListenerGamerules(IExtension * pMyself, const char * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Removes unhook listener from entity hook + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that is listening + * @param pEntity Pointer to CBaseEntity object that is listening + * @param iCallbackType Type of callback of entity hook + * @param pCallback Pointer to callback function / class of entity hook + * @param pListener Pointer to listener callback + * + * @return true, if listener removed, false otherwise + */ + virtual bool RemoveUnhookListener(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool RemoveUnhookListener(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Removes unhook listener from gamerules hook + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that is listening + * @param iCallbackType Type of callback of gamerules hook + * @param pCallback Pointer to callback function / class of gamerules hook + * @param pListener Pointer to listener callback + * + * @return true, if listener removed, false otherwise + */ + virtual bool RemoveUnhookListenerGamerules(IExtension * pMyself, SendProp * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool RemoveUnhookListenerGamerules(IExtension * pMyself, const char * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Hooks element of SendProp array of entity, this hook removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be hooked + * @param pEntity Pointer to CBaseEntity object that should be hooked + * @param iType PropType of the prop + * @param iElement Element number + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop hooked, false otherwise + */ + virtual bool HookProxyArray(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + virtual bool HookProxyArray(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + + /* + * Unhooks element of SendProp array of entity + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be unhooked + * @param pEntity Pointer to CBaseEntity object that should be unhooked + * @param iElement Element number + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop unhooked, false otherwise + * P.S. This function will trigger unhook listeners + */ + virtual bool UnhookProxyArray(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + virtual bool UnhookProxyArray(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + /* + * Hooks element of gamerules SendProp array, this hook removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be hooked + * @param iType PropType of the prop + * @param iElement Element number + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop hooked, false otherwise + */ + virtual bool HookProxyArrayGamerules(IExtension * pMyself, SendProp * pProp, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + virtual bool HookProxyArrayGamerules(IExtension * pMyself, const char * pProp, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + + /* + * Unhooks element of gamerules SendProp array + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be unhooked + * @param iElement Element number + * @param iCallbackType Type of callback + * @param pCallback Pointer to callback function / class + * + * @return true, if prop unhooked, false otherwise + * P.S. This function will trigger unhook listeners + */ + virtual bool UnhookProxyArrayGamerules(IExtension * pMyself, SendProp * pProp, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + virtual bool UnhookProxyArrayGamerules(IExtension * pMyself, const char * pProp, int iElement, CallBackType iCallbackType, void * pCallback) = 0; + + /* + * Adds unhook listener to entity array hook, so, when hook will be removed listener callback is called. This listener removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be listen + * @param pEntity Pointer to CBaseEntity object that should be listen + * @param iElement Element number + * @param iCallbackType Type of callback of entity hook + * @param pCallback Pointer to callback function / class of entity hook + * @param pListener Pointer to listener callback + * + * @return true, if listener installed, false otherwise + */ + virtual bool AddUnhookListenerArray(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool AddUnhookListenerArray(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Adds unhook listener to gamerules array hook, so, when hook will removed listener callback is called. This listener removes automatically when extension in unloaded. + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that should be listen + * @param iElement Element number + * @param iCallbackType Type of callback of gamerules hook + * @param pCallback Pointer to callback function / class of gamerules hook + * @param pListener Pointer to listener callback + * + * @return true, if listener installed, false otherwise + */ + virtual bool AddUnhookListenerArrayGamerules(IExtension * pMyself, SendProp * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool AddUnhookListenerArrayGamerules(IExtension * pMyself, const char * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Removes unhook listener from entity array hook + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that is listening + * @param pEntity Pointer to CBaseEntity object that is listening + * @param iElement Element number + * @param iCallbackType Type of callback of entity hook + * @param pCallback Pointer to callback function / class of entity hook + * @param pListener Pointer to listener callback + * + * @return true, if listener removed, false otherwise + */ + virtual bool RemoveUnhookListenerArray(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool RemoveUnhookListenerArray(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Removes unhook listener from gamerules array hook + * + * @param pMyself Pointer to IExtension interface of current extension, must be valid! + * @param pProp Pointer to SendProp / name of the prop that is listening + * @param iElement Element number + * @param iCallbackType Type of callback of gamerules hook + * @param pCallback Pointer to callback function / class of gamerules hook + * @param pListener Pointer to listener callback + * + * @return true, if listener removed, false otherwise + */ + virtual bool RemoveUnhookListenerArrayGamerules(IExtension * pMyself, SendProp * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + virtual bool RemoveUnhookListenerArrayGamerules(IExtension * pMyself, const char * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) = 0; + /* + * Checks if proxy is hooked + * + * @param pProp Pointer to SendProp / name of the prop that should be checked + * @param pEntity Pointer to CBaseEntity object that should be checked + * + * @return true, if is hooked, false otherwise + */ + virtual bool IsProxyHooked(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity) const = 0; + virtual bool IsProxyHooked(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity) const = 0; + /* + * Checks if gamerules proxy is hooked + * + * @param pProp Pointer to SendProp / name of the prop that should be checked + * + * @return true, if is hooked, false otherwise + */ + virtual bool IsProxyHookedGamerules(IExtension * pMyself, SendProp * pProp) const = 0; + virtual bool IsProxyHookedGamerules(IExtension * pMyself, const char * pProp) const = 0; + /* + * Checks if proxy array is hooked + * + * @param pProp Pointer to SendProp / name of the prop that should be checked + * @param pEntity Pointer to CBaseEntity object that should be checked + * @param iElement Element number + * + * @return true, if is hooked, false otherwise + */ + virtual bool IsProxyHookedArray(IExtension * pMyself, SendProp * pProp, CBaseEntity * pEntity, int iElement) const = 0; + virtual bool IsProxyHookedArray(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, int iElement) const = 0; + /* + * Checks if gamerules proxy is hooked + * + * @param pProp Pointer to SendProp / name of the prop that should be checked + * @param iElement Element number + * + * @return true, if is hooked, false otherwise + */ + virtual bool IsProxyHookedArrayGamerules(IExtension * pMyself, SendProp * pProp, int iElement) const = 0; + virtual bool IsProxyHookedArrayGamerules(IExtension * pMyself, const char * pProp, int iElement) const = 0; +}; + +#endif diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 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 +state 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU 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. But first, please read +. diff --git a/PackageScript b/PackageScript new file mode 100644 index 0000000..e970984 --- /dev/null +++ b/PackageScript @@ -0,0 +1,49 @@ +# vim: set ts=8 sts=2 sw=2 tw=99 et ft=python: +import os + +# This is where the files will be output to +# package is the default +builder.SetBuildFolder('package') + +# Add any folders you need to this list +folder_list = [ + 'addons/sourcemod/extensions', + 'addons/sourcemod/gamedata', + 'addons/sourcemod/scripting/include', +] + +# Create the distribution folder hierarchy. +folder_map = {} +for folder in folder_list: + norm_folder = os.path.normpath(folder) + folder_map[folder] = builder.AddFolder(norm_folder) + +# Do all straight-up file copies from the source tree. +def CopyFiles(src, dest, files): + if not dest: + dest = src + dest_entry = folder_map[dest] + for source_file in files: + source_path = os.path.join(builder.sourcePath, src, source_file) + builder.AddCopy(source_path, dest_entry) + +# Include files +CopyFiles('sourcemod/scripting/include', 'addons/sourcemod/scripting/include', + [ 'sendproxy.inc', ] +) + +# GameData files +CopyFiles('sourcemod/gamedata', 'addons/sourcemod/gamedata', + [ 'sendproxy.txt',] +) + +# Config Files +#CopyFiles('configs', 'addons/sourcemod/configs', +# [ 'configfile.cfg', +# 'otherconfig.cfg, +# ] +#) + +# Copy binaries. +for cxx_task in Extension.extensions: + builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions']) diff --git a/README.md b/README.md new file mode 100644 index 0000000..5bdea3d --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +sendproxy +=========================== + +Fork of [TheByKotik's](https://github.com/TheByKotik/sendproxy) Fork of [SlidyBat's](https://github.com/SlidyBat/sendproxy) Fork of [akowald's](https://github.com/akowald/sendproxy) Fork of [VoiDeD's](https://github.com/VoiDeD/sourcemod-sendproxy-manager) Fork of [Afronanny's](https://github.com/TheByKotik/sendproxy) SendProxy Manager extension. diff --git a/SendProxy.sln b/SendProxy.sln new file mode 100644 index 0000000..d918a9f --- /dev/null +++ b/SendProxy.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.329 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sendproxy", "sendproxy.vcxproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - CSGO|Win32 = Debug - CSGO|Win32 + Debug - TF2|Win32 = Debug - TF2|Win32 + Release - CSGO|Win32 = Release - CSGO|Win32 + Release - TF2|Win32 = Release - TF2|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - CSGO|Win32.ActiveCfg = Debug - CSGO|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - CSGO|Win32.Build.0 = Debug - CSGO|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - TF2|Win32.ActiveCfg = Release - TF2|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Debug - TF2|Win32.Build.0 = Release - TF2|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - CSGO|Win32.ActiveCfg = Release - CSGO|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - CSGO|Win32.Build.0 = Release - CSGO|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - TF2|Win32.ActiveCfg = Release - TF2|Win32 + {B3E797CF-4E77-4C9D-B8A8-7589B6902206}.Release - TF2|Win32.Build.0 = Release - TF2|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {28CBC4F9-44B1-4DB8-A845-B0FC16B54CBB} + EndGlobalSection +EndGlobal diff --git a/configure.py b/configure.py new file mode 100644 index 0000000..57910e8 --- /dev/null +++ b/configure.py @@ -0,0 +1,23 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et: +import sys +from ambuild2 import run + +# Simple extensions do not need to modify this file. + +builder = run.PrepareBuild(sourcePath = sys.path[0]) + +builder.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, + help='Root search folder for HL2SDKs') +builder.options.add_option('--mms-path', type=str, dest='mms_path', default=None, + help='Path to Metamod:Source') +builder.options.add_option('--sm-path', type=str, dest='sm_path', default=None, + help='Path to SourceMod') +builder.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', + help='Enable debugging symbols') +builder.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', + help='Enable optimization') +builder.options.add_option('-s', '--sdks', default='all', dest='sdks', + help='Build against specified SDKs; valid args are "all", "present", or ' + 'comma-delimited list of engine names (default: %default)') + +builder.Configure() diff --git a/extension.cpp b/extension.cpp new file mode 100644 index 0000000..cac1d96 --- /dev/null +++ b/extension.cpp @@ -0,0 +1,1918 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifdef _WIN32 +#undef GetProp +#ifdef _WIN64 + #define PLATFORM_x64 +#else + #define PLATFORM_x32 +#endif +#elif defined __linux__ + #if defined __x86_64__ + #define PLATFORM_x64 + #else + #define PLATFORM_x32 + #endif +#endif + +#include "CDetour/detours.h" +#include "extension.h" +#include "interfaceimpl.h" +#include "natives.h" + +//path: hl2sdk-/public/.h, "../public/" included to prevent compile errors due wrong directory scanning by compiler on my computer, and I'm too lazy to find where I can change that =D +#include <../public/iserver.h> +#include <../public/iclient.h> + +#define DECL_DETOUR(name) \ + CDetour *name##_Detour = nullptr; + +#define CREATE_DETOUR(name, signname, var) \ + if (name##_Detour == NULL) \ + { \ + name##_Detour = DETOUR_CREATE_MEMBER(name, signname); \ + if(name##_Detour) { \ + name##_Detour->EnableDetour(); \ + var = true; \ + } else { \ + g_pSM->LogError(myself, "Failed to create " signname " detour, check error log.\n"); \ + var = false; \ + } \ + } else { \ + name##_Detour->EnableDetour(); \ + var = true; \ + } + +#define CREATE_DETOUR_STATIC(name, signname, var) \ + if (name##_Detour == NULL) \ + { \ + name##_Detour = DETOUR_CREATE_STATIC(name, signname); \ + if(name##_Detour) { \ + name##_Detour->EnableDetour(); \ + var = true; \ + } else { \ + g_pSM->LogError(myself, "Failed to create " signname " detour, check error log.\n"); \ + var = false; \ + } \ + } else { \ + name##_Detour->EnableDetour(); \ + var = true; \ + } + +#define DISABLE_DETOUR(name) \ + if (name##_Detour != nullptr) \ + { \ + name##_Detour->DisableDetour(); \ + } + +#define DESTROY_DETOUR(name) \ + if (name##_Detour != nullptr) \ + { \ + name##_Detour->Destroy(); \ + name##_Detour = nullptr; \ + } + +SH_DECL_HOOK1_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, false, edict_t *); +SH_DECL_HOOK1_void(IServerGameDLL, GameFrame, SH_NOATTRIB, false, bool); +SH_DECL_HOOK0(IServer, GetClientCount, const, false, int); + +DECL_DETOUR(CGameServer_SendClientMessages); +DECL_DETOUR(CGameClient_ShouldSendMessages); +DECL_DETOUR(SV_ComputeClientPacks); + +class CGameClient; +class CFrameSnapshot; +class CGlobalEntityList; + +CGameClient * g_pCurrentGameClientPtr = nullptr; +int g_iCurrentClientIndexInLoop = -1; //used for optimization +bool g_bCurrentGameClientCallFwd = false; +bool g_bCallingForNullClients = false; +bool g_bFirstTimeCalled = true; +bool g_bSVComputePacksDone = true; +IServer * g_pIServer = nullptr; + +SendProxyManager g_SendProxyManager; +SendProxyManagerInterfaceImpl * g_pMyInterface = nullptr; +SMEXT_LINK(&g_SendProxyManager); + +CThreadFastMutex g_WorkMutex; + +#include + +std::vector g_Hooks; +std::vector g_HooksGamerules; +std::vector g_ChangeHooks; +std::vector g_ChangeHooksGamerules; + +std::vector g_vHookedEdicts; + +IServerGameEnts * gameents = nullptr; +IServerGameClients * gameclients = nullptr; +ISDKTools * g_pSDKTools = nullptr; +ISDKHooks * g_pSDKHooks = nullptr; +IGameConfig * g_pGameConf = nullptr; +IGameConfig * g_pGameConfSDKTools = nullptr; + +ConVar * sv_parallel_packentities = nullptr; +ConVar * sv_parallel_sendsnapshot = nullptr; + +edict_t * g_pGameRulesProxyEdict = nullptr; +void * g_pGameRules = nullptr; +bool g_bShouldChangeGameRulesState = false; +bool g_bSendSnapshots = false; + +size_t g_numPerClientHooks = 0; + +CGlobalVars * g_pGlobals = nullptr; + +static CBaseEntity * FindEntityByServerClassname(int, const char *); +static void CallChangeCallbacks(const PropChangeHook &pInfo, void * pOldValue, void * pNewValue); +static void CallChangeGamerulesCallbacks(const PropChangeHookGamerules &pInfo, void * pOldValue, void * pNewValue); + +const char * g_szGameRulesProxy; + +//detours + +/*Call stack: + ... + 1. CGameServer::SendClientMessages //function we hooking to send props individually for each client + 2. SV_ComputeClientPacks //function we hooking to set edicts state and to know, need we call callbacks or not, but not in csgo + 3. PackEntities_Normal //if we in multiplayer + 4. SV_PackEntity //also we can hook this instead hooking ProxyFn, but there no reason to do that + 5. SendTable_Encode + 6. SendTable_EncodeProp //here the ProxyFn will be called + 7. ProxyFn //here our callbacks is called +*/ + +DETOUR_DECL_MEMBER1(CGameServer_SendClientMessages, void, bool, bSendSnapshots) +{ + if (!bSendSnapshots) + { + g_bSendSnapshots = false; + return DETOUR_MEMBER_CALL(CGameServer_SendClientMessages)(false); //if so, we do not interested in this call + } + else + g_bSendSnapshots = true; + if (!g_pIServer && g_pSDKTools) + g_pIServer = g_pSDKTools->GetIServer(); + if (!g_pIServer) + return DETOUR_MEMBER_CALL(CGameServer_SendClientMessages)(true); //if so, we should stop to process this function! See below + if (g_bFirstTimeCalled) + { +#ifdef _WIN32 + //HACK, don't delete this, or server will be crashed on start! + g_pIServer->GetClientCount(); +#endif + SH_ADD_HOOK(IServer, GetClientCount, g_pIServer, SH_MEMBER(&g_SendProxyManager, &SendProxyManager::GetClientCount), false); + g_bFirstTimeCalled = false; + } + bool bCalledForNullIClientsThisTime = false; + for (int iClients = 1; iClients <= playerhelpers->GetMaxClients(); iClients++) + { + IGamePlayer * pPlayer = playerhelpers->GetGamePlayer(iClients); + bool bFake = (pPlayer->IsFakeClient() && !(pPlayer->IsSourceTV() +#if SOURCE_ENGINE == SE_TF2 + || pPlayer->IsReplay() +#endif + )); + volatile IClient * pClient = nullptr; //volatile used to prevent optimizations here for some reason + if (!pPlayer->IsConnected() || bFake || (pClient = g_pIServer->GetClient(iClients - 1)) == nullptr) + { + if (!bCalledForNullIClientsThisTime && !g_bCallingForNullClients) + { + g_bCurrentGameClientCallFwd = false; + g_bCallingForNullClients = true; + DETOUR_MEMBER_CALL(CGameServer_SendClientMessages)(true); + g_bCallingForNullClients = false; + } + bCalledForNullIClientsThisTime = true; + continue; + } + if (!pPlayer->IsInGame() || bFake) //We should call SV_ComputeClientPacks, but shouldn't call forwards! + g_bCurrentGameClientCallFwd = false; + else + g_bCurrentGameClientCallFwd = true; + g_pCurrentGameClientPtr = (CGameClient *)((char *)pClient - 4); + g_iCurrentClientIndexInLoop = iClients - 1; + DETOUR_MEMBER_CALL(CGameServer_SendClientMessages)(true); + } + g_bCurrentGameClientCallFwd = false; + g_iCurrentClientIndexInLoop = -1; + g_bShouldChangeGameRulesState = false; +} + +DETOUR_DECL_MEMBER0(CGameClient_ShouldSendMessages, bool) +{ + if (!g_bSendSnapshots) + return DETOUR_MEMBER_CALL(CGameClient_ShouldSendMessages)(); + if (g_bCallingForNullClients) + { + IClient * pClient = (IClient *)((char *)this + 4); +#if SOURCE_ENGINE == SE_TF2 + //don't remove this code + int iUserID = pClient->GetUserID(); + IGamePlayer * pPlayer = playerhelpers->GetGamePlayer(pClient->GetPlayerSlot() + 1); + if (pPlayer->GetUserId() != iUserID) //if so, there something went wrong, check this now! +#endif + { + if (pClient->IsHLTV() +#if SOURCE_ENGINE == SE_TF2 + || pClient->IsReplay() +#endif + || (pClient->IsConnected() && !pClient->IsActive())) + return true; //Also we need to allow connect for inactivated clients, sourcetv & replay + } + return false; + } + bool bOriginalResult = DETOUR_MEMBER_CALL(CGameClient_ShouldSendMessages)(); + if (!bOriginalResult) + return false; + if ((CGameClient *)this == g_pCurrentGameClientPtr) + return true; +#if defined PLATFORM_x32 + else + { + volatile int iToSet = g_iCurrentClientIndexInLoop - 1; +#if SOURCE_ENGINE == SE_TF2 +#ifdef _WIN32 + //some little trick to deceive msvc compiler + __asm _emit 0x5F + __asm _emit 0x5E + __asm push edx + __asm mov edx, iToSet + __asm _emit 0x3B + __asm _emit 0xF2 + __asm jge CompFailed + __asm _emit 0x8B + __asm _emit 0xF2 + __asm CompFailed: + __asm pop edx + __asm _emit 0x56 + __asm _emit 0x57 +#elif defined __linux__ + volatile int iTemp; + asm volatile("movl %%esi, %0" : "=g" (iTemp)); + if (iTemp < iToSet) + asm volatile( + "movl %0, %%esi\n\t" + "movl %%esi, %%edx\n\t" + "addl $84, %%esp\n\t" + "popl %%esi\n\t" + "pushl %%edx\n\t" + "subl $84, %%esp\n\t" + : : "g" (iToSet) : "%edx"); +#endif +#elif SOURCE_ENGINE == SE_CSGO +#ifdef _WIN32 + volatile int iEax, iEdi, iEsi; + //save registers + __asm mov iEdi, edi + __asm mov iEsi, esi + __asm mov iEax, eax + __asm mov eax, ebp + //load stack ptr + //we need to pop esi and edi to pop ebp register, we don't care about values in these, we also will use them as variables + __asm pop esi + __asm pop edi + __asm mov edi, iToSet + __asm mov esp, ebp + __asm pop ebp + //load needed info and compare + __asm mov esi, [ebp-0x7F8] //0x7F8 is an offset of loop variable + __asm cmp esi, edi + __asm jge CompFailed + //good, store our value + __asm mov [ebp-0x7F8], edi + __asm CompFailed: + //push old and restore original registers + __asm push ebp + __asm mov ebp, eax + __asm mov esp, ebp + __asm sub esp, 0x10 + __asm mov esi, iEsi + __asm mov edi, iEdi + __asm mov eax, iEax + __asm push edi + __asm push esi +#elif defined __linux__ + volatile int iTemp; + //we don't need to clubber edi register here, some low level shit + asm volatile("movl %%edi, %0" : "=g" (iTemp)); + if (iTemp < iToSet) + asm volatile("movl %0, %%edi" : : "g" (iToSet)); +#endif +#endif + } +#endif + return false; +} + +#if defined __linux__ +void __attribute__((__cdecl__)) SV_ComputeClientPacks_ActualCall(int iClientCount, CGameClient ** pClients, CFrameSnapshot * pSnapShot); +#else +void __cdecl SV_ComputeClientPacks_ActualCall(int iClientCount, CGameClient ** pClients, CFrameSnapshot * pSnapShot); +#endif + +//the better idea rewrite it with __declspec(naked) for csgo or use __stdcall function as main callback instead of this +DETOUR_DECL_STATIC3(SV_ComputeClientPacks, void, int, iClientCount, CGameClient **, pClients, CFrameSnapshot *, pSnapShot) +{ +#if defined _WIN32 && SOURCE_ENGINE == SE_CSGO + //so, here it is __userpurge call, we need manually get our arguments + __asm mov iClientCount, ecx + __asm mov pClients, edx + __asm mov pSnapShot, ebx +#endif + g_bSVComputePacksDone = false; + if (!iClientCount || !g_bSendSnapshots || pClients[0] != g_pCurrentGameClientPtr) + return SV_ComputeClientPacks_ActualCall(iClientCount, pClients, pSnapShot); + IClient * pClient = (IClient *)((char *)pClients[0] + 4); + int iClient = pClient->GetPlayerSlot(); + if (g_iCurrentClientIndexInLoop != iClient) + return SV_ComputeClientPacks_ActualCall(iClientCount, pClients, pSnapShot); + //Also here we can change actual values for each client! But for what? + //Just mark all hooked edicts as changed to bypass check in SV_PackEntity! + for (auto pEdict : g_vHookedEdicts) + { + if (pEdict && !(pEdict->m_fStateFlags & FL_EDICT_CHANGED)) + pEdict->m_fStateFlags |= FL_EDICT_CHANGED; + } + if (g_bShouldChangeGameRulesState && g_pGameRulesProxyEdict) + { + if (!(g_pGameRulesProxyEdict->m_fStateFlags & FL_EDICT_CHANGED)) + g_pGameRulesProxyEdict->m_fStateFlags |= FL_EDICT_CHANGED; + } + if (g_bCurrentGameClientCallFwd) + g_bSVComputePacksDone = true; + return SV_ComputeClientPacks_ActualCall(iClientCount, pClients, pSnapShot); +} + +#if defined _WIN32 && SOURCE_ENGINE == SE_CSGO +__declspec(naked) void __cdecl SV_ComputeClientPacks_ActualCall(int iClientCount, CGameClient ** pClients, CFrameSnapshot * pSnapShot) +{ + //we do not use ebp here + __asm mov edx, pClients //we don't care about values in edx & ecx + __asm mov ecx, iClientCount + __asm mov ebx, pSnapShot + __asm push ebx + __asm call SV_ComputeClientPacks_Actual + __asm add esp, 0x4 //restore our stack + __asm retn +} +#else +#ifdef __linux__ +void __attribute__((__cdecl__)) SV_ComputeClientPacks_ActualCall(int iClientCount, CGameClient ** pClients, CFrameSnapshot * pSnapShot) +#else +void __cdecl SV_ComputeClientPacks_ActualCall(int iClientCount, CGameClient ** pClients, CFrameSnapshot * pSnapShot) +#endif +{ + return DETOUR_STATIC_CALL(SV_ComputeClientPacks)(iClientCount, pClients, pSnapShot); +} +#endif + +//hooks + +void DoOnEntityDestroyed(int idx) +{ + auto it = g_Hooks.begin(); + while(it != g_Hooks.end()) + { + auto &hook = *it; + if (hook.objectID == idx) + { + g_SendProxyManager.UnhookProxy(hook); + g_Hooks.erase(it); + continue; + } + + ++it; + } + + auto it2 = g_ChangeHooks.begin(); + while(it2 != g_ChangeHooks.end()) + { + if (it2->objectID == idx) { + g_ChangeHooks.erase(it2); + continue; + } + + ++it2; + } +} + +void SendProxyManager::OnEntityDestroyed(CBaseEntity* pEnt) +{ + int idx = gamehelpers->EntityToBCompatRef(pEnt); + DoOnEntityDestroyed(idx); +} + +void Hook_ClientDisconnect(edict_t * pEnt) +{ + int idx = gamehelpers->IndexOfEdict(pEnt); + DoOnEntityDestroyed(idx); + RETURN_META(MRES_IGNORED); +} + +void Hook_GameFrame(bool simulating) +{ + if (simulating) + { + for (auto &it : g_ChangeHooks) + { + switch(it.propType) + { + case PropType::Prop_Int: + { + edict_t * pEnt = gamehelpers->EdictOfIndex(it.objectID); + CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt); + int iCurrent = *(int *)((unsigned char *)pEntity + it.Offset); + if (iCurrent != it.iLastValue) + { + CallChangeCallbacks(it, (void *)&it.iLastValue, (void *)&iCurrent); + it.iLastValue = iCurrent; + } + break; + } + case PropType::Prop_Float: + { + edict_t * pEnt = gamehelpers->EdictOfIndex(it.objectID); + CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt); + float flCurrent = *(float *)((unsigned char *)pEntity + it.Offset); + if (flCurrent != it.flLastValue) + { + CallChangeCallbacks(it, (void *)&it.flLastValue, (void *)&flCurrent); + it.flLastValue = flCurrent; + } + break; + } + case PropType::Prop_String: + { + edict_t * pEnt = gamehelpers->EdictOfIndex(it.objectID); + CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt); + const char * szCurrent = (const char *)((unsigned char *)pEntity + it.Offset); + if (strcmp(szCurrent, it.cLastValue) != 0) + { + CallChangeCallbacks(it, (void *)it.cLastValue, (void *)szCurrent); + memset(it.cLastValue, 0, sizeof(it.cLastValue)); + strncpynull(it.cLastValue, szCurrent, sizeof(it.cLastValue)); + } + break; + } + case PropType::Prop_Vector: + { + edict_t * pEnt = gamehelpers->EdictOfIndex(it.objectID); + CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt); + Vector * pVec = (Vector *)((unsigned char *)pEntity + it.Offset); + if (it.vecLastValue != *pVec) + { + CallChangeCallbacks(it, (void *)&it.vecLastValue, (void *)pVec); + it.vecLastValue = *pVec; + } + break; + } + default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, it.pVar->GetName()); + } + } + if (!g_pGameRules && g_pSDKTools) + { + g_pGameRules = g_pSDKTools->GetGameRules(); + if (!g_pGameRules) + { + g_pSM->LogError(myself, "CRITICAL ERROR: Could not get gamerules pointer!"); + return; + } + } + //Gamerules hooks + for (auto &it : g_ChangeHooksGamerules) + { + switch(it.propType) + { + case PropType::Prop_Int: + { + int iCurrent = *(int *)((unsigned char *)g_pGameRules + it.Offset); + if (iCurrent != it.iLastValue) + { + CallChangeGamerulesCallbacks(it, (void *)&it.iLastValue, (void *)&iCurrent); + it.iLastValue = iCurrent; + } + break; + } + case PropType::Prop_Float: + { + float flCurrent = *(float *)((unsigned char *)g_pGameRules + it.Offset); + if (flCurrent != it.flLastValue) + { + CallChangeGamerulesCallbacks(it, (void *)&it.flLastValue, (void *)&flCurrent); + it.flLastValue = flCurrent; + } + break; + } + case PropType::Prop_String: + { + const char * szCurrent = (const char *)((unsigned char *)g_pGameRules + it.Offset); + if (strcmp(szCurrent, it.cLastValue) != 0) + { + CallChangeGamerulesCallbacks(it, (void *)it.cLastValue, (void *)szCurrent); + memset(it.cLastValue, 0, sizeof(it.cLastValue)); + strncpynull(it.cLastValue, szCurrent, sizeof(it.cLastValue)); + } + break; + } + case PropType::Prop_Vector: + { + Vector * pVec = (Vector *)((unsigned char *)g_pGameRules + it.Offset); + if (it.vecLastValue != *pVec) + { + CallChangeGamerulesCallbacks(it, (void *)&it.vecLastValue, (void *)pVec); + it.vecLastValue = *pVec; + } + break; + } + default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, it.pVar->GetName()); + } + } + } + RETURN_META(MRES_IGNORED); +} + +int SendProxyManager::GetClientCount() const +{ + if (g_iCurrentClientIndexInLoop != -1) + RETURN_META_VALUE(MRES_SUPERCEDE, g_iCurrentClientIndexInLoop + 1); + RETURN_META_VALUE(MRES_IGNORED, 0/*META_RESULT_ORIG_RET(int)*/); +} + +//main sm class implementation + +bool SendProxyManager::SDK_OnLoad(char *error, size_t maxlength, bool late) +{ + char conf_error[255]; + if (!gameconfs->LoadGameConfigFile("sdktools.games", &g_pGameConfSDKTools, conf_error, sizeof(conf_error))) + { + if (conf_error[0]) + snprintf(error, maxlength, "Could not read config file sdktools.games.txt: %s", conf_error); + return false; + } + + g_szGameRulesProxy = g_pGameConfSDKTools->GetKeyValue("GameRulesProxy"); + + if (!gameconfs->LoadGameConfigFile("sendproxy", &g_pGameConf, conf_error, sizeof(conf_error))) + { + if (conf_error[0]) + snprintf(error, maxlength, "Could not read config file sendproxy.txt: %s", conf_error); + return false; + } + + CDetourManager::Init(smutils->GetScriptingEngine(), g_pGameConf); + + if (late) //if we loaded late, we need manually to call that + OnCoreMapStart(nullptr, 0, 0); + + sharesys->AddDependency(myself, "sdktools.ext", true, true); + sharesys->AddDependency(myself, "sdkhooks.ext", true, true); + + g_pMyInterface = new SendProxyManagerInterfaceImpl(); + sharesys->AddInterface(myself, g_pMyInterface); + //we should not maintain compatibility with old plugins which uses earlier versions of sendproxy (< 1.3) + sharesys->RegisterLibrary(myself, "sendproxy14"); + plsys->AddPluginsListener(this); + + return true; +} + +void SendProxyManager::SDK_OnAllLoaded() +{ + sharesys->AddNatives(myself, g_MyNatives); + SM_GET_LATE_IFACE(SDKTOOLS, g_pSDKTools); + SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks); + + if (g_pSDKHooks) + { + g_pSDKHooks->AddEntityListener(this); + } +} + +void SendProxyManager::SDK_OnUnload() +{ + for (auto &it : g_Hooks) + { + it.pVar->SetProxyFn(it.pRealProxy); + } + + SH_REMOVE_HOOK(IServerGameClients, ClientDisconnect, gameclients, SH_STATIC(Hook_ClientDisconnect), false); + SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_STATIC(Hook_GameFrame), false); + if (!g_bFirstTimeCalled) + SH_REMOVE_HOOK(IServer, GetClientCount, g_pIServer, SH_MEMBER(this, &SendProxyManager::GetClientCount), false); + + DESTROY_DETOUR(CGameServer_SendClientMessages); + DESTROY_DETOUR(CGameClient_ShouldSendMessages); + DESTROY_DETOUR(SV_ComputeClientPacks); + + gameconfs->CloseGameConfigFile(g_pGameConf); + gameconfs->CloseGameConfigFile(g_pGameConfSDKTools); + + plsys->RemovePluginsListener(this); + if( g_pSDKHooks ) + { + g_pSDKHooks->RemoveEntityListener(this); + } + delete g_pMyInterface; +} + +void SendProxyManager::OnCoreMapEnd() +{ + for (auto &it : g_HooksGamerules) + { + UnhookProxyGamerules(it); + } + g_HooksGamerules.clear(); + + g_pGameRulesProxyEdict = nullptr; + + if(g_numPerClientHooks > 0) { + DISABLE_DETOUR(CGameServer_SendClientMessages); + DISABLE_DETOUR(CGameClient_ShouldSendMessages); + DISABLE_DETOUR(SV_ComputeClientPacks); + g_iCurrentClientIndexInLoop = -1; + g_bSVComputePacksDone = true; + } +} + +void SendProxyManager::OnCoreMapStart(edict_t * pEdictList, int edictCount, int clientMax) +{ + CBaseEntity * pGameRulesProxyEnt = FindEntityByServerClassname(0, g_szGameRulesProxy); + if (!pGameRulesProxyEnt) + { + smutils->LogError(myself, "Unable to get gamerules proxy ent (1)!"); + return; + } + g_pGameRulesProxyEdict = gameents->BaseEntityToEdict(pGameRulesProxyEnt); + if (!g_pGameRulesProxyEdict) + smutils->LogError(myself, "Unable to get gamerules proxy ent (2)!"); + + if(g_numPerClientHooks > 0) { + bool bDetoursInited = false; + g_bSVComputePacksDone = false; + g_iCurrentClientIndexInLoop = -1; + CREATE_DETOUR(CGameServer_SendClientMessages, "CGameServer::SendClientMessages", bDetoursInited); + CREATE_DETOUR(CGameClient_ShouldSendMessages, "CGameClient::ShouldSendMessages", bDetoursInited); + CREATE_DETOUR_STATIC(SV_ComputeClientPacks, "SV_ComputeClientPacks", bDetoursInited); + } +} + +bool SendProxyManager::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) +{ + GET_V_IFACE_ANY(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS); + GET_V_IFACE_ANY(GetServerFactory, gameclients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS); + GET_V_IFACE_ANY(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION); + + g_pGlobals = ismm->GetCGlobals(); + + SH_ADD_HOOK(IServerGameDLL, GameFrame, gamedll, SH_STATIC(Hook_GameFrame), false); + SH_ADD_HOOK(IServerGameClients, ClientDisconnect, gameclients, SH_STATIC(Hook_ClientDisconnect), false); + + GET_CONVAR(sv_parallel_packentities); + sv_parallel_packentities->SetValue(0); //If we don't do that the sendproxy extension will crash the server (Post ref: https://forums.alliedmods.net/showpost.php?p=2540106&postcount=324 ) + GET_CONVAR(sv_parallel_sendsnapshot); + sv_parallel_sendsnapshot->SetValue(0); //If we don't do that, sendproxy will not work correctly and may crash server. This affects all versions of sendproxy manager! + + return true; +} + +void SendProxyManager::OnPluginUnloaded(IPlugin * plugin) +{ + IPluginContext * pCtx = plugin->GetBaseContext(); + auto it = g_Hooks.begin(); + while(it != g_Hooks.end()) + { + auto &hook = *it; + + if (hook.sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && + ((IPluginFunction *)hook.sCallbackInfo.pCallback)->GetParentContext() == pCtx) + { + UnhookProxy(hook); + g_Hooks.erase(it); + continue; + } + + ++it; + } + auto it2 = g_HooksGamerules.begin(); + while(it2 != g_HooksGamerules.end()) + { + auto &hook = *it2; + + if (hook.sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && + ((IPluginFunction *)hook.sCallbackInfo.pCallback)->GetParentContext() == pCtx) + { + UnhookProxyGamerules(hook); + g_HooksGamerules.erase(it2); + continue; + } + + ++it2; + } + auto it3 = g_ChangeHooks.begin(); + while(it3 != g_ChangeHooks.end()) + { + auto &pCallbacks = it3->vCallbacksInfo; + auto it4 = pCallbacks.begin(); + while(it4 != pCallbacks.end()) { + auto &cb = *it4; + + if (cb.iCallbackType == CallBackType::Callback_PluginFunction && + (IPluginContext *)cb.pOwner == pCtx) + { + pCallbacks.erase(it4); + continue; + } + + ++it4; + } + //else do not needed here + if (pCallbacks.empty()) { + g_ChangeHooks.erase(it3); + continue; + } + + ++it3; + } + auto it5 = g_ChangeHooksGamerules.begin(); + while(it5 != g_ChangeHooksGamerules.end()) + { + auto &pCallbacks = it5->vCallbacksInfo; + + auto it6 = pCallbacks.begin(); + while(it6 != pCallbacks.end()) { + auto &cb = *it6; + + if (cb.iCallbackType == CallBackType::Callback_PluginFunction && + (IPluginContext *)cb.pOwner == pCtx) + { + pCallbacks.erase(it6); + continue; + } + + ++it6; + } + //else do not needed here + if (pCallbacks.empty()) { + g_ChangeHooksGamerules.erase(it5); + continue; + } + + ++it5; + } +} + +//functions + +bool SendProxyManager::AddHookToList(SendPropHook &&hook) +{ + //Need to make sure this prop isn't already hooked for this entity - we don't care anymore + bool bEdictHooked = false; + for (auto &it : g_Hooks) + { + if (it.objectID == hook.objectID) + { + //we don't care anymore + //if (g_Hooks[i].pVar == hook.pVar) + // return false; + //else + bEdictHooked = true; + } + } + + if(hook.per_client) { + if(++g_numPerClientHooks == 1) { + bool bDetoursInited = false; + g_bSVComputePacksDone = false; + g_iCurrentClientIndexInLoop = -1; + CREATE_DETOUR(CGameServer_SendClientMessages, "CGameServer::SendClientMessages", bDetoursInited); + CREATE_DETOUR(CGameClient_ShouldSendMessages, "CGameClient::ShouldSendMessages", bDetoursInited); + CREATE_DETOUR_STATIC(SV_ComputeClientPacks, "SV_ComputeClientPacks", bDetoursInited); + } + } + + g_Hooks.emplace_back(std::move(hook)); + if (!bEdictHooked) { + g_vHookedEdicts.emplace_back(hook.pEnt); + } + return true; +} + +bool SendProxyManager::AddHookToListGamerules(SendPropHookGamerules &&hook) +{ + //Need to make sure this prop isn't already hooked for this entity - we don't care anymore + /*for (int i = 0; i < g_HooksGamerules.size(); i++) + { + if (g_HooksGamerules[i].pVar == hook.pVar) + return false; + }*/ + + if(hook.per_client) { + if(++g_numPerClientHooks == 1) { + bool bDetoursInited = false; + g_bSVComputePacksDone = false; + g_iCurrentClientIndexInLoop = -1; + CREATE_DETOUR(CGameServer_SendClientMessages, "CGameServer::SendClientMessages", bDetoursInited); + CREATE_DETOUR(CGameClient_ShouldSendMessages, "CGameClient::ShouldSendMessages", bDetoursInited); + CREATE_DETOUR_STATIC(SV_ComputeClientPacks, "SV_ComputeClientPacks", bDetoursInited); + } + } + + g_HooksGamerules.emplace_back(std::move(hook)); + return true; +} + +bool SendProxyManager::AddChangeHookToList(PropChangeHook &&sHook, CallBackInfo &&pInfo) +{ + PropChangeHook *pHookInfo = nullptr; + for (auto &it : g_ChangeHooks) + { + if (it.pVar == sHook.pVar) + { + pHookInfo = ⁢ + break; + } + } + if (pHookInfo) + { + //just validate it + switch (sHook.propType) + { + case PropType::Prop_Int: + case PropType::Prop_Float: + case PropType::Prop_String: + case PropType::Prop_Vector: + break; + default: return false; + } + pHookInfo->vCallbacksInfo.emplace_back(std::move(pInfo)); + } + else + { + edict_t * pEnt = gamehelpers->EdictOfIndex(sHook.objectID); + if (!pEnt || pEnt->IsFree()) return false; //should never happen + CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt); + if (!pEntity) return false; //should never happen + switch (sHook.propType) + { + case PropType::Prop_Int: sHook.iLastValue = *(int *)((unsigned char *)pEntity + sHook.Offset); break; + case PropType::Prop_Float: sHook.flLastValue = *(float *)((unsigned char*)pEntity + sHook.Offset); break; + case PropType::Prop_String: strncpynull(sHook.cLastValue, (const char *)((unsigned char *)pEntity + sHook.Offset), sizeof(sHook.cLastValue)); break; + case PropType::Prop_Vector: sHook.vecLastValue = *(Vector *)((unsigned char *)pEntity + sHook.Offset); break; + default: return false; + } + + sHook.vCallbacksInfo.emplace_back(std::move(pInfo)); + g_ChangeHooks.emplace_back(std::move(sHook)); + } + return true; +} + +bool SendProxyManager::AddChangeHookToListGamerules(PropChangeHookGamerules &&sHook, CallBackInfo &&pInfo) +{ + PropChangeHookGamerules *pHookInfo = nullptr; + for (auto &it : g_ChangeHooksGamerules) + { + if (it.pVar == sHook.pVar) + { + pHookInfo = ⁢ + break; + } + } + if (pHookInfo) + { + //just validate it + switch (sHook.propType) + { + case PropType::Prop_Int: + case PropType::Prop_Float: + case PropType::Prop_String: + case PropType::Prop_Vector: + break; + default: return false; + } + pHookInfo->vCallbacksInfo.emplace_back(std::move(pInfo)); + } + else + { + switch (sHook.propType) + { + case PropType::Prop_Int: sHook.iLastValue = *(int *)((unsigned char *)g_pGameRules + sHook.Offset); break; + case PropType::Prop_Float: sHook.flLastValue = *(float *)((unsigned char*)g_pGameRules + sHook.Offset); break; + case PropType::Prop_String: strncpynull(sHook.cLastValue, (const char *)((unsigned char *)g_pGameRules + sHook.Offset), sizeof(sHook.cLastValue)); break; + case PropType::Prop_Vector: sHook.vecLastValue = *(Vector *)((unsigned char *)g_pGameRules + sHook.Offset); break; + default: return false; + } + + sHook.vCallbacksInfo.emplace_back(std::move(pInfo)); + g_ChangeHooksGamerules.emplace_back(std::move(sHook)); + } + return true; +} + +bool SendProxyManager::UnhookProxy(const SendPropHook &hook) +{ + if(hook.per_client) { + if(--g_numPerClientHooks == 0) { + DISABLE_DETOUR(CGameServer_SendClientMessages); + DISABLE_DETOUR(CGameClient_ShouldSendMessages); + DISABLE_DETOUR(SV_ComputeClientPacks); + g_iCurrentClientIndexInLoop = -1; + g_bSVComputePacksDone = true; + } + } + + //if there are other hooks for this prop, don't change the proxy, just remove it from our list + auto it2 = g_Hooks.begin(); + while(it2 != g_Hooks.end()) { + if (it2->pVar == hook.pVar && + &*it2 != &hook) + { + CallListenersForHook(hook); + return true; + } + + ++it2; + } + auto it = g_vHookedEdicts.begin(); + while(it != g_vHookedEdicts.end()) { + if(*it == hook.pEnt) { + g_vHookedEdicts.erase(it); + break; + } + + ++it; + } + CallListenersForHook(hook); + hook.pVar->SetProxyFn(hook.pRealProxy); + return false; +} + +bool SendProxyManager::UnhookProxyGamerules(const SendPropHookGamerules &hook) +{ + if(hook.per_client) { + if(--g_numPerClientHooks == 0) { + DISABLE_DETOUR(CGameServer_SendClientMessages); + DISABLE_DETOUR(CGameClient_ShouldSendMessages); + DISABLE_DETOUR(SV_ComputeClientPacks); + g_iCurrentClientIndexInLoop = -1; + g_bSVComputePacksDone = true; + } + } + + //if there are other hooks for this prop, don't change the proxy, just remove it from our list + auto it = g_HooksGamerules.begin(); + while(it != g_HooksGamerules.end()) + { + if (it->pVar == hook.pVar && + &*it != &hook) + { + CallListenersForHookGamerules(hook); + return true; + } + + ++it; + } + CallListenersForHookGamerules(hook); + hook.pVar->SetProxyFn(hook.pRealProxy); + return false; +} + +bool SendProxyManager::UnhookChange(PropChangeHook &hook, const CallBackInfo &pInfo) +{ + auto &pCallbacks = hook.vCallbacksInfo; + auto it2 = pCallbacks.begin(); + while(it2 != pCallbacks.end()) { + auto &cb = *it2; + + if (cb.iCallbackType == pInfo.iCallbackType && + cb.pCallback == (void *)pInfo.pCallback) + { + pCallbacks.erase(it2); + continue; + } + + ++it2; + } + //if there no any callbacks anymore, then remove all info about this hook + if (pCallbacks.empty()) { + return true; + } + + return false; +} + +bool SendProxyManager::UnhookChangeGamerules(PropChangeHookGamerules &hook, const CallBackInfo &pInfo) +{ + auto &pCallbacks = hook.vCallbacksInfo; + auto it2 = pCallbacks.begin(); + while(it2 != pCallbacks.end()) { + auto &cb = *it2; + + if (cb.iCallbackType == pInfo.iCallbackType && + cb.pCallback == (void *)pInfo.pCallback) + { + pCallbacks.erase(it2); + continue; + } + + ++it2; + } + //if there no any callbacks anymore, then remove all info about this hook + if (pCallbacks.empty()) { + return true; + } + + return false; +} + +//callbacks + +//Change + +void CallChangeCallbacks(const PropChangeHook &pInfo, void * pOldValue, void * pNewValue) +{ + for (auto &sCallback : pInfo.vCallbacksInfo) + { + switch (sCallback.iCallbackType) + { + case CallBackType::Callback_CPPCallbackInterface: + { + edict_t * pEnt = gamehelpers->EdictOfIndex(pInfo.objectID); + if (!pEnt) + break; //??? + ISendProxyChangeCallbacks * pCallbacks = (ISendProxyChangeCallbacks *)sCallback.pCallback; + pCallbacks->OnEntityPropChange(gameents->EdictToBaseEntity(pEnt), pInfo.pVar, pNewValue, pOldValue, pInfo.propType, pInfo.Element); + break; + } + case CallBackType::Callback_PluginFunction: + { + IPluginFunction * pCallBack = (IPluginFunction *)sCallback.pCallback; + switch (pInfo.propType) + { + case PropType::Prop_Int: + { + pCallBack->PushCell(pInfo.objectID); + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushCell(pInfo.iLastValue); + pCallBack->PushCell(*(int *)pNewValue); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + case PropType::Prop_Float: + { + pCallBack->PushCell(pInfo.objectID); + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushFloat(pInfo.flLastValue); + pCallBack->PushFloat(*(float *)pNewValue); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + case PropType::Prop_String: + { + pCallBack->PushCell(pInfo.objectID); + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushString(pInfo.cLastValue); + pCallBack->PushString((char *)pNewValue); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + case PropType::Prop_Vector: + { + cell_t vector[2][3]; + Vector * pVec = (Vector *)pNewValue; + vector[0][0] = sp_ftoc(pVec->x); + vector[0][1] = sp_ftoc(pVec->y); + vector[0][2] = sp_ftoc(pVec->z); + vector[1][0] = sp_ftoc(pInfo.vecLastValue[0]); + vector[1][1] = sp_ftoc(pInfo.vecLastValue[1]); + vector[1][2] = sp_ftoc(pInfo.vecLastValue[2]); + pCallBack->PushCell(pInfo.objectID); + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushArray(vector[1], 3); + pCallBack->PushArray(vector[0], 3); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + } + break; + } + } + } +} + +void CallChangeGamerulesCallbacks(const PropChangeHookGamerules &pInfo, void * pOldValue, void * pNewValue) +{ + for (auto &sCallback : pInfo.vCallbacksInfo) + { + switch (sCallback.iCallbackType) + { + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyChangeCallbacks * pCallbacks = (ISendProxyChangeCallbacks *)sCallback.pCallback; + pCallbacks->OnGamerulesPropChange(pInfo.pVar, pNewValue, pOldValue, pInfo.propType, pInfo.Element); + break; + } + case CallBackType::Callback_PluginFunction: + { + IPluginFunction * pCallBack = (IPluginFunction *)sCallback.pCallback; + switch (pInfo.propType) + { + case PropType::Prop_Int: + { + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushCell(pInfo.iLastValue); + pCallBack->PushCell(*(int *)pNewValue); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + case PropType::Prop_Float: + { + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushFloat(pInfo.flLastValue); + pCallBack->PushFloat(*(float *)pNewValue); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + case PropType::Prop_String: + { + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushString(pInfo.cLastValue); + pCallBack->PushString((char *)pNewValue); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + case PropType::Prop_Vector: + { + cell_t vector[2][3]; + Vector * pVec = (Vector *)pNewValue; + vector[0][0] = sp_ftoc(pVec->x); + vector[0][1] = sp_ftoc(pVec->y); + vector[0][2] = sp_ftoc(pVec->z); + vector[1][0] = sp_ftoc(pInfo.vecLastValue[0]); + vector[1][1] = sp_ftoc(pInfo.vecLastValue[1]); + vector[1][2] = sp_ftoc(pInfo.vecLastValue[2]); + pCallBack->PushString(pInfo.pVar->GetName()); + pCallBack->PushArray(vector[1], 3); + pCallBack->PushArray(vector[0], 3); + pCallBack->PushCell(pInfo.Element); + pCallBack->Execute(0); + break; + } + } + break; + } + } + } +} + +//Proxy + +bool CallInt(const SendPropHook &hook, int &ret) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + cell_t value = ret; + cell_t result = Pl_Continue; + callback->PushCell(hook.objectID); + callback->PushString(hook.pVar->GetName()); + callback->PushCellByRef(&value); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + ret = value; + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + int iValue = ret; + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&iValue, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (void *)&iValue, hook.propType, hook.Element); + } + if (bChange) + { + ret = iValue; + return true; + } + break; + } + } + return false; +} + +bool CallIntGamerules(const SendPropHookGamerules &hook, int &ret) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + cell_t value = ret; + cell_t result = Pl_Continue; + callback->PushString(hook.pVar->GetName()); + callback->PushCellByRef(&value); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + ret = value; + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + int iValue = ret; + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&iValue, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (void *)&iValue, hook.propType, hook.Element); + } + if (bChange) + { + ret = iValue; + return true; + } + break; + } + } + return false; +} + +bool CallFloat(const SendPropHook &hook, float &ret) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + float value = ret; + cell_t result = Pl_Continue; + callback->PushCell(hook.objectID); + callback->PushString(hook.pVar->GetName()); + callback->PushFloatByRef(&value); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + ret = value; + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + float flValue = ret; + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&flValue, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (void *)&flValue, hook.propType, hook.Element); + } + if (bChange) + { + ret = flValue; + return true; + } + break; + } + } + return false; +} + +bool CallFloatGamerules(const SendPropHookGamerules &hook, float &ret) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + float value = ret; + cell_t result = Pl_Continue; + callback->PushString(hook.pVar->GetName()); + callback->PushFloatByRef(&value); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + ret = value; + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + float flValue = ret; + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&flValue, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (void *)&flValue, hook.propType, hook.Element); + } + if (bChange) + { + ret = flValue; + return true; + } + break; + } + } + return false; +} + +bool CallString(const SendPropHook &hook, char *&ret) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + static char value[4096]; + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + strncpynull(value, ret, 4096); + cell_t result = Pl_Continue; + callback->PushCell(hook.objectID); + callback->PushString(hook.pVar->GetName()); + callback->PushStringEx(value, 4096, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + ret = value; + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + strncpynull(value, ret, 4096); + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)value, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (void *)value, hook.propType, hook.Element); + } + if (bChange) + { + ret = value; + return true; + } + break; + } + } + return false; +} + +bool CallStringGamerules(const SendPropHookGamerules &hook, char *&ret) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + static char value[4096]; + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + void *pGamerules = g_pSDKTools->GetGameRules(); + if(!pGamerules) + { + g_pSM->LogError(myself, "CRITICAL ERROR: Could not get gamerules pointer!"); + } + + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + strncpynull(value, ret, 4096); + cell_t result = Pl_Continue; + callback->PushString(hook.pVar->GetName()); + callback->PushStringEx(value, 4096, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + ret = value; + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + void * pGamerules = g_pSDKTools->GetGameRules(); + if(!pGamerules) + return false; + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + strncpynull(value, ret, 4096); + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)value, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (void *)value, hook.propType, hook.Element); + } + if (bChange) + { + ret = value; + return true; + } + break; + } + } + return false; +} + +bool CallVector(const SendPropHook &hook, Vector &vec) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + + cell_t vector[3]; + vector[0] = sp_ftoc(vec.x); + vector[1] = sp_ftoc(vec.y); + vector[2] = sp_ftoc(vec.z); + + cell_t result = Pl_Continue; + callback->PushCell(hook.objectID); + callback->PushString(hook.pVar->GetName()); + callback->PushArray(vector, 3, SM_PARAM_COPYBACK); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + vec.x = sp_ctof(vector[0]); + vec.y = sp_ctof(vector[1]); + vec.z = sp_ctof(vector[2]); + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + Vector vNewVec(vec.x, vec.y, vec.z); + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&vNewVec, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (void *)&vNewVec, hook.propType, hook.Element); + } + if (bChange) + { + vec.x = vNewVec.x; + vec.y = vNewVec.y; + vec.z = vNewVec.z; + return true; + } + break; + } + } + return false; +} + +bool CallVectorGamerules(const SendPropHookGamerules &hook, Vector &vec) +{ + if (!g_bSVComputePacksDone) + return false; + + AUTO_LOCK_FM(g_WorkMutex); + + if(hook.per_client && g_iCurrentClientIndexInLoop == -1) { + return false; + } + + switch (hook.sCallbackInfo.iCallbackType) + { + case CallBackType::Callback_PluginFunction: + { + IPluginFunction *callback = (IPluginFunction *)hook.sCallbackInfo.pCallback; + + cell_t vector[3]; + vector[0] = sp_ftoc(vec.x); + vector[1] = sp_ftoc(vec.y); + vector[2] = sp_ftoc(vec.z); + + cell_t result = Pl_Continue; + callback->PushString(hook.pVar->GetName()); + callback->PushArray(vector, 3, SM_PARAM_COPYBACK); + callback->PushCell(hook.Element); + if(hook.per_client) { + callback->PushCell(g_iCurrentClientIndexInLoop + 1); + } + callback->Execute(&result); + if (result == Pl_Changed) + { + vec.x = sp_ctof(vector[0]); + vec.y = sp_ctof(vector[1]); + vec.z = sp_ctof(vector[2]); + return true; + } + break; + } + case CallBackType::Callback_CPPCallbackInterface: + { + ISendProxyCallbacks * pCallbacks = (ISendProxyCallbacks *)hook.sCallbackInfo.pCallback; + Vector vNewVec(vec.x, vec.y, vec.z); + bool bChange = false; + if(hook.per_client) { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&vNewVec, hook.propType, hook.Element); + } else { + bChange = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (void *)&vNewVec, hook.propType, hook.Element); + } + if (bChange) + { + vec.x = vNewVec.x; + vec.y = vNewVec.y; + vec.z = vNewVec.z; + return true; + } + break; + } + } + return false; +} + +void GlobalProxy(const SendProp *pProp, const void *pStructBase, const void * pData, DVariant *pOut, int iElement, int objectID) +{ + edict_t * pEnt = gamehelpers->EdictOfIndex(objectID); + bool bHandled = false; + for (auto &it : g_Hooks) + { + if (it.objectID == objectID && + it.pVar == pProp && + pEnt == it.pEnt) + { + switch (it.propType) + { + case PropType::Prop_Int: + { + int result = *(int *)pData; + + if (CallInt(it, result)) + { + long data = result; + it.pRealProxy(pProp, pStructBase, &data, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + case PropType::Prop_Float: + { + float result = *(float *)pData; + + if (CallFloat(it, result)) + { + it.pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + case PropType::Prop_String: + { + char * result = *(char **)pData; + + if (CallString(it, result)) + { + it.pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + case PropType::Prop_Vector: + { + Vector result = *(Vector *)pData; + + if (CallVector(it, result)) + { + it.pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, it.pVar->GetName()); + } + } + } + if (!bHandled) + { + //perhaps we aren't hooked, but we can still find the real proxy for this prop + for (auto &it : g_Hooks) + { + if (it.pVar == pProp) + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + return; + } + } + g_pSM->LogError(myself, "CRITICAL: Proxy for unmanaged entity %d called for prop %s", objectID, pProp->GetName()); + } +} + +void GlobalProxyGamerules(const SendProp *pProp, const void *pStructBase, const void * pData, DVariant *pOut, int iElement, int objectID) +{ + if (!g_bShouldChangeGameRulesState) + g_bShouldChangeGameRulesState = true; //If this called once, so, the props wants to be sent at this time, and we should do this for all clients! + bool bHandled = false; + for (auto &it : g_HooksGamerules) + { + if (it.pVar == pProp) + { + switch (it.propType) + { + case PropType::Prop_Int: + { + int result = *(int *)pData; + + if (CallIntGamerules(it, result)) + { + long data = result; + it.pRealProxy(pProp, pStructBase, &data, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + case PropType::Prop_Float: + { + float result = *(float *)pData; + + if (CallFloatGamerules(it, result)) + { + it.pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + case PropType::Prop_String: + { + char * result = *(char **)pData; //We need to use const because of C++11 restriction + + if (CallStringGamerules(it, result)) + { + it.pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + case PropType::Prop_Vector: + { + Vector result = *(Vector *)pData; + + if (CallVectorGamerules(it, result)) + { + it.pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID); + return; // If somebody already handled this call, do not call other hooks for this entity & prop + } + else + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + } + bHandled = true; + continue; + } + default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, it.pVar->GetName()); + } + } + } + if (!bHandled) + { + //perhaps we aren't hooked, but we can still find the real proxy for this prop + for (auto &it : g_HooksGamerules) + { + if (it.pVar == pProp) + { + it.pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID); + return; + } + } + g_pSM->LogError(myself, "CRITICAL: Proxy for unmanaged gamerules called for prop %s", pProp->GetName()); + } +} + +//help + +CBaseEntity * FindEntityByServerClassname(int iStart, const char * pServerClassName) +{ + if (iStart >= g_iEdictCount) + return nullptr; + for (int i = iStart; i < g_iEdictCount; i++) + { + CBaseEntity * pEnt = gamehelpers->ReferenceToEntity(i); + if (!pEnt) + continue; + IServerNetworkable * pNetworkable = ((IServerUnknown *)pEnt)->GetNetworkable(); + if (!pNetworkable) + continue; + const char * pName = pNetworkable->GetServerClass()->GetName(); + if (pName && !strcmp(pName, pServerClassName)) + return pEnt; + } + return nullptr; +} + +bool IsPropValid(SendProp * pProp, PropType iType) +{ + switch (iType) + { + case PropType::Prop_Int: + if (pProp->GetType() != DPT_Int) + return false; + return true; + case PropType::Prop_Float: + { + if (pProp->GetType() != DPT_Float) + return false; + return true; + } + case PropType::Prop_String: + { + if (pProp->GetType() != DPT_String) + return false; + return true; + } + case PropType::Prop_Vector: + { + if (pProp->GetType() != DPT_Vector) + return false; + return true; + } + } + return false; +} + +char * strncpynull(char * pDestination, const char * pSource, size_t szCount) +{ + if(!pSource) { + pSource = ""; + } + + strncpy(pDestination, pSource, szCount); + pDestination[szCount - 1] = 0; + return pDestination; +} diff --git a/extension.h b/extension.h new file mode 100644 index 0000000..1e15edc --- /dev/null +++ b/extension.h @@ -0,0 +1,255 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _EXTENSION_H_INC_ +#define _EXTENSION_H_INC_ + + /* + TODO: + Implement interface: + Add common function for prop change hooks + Add remove listeners for prop change hooks + */ + +#include "smsdk_ext.h" +#include +#include +#include "convar.h" +#include "dt_send.h" +#include "server_class.h" +#include "ISendProxy.h" +#include +#include +#include +#include + +#define GET_CONVAR(name) \ + name = g_pCVar->FindVar(#name); \ + if (name == nullptr) { \ + if (error != nullptr && maxlen != 0) { \ + ismm->Format(error, maxlen, "Could not find ConVar: " #name); \ + } \ + return false; \ + } + +class IServerGameEnts; + +void GlobalProxy(const SendProp *pProp, const void *pStructBase, const void* pData, DVariant *pOut, int iElement, int objectID); +void GlobalProxyGamerules(const SendProp *pProp, const void *pStructBase, const void* pData, DVariant *pOut, int iElement, int objectID); +bool IsPropValid(SendProp *, PropType); +char * strncpynull(char * pDestination, const char * pSource, size_t szCount); + +struct ListenerCallbackInfo +{ + IExtension * m_pExt; + IExtensionInterface * m_pExtAPI; + ISendProxyUnhookListener * m_pCallBack; +}; + +struct CallBackInfo +{ + CallBackInfo() = default; + CallBackInfo(const CallBackInfo & rObj) = default; + CallBackInfo(CallBackInfo && rObj) = default; + CallBackInfo & operator=(const CallBackInfo & rObj) = default; + CallBackInfo & operator=(CallBackInfo && rObj) = default; + ~CallBackInfo() = default; + void * pOwner; //Pointer to plugin context or IExtension * + void * pCallback; + CallBackType iCallbackType; +}; + +struct SendPropHook +{ + SendPropHook() = default; + SendPropHook(const SendPropHook & rObj) = default; + SendPropHook(SendPropHook && rObj) = default; + SendPropHook &operator=(SendPropHook &&) = default; + SendPropHook &operator=(const SendPropHook &) = default; + ~SendPropHook() = default; + CallBackInfo sCallbackInfo; + SendProp * pVar; + edict_t * pEnt; + SendVarProxyFn pRealProxy; + int objectID; + PropType propType; + int Offset; + int Element{0}; + std::vector vListeners; + bool per_client = false; +}; + +struct SendPropHookGamerules +{ + SendPropHookGamerules() = default; + SendPropHookGamerules(const SendPropHookGamerules & rObj) = default; + SendPropHookGamerules(SendPropHookGamerules && rObj) = default; + SendPropHookGamerules &operator=(SendPropHookGamerules &&) = default; + SendPropHookGamerules &operator=(const SendPropHookGamerules &) = default; + ~SendPropHookGamerules() = default; + CallBackInfo sCallbackInfo; + SendProp * pVar; + SendVarProxyFn pRealProxy; + PropType propType; + int Element{0}; + std::vector vListeners; + bool per_client = false; +}; + +struct dumbvec_t +{ + dumbvec_t() = default; + dumbvec_t(const dumbvec_t &) = default; + dumbvec_t(dumbvec_t &&) = default; + dumbvec_t &operator=(const dumbvec_t &) = default; + dumbvec_t &operator=(dumbvec_t &&) = default; + + float &operator[](int i) { return value[i]; } + const float &operator[](int i) const { return value[i]; } + dumbvec_t &operator=(const Vector &vec) { + value[0] = vec.x; + value[1] = vec.y; + value[2] = vec.z; + return *this; + } + + bool operator==(const Vector &vec) { + return value[0] == vec.x && + value[1] == vec.y && + value[2] == vec.z; + } + + bool operator!=(const Vector &vec) { + return value[0] != vec.x && + value[1] != vec.y && + value[2] != vec.z; + } + + float value[3]; +}; + +struct PropChangeHook +{ + PropChangeHook() = default; + PropChangeHook(const PropChangeHook & rObj) = default; + PropChangeHook(PropChangeHook && rObj) = default; + PropChangeHook &operator=(PropChangeHook &&) = default; + PropChangeHook &operator=(const PropChangeHook &) = default; + ~PropChangeHook() = default; + union //unfortunately we MUST use union instead of std::variant cuz we should prevent libstdc++ linking in linux =| + { + int iLastValue{0}; + float flLastValue; + dumbvec_t vecLastValue; + char cLastValue[4096]; + }; + SendProp * pVar; + PropType propType; + unsigned int Offset; + int objectID; + int Element{0}; + std::vector vCallbacksInfo; +}; + +struct PropChangeHookGamerules +{ + PropChangeHookGamerules() = default; + PropChangeHookGamerules(const PropChangeHookGamerules & rObj) = default; + PropChangeHookGamerules(PropChangeHookGamerules && rObj) = default; + PropChangeHookGamerules &operator=(PropChangeHookGamerules &&) = default; + PropChangeHookGamerules &operator=(const PropChangeHookGamerules &) = default; + ~PropChangeHookGamerules() = default; + union //unfortunately we MUST use union instead of std::variant cuz we should prevent libstdc++ linking in linux =| + { + int iLastValue{0}; + float flLastValue; + dumbvec_t vecLastValue; + char cLastValue[4096]; + }; + SendProp * pVar; + PropType propType; + unsigned int Offset; + int Element{0}; + std::vector vCallbacksInfo; +}; + +extern std::vector g_Hooks; +extern std::vector g_HooksGamerules; +extern std::vector g_ChangeHooks; +extern std::vector g_ChangeHooksGamerules; + +class SendProxyManager : + public SDKExtension, + public IPluginsListener, + public ISMEntityListener +{ +public: //sm + virtual bool SDK_OnLoad(char * error, size_t maxlength, bool late); + virtual void SDK_OnUnload(); + virtual void SDK_OnAllLoaded(); + + virtual void OnCoreMapEnd(); + virtual void OnCoreMapStart(edict_t *, int, int); + +public: //other + virtual void OnPluginUnloaded(IPlugin * plugin); + //returns true upon success + bool AddHookToList(SendPropHook &&hook); + bool AddHookToListGamerules(SendPropHookGamerules &&hook); + + //returns false if prop type not supported or entity is invalid + bool AddChangeHookToList(PropChangeHook &&sHook, CallBackInfo &&pInfo); + bool AddChangeHookToListGamerules(PropChangeHookGamerules &&sHook, CallBackInfo &&pInfo); + + bool UnhookProxy(const SendPropHook &hook); + bool UnhookProxyGamerules(const SendPropHookGamerules &hook); + + bool UnhookChange(PropChangeHook &hook, const CallBackInfo &pInfo); + bool UnhookChangeGamerules(PropChangeHookGamerules &hook, const CallBackInfo &pInfo); + virtual int GetClientCount() const; +public: // ISMEntityListener + virtual void OnEntityDestroyed(CBaseEntity * pEntity); +public: +#if defined SMEXT_CONF_METAMOD + virtual bool SDK_OnMetamodLoad(ISmmAPI * ismm, char * error, size_t maxlen, bool late); + //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); + //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); +#endif +}; + +extern SendProxyManager g_SendProxyManager; +extern IServerGameEnts * gameents; +extern const char * g_szGameRulesProxy; +constexpr int g_iEdictCount = 2048; //default value, we do not need to get it manually cuz it is constant +extern ISDKTools * g_pSDKTools; +extern void * g_pGameRules; + +#endif // _EXTENSION_H_INC_ diff --git a/interfaceimpl.cpp b/interfaceimpl.cpp new file mode 100644 index 0000000..affb79a --- /dev/null +++ b/interfaceimpl.cpp @@ -0,0 +1,938 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "interfaceimpl.h" + +SH_DECL_HOOK0_void(IExtensionInterface, OnExtensionUnload, SH_NOATTRIB, false); + +void Hook_OnExtensionUnload() +{ + IExtensionInterface * pExtAPI = META_IFACEPTR(IExtensionInterface); + auto it = g_Hooks.begin(); + while(it != g_Hooks.end()) + { + auto &hook = *it; + + //remove all listeners for this extension + auto &listeners = hook.vListeners; + auto it2 = listeners.begin(); + while(it2 != listeners.end()) { + if (it2->m_pExtAPI == pExtAPI) { + listeners.erase(it2); + continue; + } + ++it2; + } + + //remove hook for this extension + if (hook.sCallbackInfo.iCallbackType == CallBackType::Callback_CPPCallbackInterface && + static_cast(hook.sCallbackInfo.pOwner)->GetAPI() == pExtAPI) { + g_SendProxyManager.UnhookProxy(hook); + g_Hooks.erase(it); + continue; + } + + ++it; + } + + auto it2 = g_HooksGamerules.begin(); + while(it2 != g_HooksGamerules.end()) { + auto &hook = *it2; + + //remove all listeners for this extension + auto &listeners = hook.vListeners; + auto it3 = listeners.begin(); + while(it3 != listeners.end()) { + if (it3->m_pExtAPI == pExtAPI) { + listeners.erase(it3); + continue; + } + ++it3; + } + + //remove hook for this extension + if (hook.sCallbackInfo.iCallbackType == CallBackType::Callback_CPPCallbackInterface && + static_cast(hook.sCallbackInfo.pOwner)->GetAPI() == pExtAPI) { + g_SendProxyManager.UnhookProxyGamerules(hook); + g_HooksGamerules.erase(it2); + continue; + } + + ++it2; + } + SH_REMOVE_HOOK(IExtensionInterface, OnExtensionUnload, pExtAPI, SH_STATIC(Hook_OnExtensionUnload), false); + RETURN_META(MRES_IGNORED); +} + +void HookExtensionUnload(IExtension * pExt) +{ + if (!pExt) + return; + + bool bHookedAlready = false; + for (auto &it : g_Hooks) + { + if (it.sCallbackInfo.pOwner == pExt) + { + bHookedAlready = true; + break; + } + else { + for (auto &it2 : it.vListeners) + { + if (it2.m_pExtAPI == pExt->GetAPI()) + { + bHookedAlready = true; + break; + } + } + } + } + if (!bHookedAlready) { + for (auto &it : g_HooksGamerules) + { + if (it.sCallbackInfo.pOwner == pExt) + { + bHookedAlready = true; + break; + } + else { + for (auto &it2 : it.vListeners) + { + if (it2.m_pExtAPI == pExt->GetAPI()) + { + bHookedAlready = true; + break; + } + } + } + } + } + if (!bHookedAlready) //Hook only if needed! + SH_ADD_HOOK(IExtensionInterface, OnExtensionUnload, pExt->GetAPI(), SH_STATIC(Hook_OnExtensionUnload), false); +} + +void UnhookExtensionUnload(IExtension * pExt) +{ + if (!pExt) + return; + + bool bHaveHooks = false; + for (auto &it : g_Hooks) + { + if (it.sCallbackInfo.pOwner == pExt) + { + bHaveHooks = true; + break; + } + else { + auto &listeners = it.vListeners; + for (auto &it2 : listeners) + { + if (it2.m_pExtAPI == pExt->GetAPI()) + { + bHaveHooks = true; + break; + } + } + } + } + if (!bHaveHooks) { + for (auto &it : g_HooksGamerules) + { + if (it.sCallbackInfo.pOwner == pExt) + { + bHaveHooks = true; + break; + } + else { + auto &listeners = it.vListeners; + for (auto &it2 : listeners) + { + if (it2.m_pExtAPI == pExt->GetAPI()) + { + bHaveHooks = true; + break; + } + } + } + } + } + + if (!bHaveHooks) //so, if there are active hooks, we shouldn't remove hook! + SH_REMOVE_HOOK(IExtensionInterface, OnExtensionUnload, pExt->GetAPI(), SH_STATIC(Hook_OnExtensionUnload), false); +} + +void CallListenersForHook(const SendPropHook &hook) +{ + for (auto &it : hook.vListeners) + { + it.m_pCallBack->OnEntityPropHookRemoved(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, hook.propType, hook.sCallbackInfo.iCallbackType, hook.sCallbackInfo.pCallback); + } +} + +void CallListenersForHookGamerules(const SendPropHookGamerules &hook) +{ + for (auto &it : hook.vListeners) + { + it.m_pCallBack->OnGamerulesPropHookRemoved(hook.pVar, hook.propType, hook.sCallbackInfo.iCallbackType, hook.sCallbackInfo.pCallback); + } +} + +//interface + +const char * SendProxyManagerInterfaceImpl::GetInterfaceName() { return SMINTERFACE_SENDPROXY_NAME; } +unsigned int SendProxyManagerInterfaceImpl::GetInterfaceVersion() { return SMINTERFACE_SENDPROXY_VERSION; } + +bool SendProxyManagerInterfaceImpl::HookProxy(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client) +{ + if (!pEntity) + return false; + + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + if (!pEdict || pEdict->IsFree()) + return false; + + if (!IsPropValid(pProp, iType)) + return false; + + SendPropHook hook; + hook.objectID = gamehelpers->IndexOfEdict(pEdict); + hook.sCallbackInfo.pCallback = pCallback; + hook.sCallbackInfo.iCallbackType = iCallbackType; + hook.sCallbackInfo.pOwner = (void *)pExt; + hook.propType = iType; + hook.pEnt = pEdict; + hook.pVar = pProp; + hook.per_client = per_client; + bool bHookedAlready = false; + for (auto &it : g_Hooks) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + HookExtensionUnload(pExt); + if (g_SendProxyManager.AddHookToList(std::move(hook))) + { + if (!bHookedAlready) + pProp->SetProxyFn(GlobalProxy); + } + else + { + UnhookExtensionUnload(pExt); + return false; + } + return true; +} + +bool SendProxyManagerInterfaceImpl::HookProxy(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client) +{ + if (!pProp || !*pProp) + return false; + if (!pEntity) + return false; + ServerClass * sc = ((IServerUnknown *)pEntity)->GetNetworkable()->GetServerClass(); + if (!sc) + return false; //we don't use exceptions, bad extensions may do not handle this and server will crashed, just return false + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), pProp, &info); + SendProp * pSendProp = info.prop; + if (pSendProp) + return HookProxy(pExt, pSendProp, pEntity, iType, iCallbackType, pCallback, per_client); + return false; +} + +bool SendProxyManagerInterfaceImpl::HookProxyGamerules(IExtension * pExt, SendProp * pProp, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client) +{ + if (!IsPropValid(pProp, iType)) + return false; + + SendPropHookGamerules hook; + hook.sCallbackInfo.pCallback = pCallback; + hook.sCallbackInfo.iCallbackType = iCallbackType; + hook.sCallbackInfo.pOwner = (void *)pExt; + bool bHookedAlready = false; + for (auto &it : g_HooksGamerules) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + hook.propType = iType; + hook.pVar = pProp; + hook.per_client = per_client; + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, pProp->GetName(), &info); + + //if this prop has been hooked already, don't set the proxy again + HookExtensionUnload(pExt); + if (bHookedAlready) + { + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + return true; + UnhookExtensionUnload(pExt); + return false; + } + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + { + pProp->SetProxyFn(GlobalProxyGamerules); + return true; + } + UnhookExtensionUnload(pExt); + return false; +} + +bool SendProxyManagerInterfaceImpl::HookProxyGamerules(IExtension * pExt, const char * pProp, PropType iType, CallBackType iCallbackType, void * pCallback, bool per_client) +{ + if (!pProp || !*pProp) + return false; + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, pProp, &info); + SendProp * pSendProp = info.prop; + if (pSendProp) + return HookProxyGamerules(pExt, pSendProp, iType, iCallbackType, pCallback, per_client); + return false; +} + +bool SendProxyManagerInterfaceImpl::UnhookProxy(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback) +{ + const char * pPropName = pProp->GetName(); + return UnhookProxy(pExt, pPropName, pEntity, iCallbackType, pCallback); +} + +bool SendProxyManagerInterfaceImpl::UnhookProxy(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback) +{ + if (!pProp || !*pProp) + return false; + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + auto it = g_Hooks.begin(); + while(it != g_Hooks.end()) { + auto &hook = *it; + + if (hook.sCallbackInfo.pOwner == pExt && + pEdict == hook.pEnt && + hook.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(hook.pVar->GetName(), pProp) && + pCallback == hook.sCallbackInfo.pCallback) + { + g_SendProxyManager.UnhookProxy(hook); + g_Hooks.erase(it); + UnhookExtensionUnload(pExt); + return true; + } + + ++it; + } + return false; +} + +bool SendProxyManagerInterfaceImpl::UnhookProxyGamerules(IExtension * pExt, SendProp * pProp, CallBackType iCallbackType, void * pCallback) +{ + const char * pPropName = pProp->GetName(); + return UnhookProxyGamerules(pExt, pPropName, iCallbackType, pCallback); +} + +bool SendProxyManagerInterfaceImpl::UnhookProxyGamerules(IExtension * pExt, const char * pProp, CallBackType iCallbackType, void * pCallback) +{ + if (!pProp || !*pProp) + return false; + + auto it = g_HooksGamerules.begin(); + while(it != g_HooksGamerules.end()) { + auto &hook = *it; + + if (hook.sCallbackInfo.pOwner == pExt && + hook.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(hook.pVar->GetName(), pProp) && + pCallback == hook.sCallbackInfo.pCallback) + { + g_SendProxyManager.UnhookProxyGamerules(hook); + g_HooksGamerules.erase(it); + UnhookExtensionUnload(pExt); + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListener(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return AddUnhookListener(pExt, pPropName, pEntity, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListener(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + for (auto &it : g_Hooks) { + if (pEdict == it.pEnt && + it.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + ListenerCallbackInfo info; + info.m_pExt = pExt; + info.m_pExtAPI = pExt->GetAPI(); + info.m_pCallBack = pListener; + HookExtensionUnload(pExt); + it.vListeners.emplace_back(std::move(info)); + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListenerGamerules(IExtension * pExt, SendProp * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return AddUnhookListenerGamerules(pExt, pPropName, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListenerGamerules(IExtension * pExt, const char * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + for (auto &it : g_HooksGamerules) { + if (it.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + ListenerCallbackInfo info; + info.m_pExt = pExt; + info.m_pExtAPI = pExt->GetAPI(); + info.m_pCallBack = pListener; + HookExtensionUnload(pExt); + it.vListeners.emplace_back(std::move(info)); + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListener(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return RemoveUnhookListener(pExt, pPropName, pEntity, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListener(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + for (auto &it : g_Hooks) { + if (it.pEnt == pEdict && + it.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + auto &listeners = it.vListeners; + auto it2 = listeners.begin(); + while(it2 != listeners.end()) + { + auto &cb = *it2; + + if (cb.m_pExt == pExt && + cb.m_pCallBack == pListener) + { + listeners.erase(it2); + UnhookExtensionUnload(pExt); + return true; + } + + ++it2; + } + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListenerGamerules(IExtension * pExt, SendProp * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return RemoveUnhookListenerGamerules(pExt, pPropName, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListenerGamerules(IExtension * pExt, const char * pProp, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + + for (auto &it : g_HooksGamerules) { + if (it.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + auto &listeners = it.vListeners; + auto it2 = listeners.begin(); + while(it2 != listeners.end()) + { + auto &cb = *it2; + + if (cb.m_pExt == pExt && + cb.m_pCallBack == pListener) + { + listeners.erase(it2); + UnhookExtensionUnload(pExt); + return true; + } + + ++it2; + } + } + } + return false; +} + +//same for the arrays + +bool SendProxyManagerInterfaceImpl::HookProxyArray(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) +{ + if (!pEntity) + return false; + + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + if (!pEdict || pEdict->IsFree()) + return false; + + SendTable * pSendTable = pProp->GetDataTable(); + if (!pSendTable) + return false; + + SendProp * pPropElem = pSendTable->GetProp(iElement); + if (!pPropElem) + return false; + + if (!IsPropValid(pPropElem, iType)) + return false; + + SendPropHook hook; + hook.objectID = gamehelpers->IndexOfEdict(pEdict); + hook.sCallbackInfo.pCallback = pCallback; + hook.sCallbackInfo.iCallbackType = iCallbackType; + hook.sCallbackInfo.pOwner = (void *)pExt; + hook.propType = iType; + hook.pEnt = pEdict; + hook.pVar = pPropElem; + hook.Element = iElement; + bool bHookedAlready = false; + for (auto &it : g_Hooks) + { + if (it.pVar == pPropElem) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pPropElem->GetProxyFn(); + HookExtensionUnload(pExt); + if (g_SendProxyManager.AddHookToList(std::move(hook))) + { + if (!bHookedAlready) + pPropElem->SetProxyFn(GlobalProxy); + } + else + { + UnhookExtensionUnload(pExt); + return false; + } + return true; +} + +bool SendProxyManagerInterfaceImpl::HookProxyArray(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) +{ + if (!pProp || !*pProp) + return false; + if (!pEntity) + return false; + ServerClass * sc = ((IServerUnknown *)pEntity)->GetNetworkable()->GetServerClass(); + if (!sc) + return false; //we don't use exceptions, bad extensions may do not handle this and server will crashed, just return false + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), pProp, &info); + SendProp * pSendProp = info.prop; + if (pSendProp) + return HookProxyArray(pExt, pSendProp, pEntity, iType, iElement, iCallbackType, pCallback); + return false; +} + +bool SendProxyManagerInterfaceImpl::HookProxyArrayGamerules(IExtension * pExt, SendProp * pProp, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) +{ + SendTable * pSendTable = pProp->GetDataTable(); + if (!pSendTable) + return false; + + SendProp * pPropElem = pSendTable->GetProp(iElement); + if (!pPropElem) + return false; + + if (!IsPropValid(pPropElem, iType)) + return false; + + SendPropHookGamerules hook; + hook.sCallbackInfo.pCallback = pCallback; + hook.sCallbackInfo.iCallbackType = iCallbackType; + hook.sCallbackInfo.pOwner = (void *)pExt; + bool bHookedAlready = false; + for (auto &it : g_HooksGamerules) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + hook.propType = iType; + hook.pVar = pProp; + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, pProp->GetName(), &info); + hook.Element = iElement; + + //if this prop has been hooked already, don't set the proxy again + HookExtensionUnload(pExt); + if (bHookedAlready) + { + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + return true; + UnhookExtensionUnload(pExt); + return false; + } + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + { + pProp->SetProxyFn(GlobalProxyGamerules); + return true; + } + UnhookExtensionUnload(pExt); + return false; +} + +bool SendProxyManagerInterfaceImpl::HookProxyArrayGamerules(IExtension * pExt, const char * pProp, PropType iType, int iElement, CallBackType iCallbackType, void * pCallback) +{ + if (!pProp || !*pProp) + return false; + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, pProp, &info); + SendProp * pSendProp = info.prop; + if (pSendProp) + return HookProxyArrayGamerules(pExt, pSendProp, iType, iElement, iCallbackType, pCallback); + return false; +} + +bool SendProxyManagerInterfaceImpl::UnhookProxyArray(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback) +{ + const char * pPropName = pProp->GetName(); + return UnhookProxyArray(pExt, pPropName, pEntity, iElement, iCallbackType, pCallback); +} + +bool SendProxyManagerInterfaceImpl::UnhookProxyArray(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback) +{ + if (!pProp || !*pProp) + return false; + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + auto it = g_Hooks.begin(); + while (it != g_Hooks.end()) { + auto &hook = *it; + + if (hook.sCallbackInfo.pOwner == pExt && + hook.Element == iElement && + pEdict == hook.pEnt && + hook.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(hook.pVar->GetName(), pProp) && + pCallback == hook.sCallbackInfo.pCallback) + { + g_SendProxyManager.UnhookProxy(hook); + g_Hooks.erase(it); + UnhookExtensionUnload(pExt); + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::UnhookProxyArrayGamerules(IExtension * pExt, SendProp * pProp, int iElement, CallBackType iCallbackType, void * pCallback) +{ + const char * pPropName = pProp->GetName(); + return UnhookProxyArrayGamerules(pExt, pPropName, iElement, iCallbackType, pCallback); +} + +bool SendProxyManagerInterfaceImpl::UnhookProxyArrayGamerules(IExtension * pExt, const char * pProp, int iElement, CallBackType iCallbackType, void * pCallback) +{ + if (!pProp || !*pProp) + return false; + auto it = g_HooksGamerules.begin(); + while(it != g_HooksGamerules.end()) { + auto &hook = *it; + + if (hook.sCallbackInfo.pOwner == pExt && + hook.Element == iElement && + hook.sCallbackInfo.iCallbackType == iCallbackType && + !strcmp(hook.pVar->GetName(), pProp) && + pCallback == hook.sCallbackInfo.pCallback) + { + g_SendProxyManager.UnhookProxyGamerules(hook); + g_HooksGamerules.erase(it); + UnhookExtensionUnload(pExt); + return true; + } + + ++it; + } + return false; +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListenerArray(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return AddUnhookListenerArray(pExt, pPropName, pEntity, iElement, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListenerArray(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + for (auto &it : g_Hooks) { + if (pEdict == it.pEnt && + it.sCallbackInfo.iCallbackType == iCallbackType && + it.Element == iElement && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + ListenerCallbackInfo info; + info.m_pExt = pExt; + info.m_pExtAPI = pExt->GetAPI(); + info.m_pCallBack = pListener; + HookExtensionUnload(pExt); + it.vListeners.emplace_back(std::move(info)); + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListenerArrayGamerules(IExtension * pExt, SendProp * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return AddUnhookListenerArrayGamerules(pExt, pPropName, iElement, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::AddUnhookListenerArrayGamerules(IExtension * pExt, const char * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + for (auto &it : g_HooksGamerules) { + if (it.sCallbackInfo.iCallbackType == iCallbackType && + it.Element == iElement && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + ListenerCallbackInfo info; + info.m_pExt = pExt; + info.m_pExtAPI = pExt->GetAPI(); + info.m_pCallBack = pListener; + HookExtensionUnload(pExt); + it.vListeners.emplace_back(std::move(info)); + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListenerArray(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return RemoveUnhookListenerArray(pExt, pPropName, pEntity, iElement, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListenerArray(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + for (auto &it : g_Hooks) { + if (it.pEnt == pEdict && + it.sCallbackInfo.iCallbackType == iCallbackType && + it.Element == iElement && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + auto &listeners = it.vListeners; + auto it2 = listeners.begin(); + while(it2 != listeners.end()) + { + if (it2->m_pExt == pExt && + it2->m_pCallBack == pListener) + { + listeners.erase(it2); + UnhookExtensionUnload(pExt); + return true; + } + + ++it2; + } + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListenerArrayGamerules(IExtension * pExt, SendProp * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + const char * pPropName = pProp->GetName(); + return RemoveUnhookListenerArrayGamerules(pExt, pPropName, iElement, iCallbackType, pCallback, pListener); +} + +bool SendProxyManagerInterfaceImpl::RemoveUnhookListenerArrayGamerules(IExtension * pExt, const char * pProp, int iElement, CallBackType iCallbackType, void * pCallback, ISendProxyUnhookListener * pListener) +{ + if (!pProp || !*pProp) + return false; + + for (auto &it : g_HooksGamerules) { + if (it.sCallbackInfo.iCallbackType == iCallbackType && + it.Element == iElement && + !strcmp(it.pVar->GetName(), pProp) && + pCallback == it.sCallbackInfo.pCallback) + { + auto &listeners = it.vListeners; + auto it2 = listeners.begin(); + while(it2 != listeners.end()) + { + if (it2->m_pExt == pExt && + it2->m_pCallBack == pListener) + { + listeners.erase(it2); + UnhookExtensionUnload(pExt); + return true; + } + + ++it2; + } + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::IsProxyHooked(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity) const +{ + return IsProxyHooked(pExt, pProp->GetName(), pEntity); +} + +bool SendProxyManagerInterfaceImpl::IsProxyHooked(IExtension * pExt, const char * pProp, CBaseEntity * pEntity) const +{ + if (!pProp || !*pProp) + return false; + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + for (auto &it : g_Hooks) { + if (it.sCallbackInfo.pOwner == (void *)pExt && + it.pEnt == pEdict && + !strcmp(pProp, it.pVar->GetName())) { + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::IsProxyHookedGamerules(IExtension * pExt, SendProp * pProp) const +{ + return IsProxyHookedGamerules(pExt, pProp->GetName()); +} + +bool SendProxyManagerInterfaceImpl::IsProxyHookedGamerules(IExtension * pExt, const char * pProp) const +{ + if (!pProp || !*pProp) + return false; + for (auto &it : g_HooksGamerules) { + if (it.sCallbackInfo.pOwner == (void *)pExt && + !strcmp(pProp, it.pVar->GetName())) { + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::IsProxyHookedArray(IExtension * pExt, SendProp * pProp, CBaseEntity * pEntity, int iElement) const +{ + return IsProxyHookedArray(pExt, pProp->GetName(), pEntity, iElement); +} + +bool SendProxyManagerInterfaceImpl::IsProxyHookedArray(IExtension * pExt, const char * pProp, CBaseEntity * pEntity, int iElement) const +{ + if (!pProp || !*pProp) + return false; + edict_t * pEdict = gameents->BaseEntityToEdict(pEntity); + for (auto &it : g_Hooks) { + if (it.sCallbackInfo.pOwner == (void *)pExt && + it.pEnt == pEdict && + it.Element == iElement && + !strcmp(pProp, it.pVar->GetName())) { + return true; + } + } + return false; +} + +bool SendProxyManagerInterfaceImpl::IsProxyHookedArrayGamerules(IExtension * pExt, SendProp * pProp, int iElement) const +{ + return IsProxyHookedArrayGamerules(pExt, pProp->GetName(), iElement); +} + +bool SendProxyManagerInterfaceImpl::IsProxyHookedArrayGamerules(IExtension * pExt, const char * pProp, int iElement) const +{ + if (!pProp || !*pProp) + return false; + for (auto &it : g_HooksGamerules) { + if (it.sCallbackInfo.pOwner == (void *)pExt && + it.Element == iElement && + !strcmp(pProp, it.pVar->GetName())) { + return true; + } + } + return false; +} diff --git a/interfaceimpl.h b/interfaceimpl.h new file mode 100644 index 0000000..d61b193 --- /dev/null +++ b/interfaceimpl.h @@ -0,0 +1,123 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef SENDPROXY_IFACE_IMPL_INC +#define SENDPROXY_IFACE_IMPL_INC + +#include "extension.h" +#include "ISendProxy.h" + +void CallListenersForHook(const SendPropHook &hook); +void CallListenersForHookGamerules(const SendPropHookGamerules &hook); + +class SendProxyManagerInterfaceImpl : public ISendProxyManager +{ +public: //SMInterface + virtual const char * GetInterfaceName() override; + virtual unsigned int GetInterfaceVersion() override; +public: //interface impl: + virtual bool HookProxy(IExtension *, SendProp *, CBaseEntity *, PropType, CallBackType, void *, bool) override; + virtual bool HookProxy(IExtension *, const char *, CBaseEntity *, PropType, CallBackType, void *, bool) override; + virtual bool HookProxyGamerules(IExtension *, SendProp *, PropType, CallBackType, void *, bool) override; + virtual bool HookProxyGamerules(IExtension *, const char *, PropType, CallBackType, void *, bool) override; + virtual bool UnhookProxy(IExtension *, SendProp *, CBaseEntity *, CallBackType, void *) override; + virtual bool UnhookProxy(IExtension *, const char *, CBaseEntity *, CallBackType, void *) override; + virtual bool UnhookProxyGamerules(IExtension *, SendProp *, CallBackType, void *) override; + virtual bool UnhookProxyGamerules(IExtension *, const char *, CallBackType, void *) override; + virtual bool AddUnhookListener(IExtension *, SendProp *, CBaseEntity *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool AddUnhookListener(IExtension *, const char *, CBaseEntity *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool AddUnhookListenerGamerules(IExtension *, SendProp *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool AddUnhookListenerGamerules(IExtension *, const char *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListener(IExtension *, SendProp *, CBaseEntity *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListener(IExtension *, const char *, CBaseEntity *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListenerGamerules(IExtension *, SendProp *, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListenerGamerules(IExtension *, const char *, CallBackType, void *, ISendProxyUnhookListener *) override; + //same for the arrays =| + virtual bool HookProxyArray(IExtension *, SendProp *, CBaseEntity *, PropType, int, CallBackType, void *) override; + virtual bool HookProxyArray(IExtension *, const char *, CBaseEntity *, PropType, int, CallBackType, void *) override; + virtual bool UnhookProxyArray(IExtension *, SendProp *, CBaseEntity *, int, CallBackType, void *) override; + virtual bool UnhookProxyArray(IExtension *, const char *, CBaseEntity *, int, CallBackType, void *) override; + virtual bool HookProxyArrayGamerules(IExtension *, SendProp *, PropType, int, CallBackType, void *) override; + virtual bool HookProxyArrayGamerules(IExtension *, const char *, PropType, int, CallBackType, void *) override; + virtual bool UnhookProxyArrayGamerules(IExtension *, SendProp *, int, CallBackType, void *) override; + virtual bool UnhookProxyArrayGamerules(IExtension *, const char *, int, CallBackType, void *) override; + virtual bool AddUnhookListenerArray(IExtension *, SendProp *, CBaseEntity *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool AddUnhookListenerArray(IExtension *, const char *, CBaseEntity *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool AddUnhookListenerArrayGamerules(IExtension *, SendProp *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool AddUnhookListenerArrayGamerules(IExtension *, const char *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListenerArray(IExtension *, SendProp *, CBaseEntity *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListenerArray(IExtension *, const char *, CBaseEntity *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListenerArrayGamerules(IExtension *, SendProp *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + virtual bool RemoveUnhookListenerArrayGamerules(IExtension *, const char *, int, CallBackType, void *, ISendProxyUnhookListener *) override; + //checkers + virtual bool IsProxyHooked(IExtension *, SendProp *, CBaseEntity *) const override; + virtual bool IsProxyHooked(IExtension *, const char *, CBaseEntity *) const override; + virtual bool IsProxyHookedGamerules(IExtension *, SendProp *) const override; + virtual bool IsProxyHookedGamerules(IExtension *, const char *) const override; + virtual bool IsProxyHookedArray(IExtension *, SendProp *, CBaseEntity *, int) const override; + virtual bool IsProxyHookedArray(IExtension *, const char *, CBaseEntity *, int) const override; + virtual bool IsProxyHookedArrayGamerules(IExtension *, SendProp *, int) const override; + virtual bool IsProxyHookedArrayGamerules(IExtension *, const char *, int) const override; + + /* + TODO: + //For the change hooks + virtual bool HookChange(IExtension *, SendProp *, CBaseEntity *, PropType, CallBackType, void *) override; + virtual bool HookChange(IExtension *, const char *, CBaseEntity *, PropType, CallBackType, void *) override; + virtual bool HookChangeGamerules(IExtension *, SendProp *, PropType, CallBackType, void *) override; + virtual bool HookChangeGamerules(IExtension *, const char *, PropType, CallBackType, void *) override; + virtual bool UnhookChange(IExtension *, SendProp *, CBaseEntity *, CallBackType, void *) override; + virtual bool UnhookChange(IExtension *, const char *, CBaseEntity *, CallBackType, void *) override; + virtual bool UnhookChangeGamerules(IExtension *, SendProp *, CallBackType, void *) override; + virtual bool UnhookChangeGamerules(IExtension *, const char *, CallBackType, void *) override; + //same for the arrays =| + virtual bool HookChangeArray(IExtension *, SendProp *, CBaseEntity *, PropType, int, CallBackType, void *) override; + virtual bool HookChangeArray(IExtension *, const char *, CBaseEntity *, PropType, int, CallBackType, void *) override; + virtual bool UnhookChangeArray(IExtension *, SendProp *, CBaseEntity *, int, CallBackType, void *) override; + virtual bool UnhookChangeArray(IExtension *, const char *, CBaseEntity *, int, CallBackType, void *) override; + virtual bool HookChangeArrayGamerules(IExtension *, SendProp *, PropType, int, CallBackType, void *) override; + virtual bool HookChangeArrayGamerules(IExtension *, const char *, PropType, int, CallBackType, void *) override; + virtual bool UnhookChangeArrayGamerules(IExtension *, SendProp *, int, CallBackType, void *) override; + virtual bool UnhookChangeArrayGamerules(IExtension *, const char *, int, CallBackType, void *) override; + //checkers + virtual bool IsChangeHooked(IExtension *, SendProp *, CBaseEntity *) override; + virtual bool IsChangeHooked(IExtension *, const char *, CBaseEntity *) override; + virtual bool IsChangeHookedGamerules(IExtension *, SendProp *) override; + virtual bool IsChangeHookedGamerules(IExtension *, const char *) override; + virtual bool IsChangeHookedArray(IExtension *, SendProp *, CBaseEntity *, int) override; + virtual bool IsChangeHookedArray(IExtension *, const char *, CBaseEntity *, int) override; + virtual bool IsChangeHookedArrayGamerules(IExtension *, SendProp *, int) override; + virtual bool IsChangeHookedArrayGamerules(IExtension *, const char *, int) override; + //More TODO: Add unhook listeners for change hooks + */ +}; + +#endif diff --git a/natives.cpp b/natives.cpp new file mode 100644 index 0000000..40f0486 --- /dev/null +++ b/natives.cpp @@ -0,0 +1,1041 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "natives.h" + +static cell_t Native_UnhookPropChange(IPluginContext * pContext, const cell_t * params) +{ + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * name; + edict_t * pEnt = gamehelpers->EdictOfIndex(entity); + pContext->LocalToString(params[2], &name); + ServerClass * sc = pEnt->GetNetworkable()->GetServerClass(); + + if (!sc) + return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity); + + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), name, &info); + IPluginFunction * callback = pContext->GetFunctionById(params[3]); + + auto it = g_ChangeHooks.begin(); + while(it != g_ChangeHooks.end()) + { + auto &hook = *it; + + if (hook.objectID == entity && + hook.pVar == info.prop) + { + CallBackInfo sInfo; + sInfo.pCallback = callback; + sInfo.pOwner = (void *)pContext; + sInfo.iCallbackType = CallBackType::Callback_PluginFunction; + if(g_SendProxyManager.UnhookChange(hook, sInfo)) { + g_ChangeHooks.erase(it); + } + break; + } + } + return 1; +} + +static cell_t Native_UnhookPropChangeGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * name; + pContext->LocalToString(params[1], &name); + IPluginFunction * callback = pContext->GetFunctionById(params[2]); + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, name, &info); + + auto it = g_ChangeHooksGamerules.begin(); + while(it != g_ChangeHooksGamerules.end()) + { + auto &hook = *it; + + if (hook.pVar == info.prop) + { + CallBackInfo sInfo; + sInfo.pCallback = callback; + sInfo.pOwner = (void *)pContext; + sInfo.iCallbackType = CallBackType::Callback_PluginFunction; + if(g_SendProxyManager.UnhookChangeGamerules(hook, sInfo)) { + g_ChangeHooksGamerules.erase(it); + } + break; + } + } + return 1; +} + +static cell_t Native_HookPropChange(IPluginContext * pContext, const cell_t * params) +{ + bool bSafeCheck = params[0] >= 4; + + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * name; + edict_t * pEnt = gamehelpers->EdictOfIndex(entity); + pContext->LocalToString(params[2], &name); + ServerClass * sc = pEnt->GetNetworkable()->GetServerClass(); + + if (!sc) + return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity); + + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), name, &info); + SendProp * pProp = info.prop; + + if (!pProp) + return pContext->ThrowNativeError("Could not find prop %s", name); + + IPluginFunction * callback = nullptr; + PropType propType = PropType::Prop_Max; + if (bSafeCheck) + { + propType = static_cast(params[3]); + callback = pContext->GetFunctionById(params[4]); + } + else + callback = pContext->GetFunctionById(params[3]); + + if (bSafeCheck && !IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + PropChangeHook hook; + hook.objectID = entity; + hook.Offset = info.actual_offset; + hook.pVar = pProp; + CallBackInfo sCallInfo; + sCallInfo.iCallbackType = CallBackType::Callback_PluginFunction; + sCallInfo.pCallback = (void *)callback; + sCallInfo.pOwner = (void *)pContext; + if (!g_SendProxyManager.AddChangeHookToList(std::move(hook), std::move(sCallInfo))) + return pContext->ThrowNativeError("Entity %d isn't valid", entity); + return 1; +} + +static cell_t Native_HookPropChangeGameRules(IPluginContext * pContext, const cell_t * params) +{ + bool bSafeCheck = params[0] >= 3; + char * name; + pContext->LocalToString(params[1], &name); + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, name, &info); + SendProp * pProp = info.prop; + + if (!pProp) + return pContext->ThrowNativeError("Could not find prop %s", name); + + IPluginFunction * callback = nullptr; + PropType propType = PropType::Prop_Max; + if (bSafeCheck) + { + propType = static_cast(params[2]); + callback = pContext->GetFunctionById(params[3]); + } + else + callback = pContext->GetFunctionById(params[2]); + + if (bSafeCheck && !IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + if (!g_pGameRules) + { + g_pGameRules = g_pSDKTools->GetGameRules(); + if (!g_pGameRules) + { + return pContext->ThrowNativeError("CRITICAL ERROR: Could not get gamerules pointer!"); + } + } + + PropChangeHookGamerules hook; + hook.Offset = info.actual_offset; + hook.pVar = pProp; + CallBackInfo sCallInfo; + sCallInfo.iCallbackType = CallBackType::Callback_PluginFunction; + sCallInfo.pCallback = (void *)callback; + sCallInfo.pOwner = (void *)pContext; + if (!g_SendProxyManager.AddChangeHookToListGamerules(std::move(hook), std::move(sCallInfo))) + return pContext->ThrowNativeError("Prop type %d isn't valid", pProp->GetType()); //should never happen + return 1; +} + +static cell_t Native_Hook(IPluginContext * pContext, const cell_t * params) +{ + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * name; + pContext->LocalToString(params[2], &name); + edict_t * pEnt = gamehelpers->EdictOfIndex(entity); + ServerClass * sc = pEnt->GetNetworkable()->GetServerClass(); + + if (!sc) + return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity); + + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), name, &info); + SendProp * pProp = info.prop; + + if (!pProp) + return pContext->ThrowNativeError("Could not find prop %s", name); + + PropType propType = static_cast(params[3]); + if (!IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + SendPropHook hook; + hook.objectID = entity; + hook.sCallbackInfo.pCallback = (void *)pContext->GetFunctionById(params[4]); + hook.sCallbackInfo.iCallbackType = CallBackType::Callback_PluginFunction; + hook.sCallbackInfo.pOwner = (void *)pContext; + hook.pEnt = pEnt; + hook.per_client = params[5]; + bool bHookedAlready = false; + for (auto &it : g_Hooks) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + hook.propType = propType; + hook.pVar = pProp; + + //if this prop has been hooked already, don't set the proxy again + if (bHookedAlready) + { + if (g_SendProxyManager.AddHookToList(std::move(hook))) + return 1; + return 0; + } + if (g_SendProxyManager.AddHookToList(std::move(hook))) + { + pProp->SetProxyFn(GlobalProxy); + return 1; + } + return 0; +} + +static cell_t Native_HookGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * name; + pContext->LocalToString(params[1], &name); + sm_sendprop_info_t info; + + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, name, &info); + SendProp * pProp = info.prop; + + if (!pProp) + return pContext->ThrowNativeError("Could not find prop %s", name); + + PropType propType = static_cast(params[2]); + if (!IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + SendPropHookGamerules hook; + hook.sCallbackInfo.pCallback = (void *)pContext->GetFunctionById(params[3]); + hook.sCallbackInfo.iCallbackType = CallBackType::Callback_PluginFunction; + hook.sCallbackInfo.pOwner = (void *)pContext; + bool bHookedAlready = false; + for (auto &it : g_HooksGamerules) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + hook.propType = propType; + hook.pVar = pProp; + hook.per_client = params[4]; + + //if this prop has been hooked already, don't set the proxy again + if (bHookedAlready) + { + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + return 1; + return 0; + } + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + { + pProp->SetProxyFn(GlobalProxyGamerules); + return 1; + } + return 0; +} + +static cell_t Native_HookArrayProp(IPluginContext * pContext, const cell_t * params) +{ + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * propName; + pContext->LocalToString(params[2], &propName); + + edict_t * pEnt = gamehelpers->EdictOfIndex(entity); + ServerClass * sc = pEnt->GetNetworkable()->GetServerClass(); + + if (!sc) + return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity); + + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), propName, &info); + + if (!info.prop) + return pContext->ThrowNativeError("Could not find prop %s", propName); + + SendTable * st = info.prop->GetDataTable(); + if (!st) + return pContext->ThrowNativeError("Prop %s does not contain any elements", propName); + + int element = params[3]; + SendProp * pProp = st->GetProp(element); + if (!pProp) + return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName()); + + PropType propType = static_cast(params[4]); + + if (!IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + SendPropHook hook; + hook.objectID = entity; + hook.sCallbackInfo.pCallback = (void *)pContext->GetFunctionById(params[5]);; + hook.sCallbackInfo.iCallbackType = CallBackType::Callback_PluginFunction; + hook.sCallbackInfo.pOwner = (void *)pContext; + hook.pEnt = pEnt; + hook.Element = element; + bool bHookedAlready = false; + for (auto &it : g_Hooks) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + hook.propType = propType; + hook.pVar = pProp; + hook.per_client = params[6]; + + if (bHookedAlready) + { + if (g_SendProxyManager.AddHookToList(std::move(hook))) + return 1; + return 0; + } + if (g_SendProxyManager.AddHookToList(std::move(hook))) + { + pProp->SetProxyFn(GlobalProxy); + return 1; + } + return 0; +} + +static cell_t Native_UnhookArrayProp(IPluginContext * pContext, const cell_t * params) +{ + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * propName; + pContext->LocalToString(params[2], &propName); + int element = params[3]; + PropType propType = static_cast(params[4]); + IPluginFunction * callback = pContext->GetFunctionById(params[5]); + auto it = g_Hooks.begin(); + while(it != g_Hooks.end()) + { + auto &hook = *it; + + //we check callback here, so, we do not need to check owner + if (hook.Element == element && + hook.sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && + hook.propType == propType && + hook.sCallbackInfo.pCallback == (void *)callback && + !strcmp(hook.pVar->GetName(), propName) && + hook.objectID == entity) + { + g_SendProxyManager.UnhookProxy(hook); + g_Hooks.erase(it); + return 1; + } + + ++it; + } + return 0; +} + +static cell_t Native_Unhook(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[2], &propName); + IPluginFunction * pFunction = pContext->GetFunctionById(params[3]); + auto it = g_Hooks.begin(); + while(it != g_Hooks.end()) + { + auto &hook = *it; + + //we check callback here, so, we do not need to check owner + if (params[1] == hook.objectID && + hook.sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && + strcmp(hook.pVar->GetName(), propName) == 0 && + (void *)pFunction == hook.sCallbackInfo.pCallback) + { + g_SendProxyManager.UnhookProxy(hook); + g_Hooks.erase(it); + return 1; + } + + ++it; + } + return 0; +} + +static cell_t Native_UnhookGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + IPluginFunction * pFunction = pContext->GetFunctionById(params[2]); + auto it = g_HooksGamerules.begin(); + while(it != g_HooksGamerules.end()) + { + auto &hook = *it; + + //we check callback here, so, we do not need to check owner + if (hook.sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && + strcmp(hook.pVar->GetName(), propName) == 0 && + (void *)pFunction == hook.sCallbackInfo.pCallback) + { + g_SendProxyManager.UnhookProxyGamerules(hook); + g_HooksGamerules.erase(it); + return 1; + } + + ++it; + } + return 0; +} + +static cell_t Native_IsHooked(IPluginContext * pContext, const cell_t * params) +{ + int objectID = params[1]; + char * propName; + pContext->LocalToString(params[2], &propName); + + for (auto &it : g_Hooks) + { + if (it.objectID == objectID && + it.sCallbackInfo.pOwner == (void *)pContext && + strcmp(propName, it.pVar->GetName()) == 0) { + return 1; + } + } + return 0; +} + +static cell_t Native_IsHookedGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + + for (auto &it : g_HooksGamerules) + { + if (it.sCallbackInfo.pOwner == (void *)pContext && + strcmp(propName, it.pVar->GetName()) == 0) { + return 1; + } + } + return 0; +} + +static cell_t Native_HookArrayPropGamerules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + sm_sendprop_info_t info; + + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, propName, &info); + + if (!info.prop) + return pContext->ThrowNativeError("Could not find prop %s", propName); + + SendTable * st = info.prop->GetDataTable(); + + if (!st) + return pContext->ThrowNativeError("Prop %s does not contain any elements", propName); + + int element = params[2]; + SendProp * pProp = st->GetProp(element); + + if (!pProp) + return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName()); + + PropType propType = static_cast(params[3]); + + if (!IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + SendPropHookGamerules hook; + hook.sCallbackInfo.pCallback = (void *)pContext->GetFunctionById(params[4]); + hook.sCallbackInfo.iCallbackType = CallBackType::Callback_PluginFunction; + hook.sCallbackInfo.pOwner = (void *)pContext; + hook.Element = element; + bool bHookedAlready = false; + for (auto &it : g_HooksGamerules) + { + if (it.pVar == pProp) + { + hook.pRealProxy = it.pRealProxy; + bHookedAlready = true; + break; + } + } + if (!bHookedAlready) + hook.pRealProxy = pProp->GetProxyFn(); + hook.propType = propType; + hook.pVar = pProp; + + if (bHookedAlready) + { + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + return 1; + return 0; + } + if (g_SendProxyManager.AddHookToListGamerules(std::move(hook))) + { + pProp->SetProxyFn(GlobalProxy); + return 1; + } + return 0; +} + +static cell_t Native_UnhookArrayPropGamerules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + int iElement = params[2]; + PropType iPropType = static_cast(params[3]); + IPluginFunction * pFunction = pContext->GetFunctionById(params[4]); + auto it = g_HooksGamerules.begin(); + while(it != g_HooksGamerules.end()) + { + auto &hook = *it; + + if (hook.Element == iElement && + hook.sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && + hook.propType == iPropType && + hook.sCallbackInfo.pCallback == (void *)pFunction && + !strcmp(hook.pVar->GetName(), propName)) + { + g_SendProxyManager.UnhookProxyGamerules(hook); + g_HooksGamerules.erase(it); + return 1; + } + + ++it; + } + return 0; +} + +static cell_t Native_IsHookedArray(IPluginContext * pContext, const cell_t * params) +{ + int objectID = params[1]; + char * propName; + pContext->LocalToString(params[2], &propName); + int iElement = params[3]; + + for (auto &it : g_Hooks) + { + if (it.sCallbackInfo.pOwner == (void *)pContext && + it.objectID == objectID && + it.Element == iElement && + strcmp(propName, it.pVar->GetName()) == 0) { + return 1; + } + } + return 0; +} + +static cell_t Native_IsHookedArrayGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + int iElement = params[2]; + + for (auto &it : g_HooksGamerules) + { + if (it.sCallbackInfo.pOwner == (void *)pContext && + it.Element == iElement && + strcmp(propName, it.pVar->GetName()) == 0) { + return 1; + } + } + return 0; +} + +static cell_t Native_HookPropChangeArray(IPluginContext * pContext, const cell_t * params) +{ + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * name; + edict_t * pEnt = gamehelpers->EdictOfIndex(entity); + pContext->LocalToString(params[2], &name); + ServerClass * sc = pEnt->GetNetworkable()->GetServerClass(); + + if (!sc) + return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity); + + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), name, &info); + + if (!info.prop) + return pContext->ThrowNativeError("Could not find prop %s", name); + + SendTable * st = info.prop->GetDataTable(); + + if (!st) + return pContext->ThrowNativeError("Prop %s does not contain any elements", name); + + int element = params[3]; + SendProp * pProp = st->GetProp(element); + + if (!pProp) + return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName()); + + PropType propType = static_cast(params[4]); + + if (!IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + PropChangeHook hook; + hook.objectID = entity; + hook.Offset = info.actual_offset + pProp->GetOffset(); + hook.pVar = pProp; + CallBackInfo sCallInfo; + sCallInfo.iCallbackType = CallBackType::Callback_PluginFunction; + sCallInfo.pCallback = (void *)pContext->GetFunctionById(params[5]); + sCallInfo.pOwner = (void *)pContext; + if (!g_SendProxyManager.AddChangeHookToList(std::move(hook), std::move(sCallInfo))) + return pContext->ThrowNativeError("Entity %d isn't valid", entity); + return 1; +} + +static cell_t Native_UnhookPropChangeArray(IPluginContext * pContext, const cell_t * params) +{ + if (params[1] < 0 || params[1] >= g_iEdictCount) + return pContext->ThrowNativeError("Invalid Edict Index %d", params[1]); + + int entity = params[1]; + char * name; + edict_t * pEnt = gamehelpers->EdictOfIndex(entity); + pContext->LocalToString(params[2], &name); + ServerClass * sc = pEnt->GetNetworkable()->GetServerClass(); + + if (!sc) + return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity); + + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(sc->GetName(), name, &info); + SendTable * st = info.prop->GetDataTable(); + + if (!st) + return pContext->ThrowNativeError("Prop %s does not contain any elements", name); + + int element = params[3]; + SendProp * pProp = st->GetProp(element); + + if (!pProp) + return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName()); + + IPluginFunction * callback = pContext->GetFunctionById(params[4]); + + auto it = g_ChangeHooks.begin(); + while(it != g_ChangeHooks.end()) + { + auto &hook = *it; + + if (hook.objectID == entity && + hook.pVar == info.prop) + { + CallBackInfo sInfo; + sInfo.pCallback = callback; + sInfo.pOwner = (void *)pContext; + sInfo.iCallbackType = CallBackType::Callback_PluginFunction; + if(g_SendProxyManager.UnhookChange(hook, sInfo)) { + g_ChangeHooks.erase(it); + } + break; + } + + ++it; + } + return 1; +} + +static cell_t Native_HookPropChangeArrayGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * name; + pContext->LocalToString(params[1], &name); + sm_sendprop_info_t info; + + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, name, &info); + + if (!info.prop) + return pContext->ThrowNativeError("Could not find prop %s", name); + + SendTable * st = info.prop->GetDataTable(); + + if (!st) + return pContext->ThrowNativeError("Prop %s does not contain any elements", name); + + + int element = params[2]; + SendProp * pProp = st->GetProp(element); + + if (!pProp) + return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName()); + + PropType propType = static_cast(params[3]); + if (!IsPropValid(pProp, propType)) { + switch (propType) + { + case PropType::Prop_Int: + return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName()); + case PropType::Prop_Float: + return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName()); + case PropType::Prop_String: + return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName()); + case PropType::Prop_Vector: + return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName()); + default: + return pContext->ThrowNativeError("Unsupported prop type %d", propType); + } + } + + PropChangeHookGamerules hook; + hook.Offset = info.actual_offset + pProp->GetOffset(); + hook.pVar = pProp; + CallBackInfo sCallInfo; + sCallInfo.iCallbackType = CallBackType::Callback_PluginFunction; + sCallInfo.pCallback = (void *)pContext->GetFunctionById(params[4]); + sCallInfo.pOwner = (void *)pContext; + if (!g_SendProxyManager.AddChangeHookToListGamerules(std::move(hook), std::move(sCallInfo))) + return pContext->ThrowNativeError("Prop type %d isn't valid", pProp->GetType()); //should never happen + return 1; +} + +static cell_t Native_UnhookPropChangeArrayGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * name; + pContext->LocalToString(params[1], &name); + sm_sendprop_info_t info; + gamehelpers->FindSendPropInfo(g_szGameRulesProxy, name, &info); + + if (!info.prop) + return pContext->ThrowNativeError("Could not find prop %s", name); + + SendTable * st = info.prop->GetDataTable(); + + if (!st) + return pContext->ThrowNativeError("Prop %s does not contain any elements", name); + + int element = params[2]; + SendProp * pProp = st->GetProp(element); + + if (!pProp) + return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName()); + + IPluginFunction * callback = pContext->GetFunctionById(params[3]); + + auto it = g_ChangeHooksGamerules.begin(); + while(it != g_ChangeHooksGamerules.end()) + { + auto &hook = *it; + + if (hook.pVar == info.prop) + { + CallBackInfo sInfo; + sInfo.pCallback = callback; + sInfo.pOwner = (void *)pContext; + sInfo.iCallbackType = CallBackType::Callback_PluginFunction; + if(g_SendProxyManager.UnhookChangeGamerules(hook, sInfo)) { + g_ChangeHooksGamerules.erase(it); + } + break; + } + + ++it; + } + return 1; +} + +static cell_t Native_IsPropChangeHooked(IPluginContext * pContext, const cell_t * params) +{ + int objectID = params[1]; + char * propName; + pContext->LocalToString(params[2], &propName); + + for (auto &it : g_ChangeHooks) + { + if (it.objectID == objectID && + strcmp(propName, it.pVar->GetName()) == 0) + { + auto &pCallbacks = it.vCallbacksInfo; + for (auto &it2 : pCallbacks) { + if (it2.iCallbackType == CallBackType::Callback_PluginFunction && + it2.pOwner == (void *)pContext) + { + return 1; + } + } + break; + } + } + return 0; +} + +static cell_t Native_IsPropChangeHookedGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + + for (auto &it : g_ChangeHooksGamerules) + { + if (strcmp(propName, it.pVar->GetName()) == 0) + { + auto &pCallbacks = it.vCallbacksInfo; + for (auto &it2 : pCallbacks) { + if (it2.iCallbackType == CallBackType::Callback_PluginFunction && + it2.pOwner == (void *)pContext) + { + return 1; + } + } + break; + } + } + return 0; +} + +static cell_t Native_IsPropChangeArrayHooked(IPluginContext * pContext, const cell_t * params) +{ + int objectID = params[1]; + char * propName; + pContext->LocalToString(params[2], &propName); + int element = params[3]; + + for (auto &it : g_ChangeHooks) + { + if (it.Element == element && + it.objectID == objectID && + strcmp(propName, it.pVar->GetName()) == 0) + { + auto &pCallbacks = it.vCallbacksInfo; + for (auto &it2 : pCallbacks) { + if (it2.iCallbackType == CallBackType::Callback_PluginFunction && + it2.pOwner == (void *)pContext) + { + return 1; + } + } + break; + } + } + return 0; +} + +static cell_t Native_IsPropChangeArrayHookedGameRules(IPluginContext * pContext, const cell_t * params) +{ + char * propName; + pContext->LocalToString(params[1], &propName); + int element = params[2]; + + for (auto &it : g_ChangeHooksGamerules) + { + if (it.Element == element && + strcmp(propName, it.pVar->GetName()) == 0) + { + auto &pCallbacks = it.vCallbacksInfo; + for (auto &it2 : pCallbacks) { + if (it2.iCallbackType == CallBackType::Callback_PluginFunction && + it2.pOwner == (void *)pContext) + { + return 1; + } + } + break; + } + } + return 0; +} + +const sp_nativeinfo_t g_MyNatives[] = { + {"SendProxy_Hook", Native_Hook}, + {"SendProxy_HookGameRules", Native_HookGameRules}, + {"SendProxy_HookArrayProp", Native_HookArrayProp}, + {"SendProxy_UnhookArrayProp", Native_UnhookArrayProp}, + {"SendProxy_Unhook", Native_Unhook}, + {"SendProxy_UnhookGameRules", Native_UnhookGameRules}, + {"SendProxy_IsHooked", Native_IsHooked}, + {"SendProxy_IsHookedGameRules", Native_IsHookedGameRules}, + {"SendProxy_HookPropChange", Native_HookPropChange}, + {"SendProxy_HookPropChangeGameRules", Native_HookPropChangeGameRules}, + {"SendProxy_UnhookPropChange", Native_UnhookPropChange}, + {"SendProxy_UnhookPropChangeGameRules", Native_UnhookPropChangeGameRules}, + {"SendProxy_HookArrayPropGamerules", Native_HookArrayPropGamerules}, + {"SendProxy_UnhookArrayPropGamerules", Native_UnhookArrayPropGamerules}, + {"SendProxy_IsHookedArrayProp", Native_IsHookedArray}, + {"SendProxy_IsHookedArrayPropGamerules", Native_IsHookedArrayGameRules}, + {"SendProxy_HookPropChangeArray", Native_HookPropChangeArray}, + {"SendProxy_UnhookPropChangeArray", Native_UnhookPropChangeArray}, + {"SendProxy_HookPropChangeArrayGameRules", Native_HookPropChangeArrayGameRules}, + {"SendProxy_UnhookPropChangeArrayGameRules", Native_UnhookPropChangeArrayGameRules}, + {"SendProxy_IsPropChangeHooked", Native_IsPropChangeHooked}, + {"SendProxy_IsPropChangeHookedGameRules", Native_IsPropChangeHookedGameRules}, + {"SendProxy_IsPropChangeArrayHooked", Native_IsPropChangeArrayHooked}, + {"SendProxy_IsPropChangeArrayHookedGameRules", Native_IsPropChangeArrayHookedGameRules}, + {"SendProxy_HookPropChangeSafe", Native_HookPropChange}, + {"SendProxy_HookPropChangeGameRulesSafe", Native_HookPropChangeGameRules}, + //Probably add listeners for plugins? + {NULL, NULL} +}; diff --git a/natives.h b/natives.h new file mode 100644 index 0000000..7eb3efc --- /dev/null +++ b/natives.h @@ -0,0 +1,38 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SendVar Proxy Manager + * Copyright (C) 2011-2019 Afronanny & AlliedModders community. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef SENDPROXY_NATIVES_INC +#define SENDPROXY_NATIVES_INC + +#include "extension.h" +extern const sp_nativeinfo_t g_MyNatives[]; + +#endif \ No newline at end of file diff --git a/sendproxy.vcxproj b/sendproxy.vcxproj new file mode 100644 index 0000000..e841372 --- /dev/null +++ b/sendproxy.vcxproj @@ -0,0 +1,220 @@ + + + + + Debug - CSGO + Win32 + + + Debug - TF2 + Win32 + + + Release - CSGO + Win32 + + + Release - TF2 + Win32 + + + + {B3E797CF-4E77-4C9D-B8A8-7589B6902206} + sdk + Win32Proj + 10.0.17763.0 + sendproxy + + + + DynamicLibrary + MultiByte + true + v141 + + + DynamicLibrary + MultiByte + v141 + + + DynamicLibrary + MultiByte + true + v141 + + + DynamicLibrary + MultiByte + v141 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + true + false + true + false + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + AllRules.ruleset + + + sendproxy.ext.2.csgo + sendproxy.ext.2.tf2 + sendproxy.ext.2.csgo + sendproxy.ext.2.tf2 + + + + Disabled + .;..;..\extensions;..\..;..\..\sourcepawn\include;..\..\..\hl2sdk-tf2\public;..\..\..\hl2sdk-tf2\public\engine;..\..\..\hl2sdk-tf2\public\game\server;..\..\..\hl2sdk-tf2\public\tier0;..\..\..\hl2sdk-tf2\public\tier1;..\..\..\mmsource-1.10\core;..\..\..\mmsource-1.10\core\sourcehook;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;COMPILER_MSVC;COMPILER_MSVC32;SOURCE_ENGINE=11;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + NotSet + false + + + Level3 + /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_HL2DM=7 /D SE_DODS=8 /D SE_SDK2013=9 /D SE_BMS=10 /D SE_TF2=11 /D SE_LEFT4DEAD=12 /D SE_NUCLEARDAWN=13 /D SE_CONTAGION=14 /D SE_LEFT4DEAD2=15 /D SE_ALIENSWARM=16 /D SE_PORTAL2=17 /D SE_BLADE=18 /D SE_INSURGENCY=19 /D SE_CSGO=20 /D SE_DOTA=21 + EditAndContinue + + + ..\..\..\hl2sdk-tf2\lib\public\mathlib.lib;..\..\..\hl2sdk-tf2\lib\public\tier0.lib;..\..\..\hl2sdk-tf2\lib\public\tier1.lib;..\..\..\hl2sdk-tf2\lib\public\vstdlib.lib;legacy_stdio_definitions.lib;%(AdditionalDependencies) + LIBC;LIBCD;LIBCMT;%(IgnoreSpecificDefaultLibraries) + true + Windows + false + + + MachineX86 + + + + + Speed + .;..;..\extensions;..\..;..\..\sourcepawn\include;..\..\..\hl2sdk-tf2\public;..\..\..\hl2sdk-tf2\public\engine;..\..\..\hl2sdk-tf2\public\game\server;..\..\..\hl2sdk-tf2\public\tier0;..\..\..\hl2sdk-tf2\public\tier1;..\..\..\mmsource-1.10\core;..\..\..\mmsource-1.10\core\sourcehook;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;COMPILER_MSVC;COMPILER_MSVC32;SOURCE_ENGINE=11;%(PreprocessorDefinitions) + MultiThreaded + NotSet + false + + + Level3 + ProgramDatabase + /MP /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_HL2DM=7 /D SE_DODS=8 /D SE_SDK2013=9 /D SE_BMS=10 /D SE_TF2=11 /D SE_LEFT4DEAD=12 /D SE_NUCLEARDAWN=13 /D SE_CONTAGION=14 /D SE_LEFT4DEAD2=15 /D SE_ALIENSWARM=16 /D SE_PORTAL2=17 /D SE_BLADE=18 /D SE_INSURGENCY=19 /D SE_CSGO=20 /D SE_DOTA=21 + + + ..\..\..\hl2sdk-tf2\lib\public\mathlib.lib;..\..\..\hl2sdk-tf2\lib\public\tier0.lib;..\..\..\hl2sdk-tf2\lib\public\tier1.lib;..\..\..\hl2sdk-tf2\lib\public\vstdlib.lib;legacy_stdio_definitions.lib;%(AdditionalDependencies) + LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries) + true + Windows + true + true + false + + + MachineX86 + + + + + /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_HL2DM=7 /D SE_DODS=8 /D SE_SDK2013=9 /D SE_BMS=10 /D SE_TF2=11 /D SE_LEFT4DEAD=12 /D SE_NUCLEARDAWN=13 /D SE_CONTAGION=14 /D SE_LEFT4DEAD2=15 /D SE_ALIENSWARM=16 /D SE_PORTAL2=17 /D SE_BLADE=18 /D SE_INSURGENCY=19 /D SE_CSGO=20 /D SE_DOTA=21 + Disabled + .;..;..\extensions;..\..;..\..\sourcepawn\include;..\..\..\hl2sdk-csgo\public;..\..\..\hl2sdk-csgo\public\engine;..\..\..\hl2sdk-csgo\public\game\server;..\..\..\hl2sdk-csgo\public\tier0;..\..\..\hl2sdk-csgo\public\tier1;..\..\..\mmsource-1.10\core;..\..\..\mmsource-1.10\core\sourcehook;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;COMPILER_MSVC;COMPILER_MSVC32;SOURCE_ENGINE=20;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + NotSet + false + + + Level3 + EditAndContinue + + + ..\..\..\hl2sdk-csgo\lib\public\interfaces.lib;..\..\..\hl2sdk-csgo\lib\public\mathlib.lib;..\..\..\hl2sdk-csgo\lib\public\tier0.lib;..\..\..\hl2sdk-csgo\lib\public\tier1.lib;..\..\..\hl2sdk-csgo\lib\public\vstdlib.lib;legacy_stdio_definitions.lib;%(AdditionalDependencies) + LIBC;LIBCD;LIBCMT;%(IgnoreSpecificDefaultLibraries) + true + Windows + false + + + MachineX86 + + + + + /MP /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_HL2DM=7 /D SE_DODS=8 /D SE_SDK2013=9 /D SE_BMS=10 /D SE_TF2=11 /D SE_LEFT4DEAD=12 /D SE_NUCLEARDAWN=13 /D SE_CONTAGION=14 /D SE_LEFT4DEAD2=15 /D SE_ALIENSWARM=16 /D SE_PORTAL2=17 /D SE_BLADE=18 /D SE_INSURGENCY=19 /D SE_CSGO=20 /D SE_DOTA=21 + Speed + .;..;..\extensions;..\..;..\..\sourcepawn\include;..\..\..\hl2sdk-csgo\public;..\..\..\hl2sdk-csgo\public\engine;..\..\..\hl2sdk-csgo\public\game\server;..\..\..\hl2sdk-csgo\public\tier0;..\..\..\hl2sdk-csgo\public\tier1;..\..\..\mmsource-1.10\core;..\..\..\mmsource-1.10\core\sourcehook;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;COMPILER_MSVC;COMPILER_MSVC32;SOURCE_ENGINE=20;%(PreprocessorDefinitions) + MultiThreaded + NotSet + false + + + Level3 + ProgramDatabase + + + ..\..\..\hl2sdk-csgo\lib\public\interfaces.lib;..\..\..\hl2sdk-csgo\lib\public\mathlib.lib;..\..\..\hl2sdk-csgo\lib\public\tier0.lib;..\..\..\hl2sdk-csgo\lib\public\tier1.lib;..\..\..\hl2sdk-csgo\lib\public\vstdlib.lib;legacy_stdio_definitions.lib;%(AdditionalDependencies) + LIBC;LIBCD;LIBCMTD;%(IgnoreSpecificDefaultLibraries) + true + Windows + true + true + false + + + MachineX86 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sendproxy.vcxproj.filters b/sendproxy.vcxproj.filters new file mode 100644 index 0000000..905ef55 --- /dev/null +++ b/sendproxy.vcxproj.filters @@ -0,0 +1,78 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {31958233-BB2D-4e41-A8F9-CE8A4684F436} + + + {b460818f-b4e8-41d5-aa25-39105c3f190b} + + + {2d16564b-f983-4a72-af62-259632ae0cfb} + + + {0b59b3f8-f0f5-4c20-859e-9f122aeebf93} + + + + + Source Files + + + SourceMod SDK + + + Source Files + + + Source Files\detours + + + Source Files\detours + + + Source Files + + + + + Header Files + + + SourceMod SDK + + + SourceMod SDK + + + Header Files + + + Header Files\iface + + + Header Files\detours + + + Header Files\detours + + + Header Files\detours + + + Header Files + + + \ No newline at end of file diff --git a/smsdk_config.h b/smsdk_config.h new file mode 100644 index 0000000..59d8dc3 --- /dev/null +++ b/smsdk_config.h @@ -0,0 +1,82 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Sample Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * 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, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ + +/** + * @file smsdk_config.h + * @brief Contains macros for configuring basic extension information. + */ + +/* Basic information exposed publicly */ +#define SMEXT_CONF_NAME "SendProxy Manager" +#define SMEXT_CONF_DESCRIPTION "Change stuff without actually changing stuff!" +#define SMEXT_CONF_VERSION "1.3.4" +#define SMEXT_CONF_AUTHOR "afronanny & AlliedModders community" +#define SMEXT_CONF_URL "https://forums.alliedmods.net/showthread.php?t=169795" +#define SMEXT_CONF_LOGTAG "SENDPROXY" +#define SMEXT_CONF_LICENSE "GPL" +#define SMEXT_CONF_DATESTRING __DATE__ + +/** + * @brief Exposes plugin's main interface. + */ +#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name; + +/** + * @brief Sets whether or not this plugin required Metamod. + * NOTE: Uncomment to enable, comment to disable. + */ +#define SMEXT_CONF_METAMOD + +/** Enable interfaces you want to use here by uncommenting lines */ +#define SMEXT_ENABLE_FORWARDSYS +//#define SMEXT_ENABLE_HANDLESYS +#define SMEXT_ENABLE_PLAYERHELPERS +//#define SMEXT_ENABLE_DBMANAGER +#define SMEXT_ENABLE_GAMECONF +//#define SMEXT_ENABLE_MEMUTILS +#define SMEXT_ENABLE_GAMEHELPERS +//#define SMEXT_ENABLE_TIMERSYS +//#define SMEXT_ENABLE_THREADER +//#define SMEXT_ENABLE_LIBSYS +//#define SMEXT_ENABLE_MENUS +//#define SMEXT_ENABLE_ADTFACTORY +#define SMEXT_ENABLE_PLUGINSYS +//#define SMEXT_ENABLE_ADMINSYS +//#define SMEXT_ENABLE_TEXTPARSERS +//#define SMEXT_ENABLE_USERMSGS +//#define SMEXT_ENABLE_TRANSLATOR +//#define SMEXT_ENABLE_NINVOKE +#define SMEXT_ENABLE_ROOTCONSOLEMENU + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ diff --git a/sourcemod/gamedata/sendproxy.txt b/sourcemod/gamedata/sendproxy.txt new file mode 100644 index 0000000..25b7298 --- /dev/null +++ b/sourcemod/gamedata/sendproxy.txt @@ -0,0 +1,97 @@ +"Games" +{ + "#default" + { + "#supported" + { + "game" "tf" + "game" "left4dead2" + "game" "cstrike" + } + + "Signatures" + { + "CGameClient::ShouldSendMessages" + { + "library" "engine" + "linux" "@_ZN11CGameClient18ShouldSendMessagesEv" + } + "CGameServer::SendClientMessages" + { + "library" "engine" + "linux" "@_ZN11CGameServer18SendClientMessagesEb" + } + "SV_ComputeClientPacks" + { + "library" "engine" + "linux" "@_Z21SV_ComputeClientPacksiPP11CGameClientP14CFrameSnapshot" + } + } + } + "tf" + { + "Signatures" + { + "CGameClient::ShouldSendMessages" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x80\xBE\x94\x00\x00\x00\x00" + } + "CGameServer::SendClientMessages" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x81\xEC\x30\x04\x00\x00\x53\x56\x57\x33\xDB" + } + "SV_ComputeClientPacks" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x83\xEC\x38\x8B\x0D\x2A\x2A\x2A\x2A\x53\x33\xDB" + } + } + } + "cstrike" + { + "Signatures" + { + "CGameClient::ShouldSendMessages" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x80\xBE\x94\x00\x00\x00\x00" + } + "CGameServer::SendClientMessages" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x81\xEC\x30\x04\x00\x00\x53\x56\x57\x33\xDB" + } + "SV_ComputeClientPacks" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x83\xEC\x38\x8B\x0D\x2A\x2A\x2A\x2A\x53\x33\xDB" + } + } + } + "csgo" + { + "Signatures" + { + "CGameClient::ShouldSendMessages" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x51\x57\x8B\xF9\x80\xBF\xEC\x01\x00\x00\x00" + "linux" "\x55\x89\xE5\x83\xEC\x28\x89\x5D\xF8\x8B\x5D\x08\x89\x75\xFC\x80\xBB\xD8\x01\x00\x00\x00" + } + "CGameServer::SendClientMessages" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\xFC\x07\x00\x00" + "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x1C\x08\x00\x00" + } + "SV_ComputeClientPacks" + { + "library" "engine" + "windows" "\x55\x8B\xEC\x83\xEC\x10\x53\x8B\xD9\x89\x55\xFC" + "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x3C\x8B\x0D\x2A\x2A\x2A\x2A\x8B\x75\x0C" + } + } + } +} diff --git a/sourcemod/scripting/include/sendproxy.inc b/sourcemod/scripting/include/sendproxy.inc new file mode 100644 index 0000000..9e13f67 --- /dev/null +++ b/sourcemod/scripting/include/sendproxy.inc @@ -0,0 +1,140 @@ +#if !defined _SENDPROXYMANAGER_INC_ +#define _SENDPROXYMANAGER_INC_ + +#define SENDPROXY_LIB "sendproxy14" + +enum SendPropType { + Prop_Int, + Prop_Float, + Prop_String, + Prop_Vector = 4, + Prop_Max +}; + +typeset SendProxyCallback +{ + function Action (const int iEntity, const char[] cPropName, int &iValue, const int iElement, const int iClient); //Prop_Int + function Action (const int iEntity, const char[] cPropName, float &flValue, const int iElement, const int iClient); //Prop_Float + function Action (const int iEntity, const char[] cPropName, char cModifiedValue[4096], const int iElement, const int iClient); //Prop_String + function Action (const int iEntity, const char[] cPropName, float vecValues[3], const int iElement, const int iClient); //Prop_Vector + + function Action (const int iEntity, const char[] cPropName, int &iValue, const int iElement); //Prop_Int + function Action (const int iEntity, const char[] cPropName, float &flValue, const int iElement); //Prop_Float + function Action (const int iEntity, const char[] cPropName, char cModifiedValue[4096], const int iElement); //Prop_String + function Action (const int iEntity, const char[] cPropName, float vecValues[3], const int iElement); //Prop_Vector +}; + +typeset SendProxyCallbackGamerules +{ + function Action (const char[] cPropName, int &iValue, const int iElement, const int iClient); //Prop_Int + function Action (const char[] cPropName, float &flValue, const int iElement, const int iClient); //Prop_Float + function Action (const char[] cPropName, char cModifiedValue[4096], const int iElement, const int iClient); //Prop_String + function Action (const char[] cPropName, float vecValues[3], const int iElement, const int iClient); //Prop_Vector + + function Action (const char[] cPropName, int &iValue, const int iElement); //Prop_Int + function Action (const char[] cPropName, float &flValue, const int iElement); //Prop_Float + function Action (const char[] cPropName, char cModifiedValue[4096], const int iElement); //Prop_String + function Action (const char[] cPropName, float vecValues[3], const int iElement); //Prop_Vector +}; + +typeset PropChangedCallback +{ + function void(const int iEntity, const char[] cPropName, const int iOldValue, const int iNewValue, const int iElement); //Prop_Int + function void(const int iEntity, const char[] cPropName, const float flOldValue, const float flNewValue, const int iElement); //Prop_Int + function void(const int iEntity, const char[] cPropName, const char[] cOldValue, const char[] cNewValue, const int iElement); //Prop_String + function void(const int iEntity, const char[] cPropName, const float vecOldValue[3], const float vecNewValue[3], const int iElement); //Prop_Vector +}; + +typeset GameRulesPropChangedCallback +{ + function void(const char[] cPropName, const int iOldValue, const int iNewValue, const int iElement); //Prop_Int + function void(const char[] cPropName, const float flOldValue, const float flNewValue, const int iElement); //Prop_Int + function void(const char[] cPropName, const char[] cOldValue, const char[] cNewValue, const int iElement); //Prop_String + function void(const char[] cPropName, const float vecOldValue[3], const float vecNewValue[3], const int iElement); //Prop_Vector +}; + +//Returns true upon success, false upon failure +native bool SendProxy_Hook(const int iEntity, const char[] cPropName, const SendPropType stType, const SendProxyCallback pCallback, bool per_client = false); +native bool SendProxy_HookGameRules(const char[] cPropName, const SendPropType stType, const SendProxyCallbackGamerules pCallback, bool per_client = false); +native bool SendProxy_HookArrayProp(const int iEntity, const char[] cPropName, const int iElement, const SendPropType stType, const SendProxyCallback pCallback, bool per_client = false); +native bool SendProxy_UnhookArrayProp(const int iEntity, const char[] cPropName, const int iElement, const SendPropType stType, const SendProxyCallback pCallback); +native bool SendProxy_Unhook(const int iEntity, const char[] cPropName, const SendProxyCallback pCallback); +native bool SendProxy_UnhookGameRules(const char[] cPropName, const SendProxyCallbackGamerules pCallback); +native bool SendProxy_IsHooked(const int iEntity, const char[] cPropName); +native bool SendProxy_IsHookedGameRules(const char[] cPropName); +native bool SendProxy_HookArrayPropGamerules(const char[] cPropName, const int iElement, const SendPropType stType, const SendProxyCallbackGamerules pCallback); +native bool SendProxy_UnhookArrayPropGamerules(const char[] cPropName, const int iElement, const SendPropType stType, const SendProxyCallbackGamerules pCallback); +native bool SendProxy_IsHookedArrayProp(const int iEntity, const char[] cPropName, const int iElement); +native bool SendProxy_IsHookedArrayPropGamerules(const char[] cPropName, const int iElement); + +//Deprecated functions +//here SendPropType is autodetected, this may be unsafe now +#pragma deprecated Use SendProxy_HookPropChangeSafe instead. +native bool SendProxy_HookPropChange(const int iEntity, const char[] cPropName, const PropChangedCallback pCallback); +#pragma deprecated Use SendProxy_HookPropChangeGameRulesSafe instead. +native bool SendProxy_HookPropChangeGameRules(const char[] cPropName, const GameRulesPropChangedCallback pCallback); + +native bool SendProxy_HookPropChangeSafe(const int iEntity, const char[] cPropName, const SendPropType stType, const PropChangedCallback pCallback); +native bool SendProxy_HookPropChangeGameRulesSafe(const char[] cPropName, const SendPropType stType, const GameRulesPropChangedCallback pCallback); +native bool SendProxy_HookPropChangeArray(const int iEntity, const char[] cPropName, const int iElement, const SendPropType stType, const PropChangedCallback pCallback); +native bool SendProxy_HookPropChangeArrayGameRules(const char[] cPropName, const int iElement, const SendPropType stType, const PropChangedCallback pCallback); +native bool SendProxy_IsPropChangeHooked(const int iEntity, const char[] cPropName); +native bool SendProxy_IsPropChangeHookedGameRules(const char[] cPropName); +native bool SendProxy_IsPropChangeArrayHooked(const int iEntity, const char[] cPropName, const int iElement); +native bool SendProxy_IsPropChangeArrayHookedGameRules(const char[] cPropName, const int iElement); +//these functions returns always true and because they are "void", so, we don't care about value they return because it always same +native void SendProxy_UnhookPropChangeArray(const int iEntity, const char[] cPropName, const int iElement, const PropChangedCallback pCallback); +native void SendProxy_UnhookPropChangeArrayGameRules(const char[] cPropName, const int iElement, const PropChangedCallback pCallback); + +native void SendProxy_UnhookPropChange(const int iEntity, const char[] cPropName, const PropChangedCallback pCallback); +native void SendProxy_UnhookPropChangeGameRules(const char[] cPropName, const GameRulesPropChangedCallback pCallback); + +#if !defined REQUIRE_EXTENSIONS +public __ext_sendproxymanager_SetNTVOptional() +{ + MarkNativeAsOptional("SendProxy_Hook"); + MarkNativeAsOptional("SendProxy_HookGameRules"); + MarkNativeAsOptional("SendProxy_HookArrayProp"); + MarkNativeAsOptional("SendProxy_UnhookArrayProp"); + MarkNativeAsOptional("SendProxy_Unhook"); + MarkNativeAsOptional("SendProxy_UnhookGameRules"); + MarkNativeAsOptional("SendProxy_IsHooked"); + MarkNativeAsOptional("SendProxy_IsHookedGameRules"); + MarkNativeAsOptional("SendProxy_HookPropChange"); + MarkNativeAsOptional("SendProxy_HookPropChangeGameRules"); + MarkNativeAsOptional("SendProxy_UnhookPropChange"); + MarkNativeAsOptional("SendProxy_UnhookPropChangeGameRules"); + MarkNativeAsOptional("SendProxy_HookArrayPropGamerules"); + MarkNativeAsOptional("SendProxy_UnhookArrayPropGamerules"); + MarkNativeAsOptional("SendProxy_IsHookedArrayProp"); + MarkNativeAsOptional("SendProxy_IsHookedArrayPropGamerules"); + MarkNativeAsOptional("SendProxy_HookPropChangeArray"); + MarkNativeAsOptional("SendProxy_UnhookPropChangeArray"); + MarkNativeAsOptional("SendProxy_HookPropChangeArrayGameRules"); + MarkNativeAsOptional("SendProxy_UnhookPropChangeArrayGameRules"); + MarkNativeAsOptional("SendProxy_IsPropChangeHooked"); + MarkNativeAsOptional("SendProxy_IsPropChangeHookedGameRules"); + MarkNativeAsOptional("SendProxy_IsPropChangeArrayHooked"); + MarkNativeAsOptional("SendProxy_IsPropChangeArrayHookedGameRules"); + MarkNativeAsOptional("SendProxy_HookPropChangeSafe"); + MarkNativeAsOptional("SendProxy_HookPropChangeGameRulesSafe"); +} +#endif + +public Extension __ext_sendproxymanager = +{ + name = "SendProxy Manager", + file = "sendproxy.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#endif