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