diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2f5daf5..7d70157 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,24 +13,42 @@ jobs:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-latest, windows-latest]
+ sm_version: [sm1.11, sm1.12, smlatest]
include:
- os: windows-latest
os_short: win
compiler_cc: msvc
+ dbgopt:
- os: ubuntu-latest
os_short: linux
compiler_cc: clang
compiler_cxx: clang++
+ dbgopt: --enable-debug
- os: ubuntu-22.04
os_short: oldlinux
compiler_cc: clang-14
compiler_cxx: clang++-14
+ dbgopt: --enable-debug
+
+ - sm_version: sm1.11
+ sm_branch: 1.11-dev
+ mm_branch: 1.10-dev
+ hl2sdk_manifest_prepare: true
+ - sm_version: sm1.12
+ sm_branch: 1.12-dev
+ mm_branch: 1.12-dev
+ hl2sdk_manifest_prepare: false
+ - sm_version: smlatest
+ sm_branch: master
+ mm_branch: 1.12-dev
+ hl2sdk_manifest_prepare: false
+
fail-fast: false
- name: Build Project ${{ matrix.os_short }}
+ name: Build ${{ matrix.os_short }}-${{ matrix.sm_version }}
runs-on: ${{ matrix.os }}
env:
- SDKS: '["l4d","l4d2"]'
+ SDKS: '["l4d2"]'
steps:
- name: Setup Environment
@@ -72,27 +90,42 @@ jobs:
)
- uses: actions/setup-python@v5
- name: Setup Python 3.8
+ name: Setup Python 3.12
with:
- python-version: 3.8
+ python-version: 3.12
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
- - name: Checkout Repository
- uses: actions/checkout@v4
- with:
- path: src
-
- name: Prepare Alliedmodders Directory
shell: bash
run: |
mkdir alliedmodders
+ - name: Install AMBuild
+ working-directory: alliedmodders
+ run: |
+ git clone https://github.com/alliedmodders/ambuild
+ pip install ./ambuild
+
- name: Prepare Sourcemod
working-directory: alliedmodders
run: |
- git clone --recursive https://github.com/alliedmodders/sourcemod -b 1.11-dev
+ git clone --recursive https://github.com/alliedmodders/sourcemod sourcemod -b ${{ matrix.sm_branch }}
+
+ - name: Setup HL2SDK Manifest Environment
+ if: ${{ !matrix.hl2sdk_manifest_prepare }}
+ shell: bash
+ run: |
+ echo "HL2SDKMANIFEST=${{ github.workspace }}/alliedmodders/sourcemod/hl2sdk-manifests" >> $GITHUB_ENV
+
+ - name: Prepare HL2SDK Manifest
+ working-directory: alliedmodders
+ if: matrix.hl2sdk_manifest_prepare
+ shell: bash
+ run: |
+ git clone https://github.com/alliedmodders/hl2sdk-manifests hl2sdk-manifests
+ echo "HL2SDKMANIFEST=${{ github.workspace }}/alliedmodders/hl2sdk-manifests" >> $GITHUB_ENV
- name: Prepare L4D SDK
working-directory: alliedmodders
@@ -104,36 +137,30 @@ jobs:
- name: Prepare Metamod Source
working-directory: alliedmodders
run: |
- git clone https://github.com/alliedmodders/metamod-source mmsource-1.10 -b 1.10-dev
-
- - name: Install AMBuild
- working-directory: alliedmodders
- run: |
- git clone https://github.com/alliedmodders/ambuild
- pip install ./ambuild
+ git clone https://github.com/alliedmodders/metamod-source metamod-source -b ${{ matrix.mm_branch }}
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+ with:
+ path: src
+
- name: Run AMBuild
working-directory: src
shell: bash
run: |
- cd extension
mkdir build
cd build
python ../configure.py \
--hl2sdk-root="${{ github.workspace }}/alliedmodders" \
--sm-path="${{ github.workspace }}/alliedmodders/sourcemod" \
- --mms-path="${{ github.workspace }}/alliedmodders/mmsource-1.10" \
- --sdks=${{ join(fromJSON(env.SDKS)) }}
+ --mms-path="${{ github.workspace }}/alliedmodders/metamod-source" \
+ --sdks=${{ join(fromJSON(env.SDKS)) }} \
+ --enable-optimize \
+ ${{ matrix.dbgopt }}
ambuild
- - name: Copy to addons directory
- working-directory: src
- shell: bash
- run: |
- cp -r extension/build/package/addons/sourcemod/extensions addons/sourcemod
-
- name: Upload Binary (Package)
uses: actions/upload-artifact@v4
with:
- name: sendproxy-${{ matrix.os_short }}
- path: src
\ No newline at end of file
+ name: sendproxy-${{ matrix.os_short }}-${{ matrix.sm_version }}-${{ env.GITHUB_SHA_SHORT }}
+ path: src/build/package
diff --git a/.gitignore b/.gitignore
index 25e7109..3e85166 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,4 +33,6 @@
.vscode/*.*
-extension/build/*
\ No newline at end of file
+extension/build/*
+build/*
+safetyhook/*
\ No newline at end of file
diff --git a/AMBuildScript b/AMBuildScript
new file mode 100644
index 0000000..922ffef
--- /dev/null
+++ b/AMBuildScript
@@ -0,0 +1,445 @@
+# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
+import os, shutil
+
+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))
+
+def SetArchFlags(compiler):
+ if compiler.behavior == 'gcc':
+ if compiler.target.arch == 'x86_64':
+ compiler.cflags += ['-fPIC']
+ elif compiler.like('msvc'):
+ if compiler.target.arch == 'x86_64':
+ compiler.defines += ['WIN64']
+
+hl2sdk_manifests_path = None
+
+# First we check if the manifest exists in the current path
+if builder.options.hl2sdk_manifest:
+ hl2sdk_manifests_path = os.path.join(builder.options.hl2sdk_manifest, 'SdkHelpers.ambuild')
+else:
+ hl2sdk_manifests_path = ResolveEnvPath('HL2SDKMANIFEST', 'hl2sdk-manifests') or os.path.join(builder.sourcePath, 'hl2sdk-manifests')
+ hl2sdk_manifests_path = os.path.join(hl2sdk_manifests_path, 'SdkHelpers.ambuild')
+
+if not os.path.exists(hl2sdk_manifests_path):
+ raise Exception('Could not find SdkHelpers.ambuild in the given HL2SDK Manifest path!')
+
+SdkHelpers = builder.Eval(hl2sdk_manifests_path, {
+ 'Project': 'sm-extension'
+})
+
+class ExtensionConfig(object):
+ def __init__(self):
+ self.sdk_manifests = []
+ self.sdks = {}
+ self.sdk_targets = []
+ self.extensions = []
+ self.generated_headers = None
+ self.mms_root = None
+ self.sm_root = None
+ self.all_targets = []
+ self.target_archs = set()
+
+ if builder.options.targets:
+ target_archs = builder.options.targets.split(',')
+ else:
+ target_archs = ['x86', 'x86_64']
+
+ for arch in target_archs:
+ try:
+ cxx = builder.DetectCxx(target_arch = arch)
+ self.target_archs.add(cxx.target.arch)
+ except Exception as e:
+ # Error if archs were manually overridden.
+ if builder.options.targets:
+ raise
+ print('Skipping target {}: {}'.format(arch, e))
+ continue
+ self.all_targets.append(cxx)
+
+ if not self.all_targets:
+ raise Exception('No suitable C/C++ compiler was found.')
+
+ def use_auto_versioning(self):
+ if builder.backend != 'amb2':
+ return False
+ return not getattr(builder.options, 'disable_auto_versioning', False)
+
+ @property
+ def tag(self):
+ if builder.options.debug == '1':
+ return 'Debug'
+ return 'Release'
+
+ def findSdkPath(self, sdk_name):
+ dir_name = 'hl2sdk-{}'.format(sdk_name)
+ if builder.options.hl2sdk_root:
+ sdk_path = os.path.join(builder.options.hl2sdk_root, dir_name)
+ if os.path.exists(sdk_path):
+ return sdk_path
+ return ResolveEnvPath('HL2SDK{}'.format(sdk_name.upper()), dir_name)
+
+ def shouldIncludeSdk(self, sdk):
+ return not sdk.get('source2', False)
+
+ def detectSDKs(self):
+ sdk_list = [s for s in builder.options.sdks.split(',') if s]
+ SdkHelpers.sdk_filter = self.shouldIncludeSdk
+ SdkHelpers.find_sdk_path = self.findSdkPath
+ SdkHelpers.findSdks(builder, self.all_targets, sdk_list)
+
+ self.sdks = SdkHelpers.sdks
+ self.sdk_manifests = SdkHelpers.sdk_manifests
+ self.sdk_targets = SdkHelpers.sdk_targets
+
+ if builder.options.mms_path:
+ self.mms_root = builder.options.mms_path
+ else:
+ self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12')
+ if not self.mms_root:
+ self.mms_root = ResolveEnvPath('MMSOURCE', 'metamod-source')
+ if not self.mms_root:
+ self.mms_root = ResolveEnvPath('MMSOURCE_DEV', '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)
+
+ if builder.options.sm_path:
+ self.sm_root = builder.options.sm_path
+ else:
+ self.sm_root = ResolveEnvPath('SOURCEMOD112', 'sourcemod-1.12')
+ if not self.sm_root:
+ self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod')
+ if not self.sm_root:
+ self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', '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)
+
+ def configure(self):
+
+ allowed_archs = ['x86','x86_64']
+
+ if not set(self.target_archs).issubset(allowed_archs):
+ raise Exception('Unknown target architecture: {0}'.format(self.target_archs))
+
+ for cxx in self.all_targets:
+ self.configure_cxx(cxx)
+
+ def configure_cxx(self, cxx):
+ if cxx.family == 'msvc':
+ if cxx.version < 1914 and builder.options.generator != 'vs':
+ raise Exception(f'Only MSVC 2017 15.7 and later are supported, full C++17 support is required. ({str(cxx.version)} < 1914)')
+ elif cxx.family == 'gcc':
+ if cxx.version < 'gcc-8':
+ raise Exception('Only GCC versions 8 or later are supported, full C++17 support is required.')
+ elif cxx.family == 'clang':
+ if cxx.version < 'clang-5':
+ raise Exception('Only clang versions 5 or later are supported, full C++17 support is required.')
+
+ if cxx.like('gcc'):
+ self.configure_gcc(cxx)
+ elif cxx.family == 'msvc':
+ self.configure_msvc(cxx)
+
+ # Optimization
+ if builder.options.opt == '1':
+ cxx.defines += ['NDEBUG']
+
+ # Debugging
+ if builder.options.debug == '1':
+ cxx.defines += ['DEBUG', '_DEBUG']
+
+ # Platform-specifics
+ if cxx.target.platform == 'linux':
+ self.configure_linux(cxx)
+ elif cxx.target.platform == 'mac':
+ self.configure_mac(cxx)
+ elif cxx.target.platform == 'windows':
+ self.configure_windows(cxx)
+
+ def configure_gcc(self, cxx):
+ 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',
+ '-fvisibility=hidden',
+ ]
+
+ if cxx.target.arch in ['x86', 'x86_64']:
+ cxx.cflags += ['-msse']
+
+ cxx.cxxflags += [
+ '-fno-threadsafe-statics',
+ '-Wno-non-virtual-dtor',
+ '-Wno-overloaded-virtual',
+ '-Wno-register',
+ '-fvisibility-inlines-hidden',
+ '-std=c++17',
+ ]
+
+
+ have_gcc = cxx.family == 'gcc'
+ have_clang = cxx.family == 'clang'
+
+ # Work around errors from smsdk_ext.cpp
+ if have_clang:
+ cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch']
+
+ # Work around SDK warnings.
+ if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0':
+ cxx.cflags += [
+ '-Wno-implicit-int-float-conversion',
+ '-Wno-tautological-overlap-compare',
+ ]
+
+ if have_gcc:
+ cxx.cflags += ['-mfpmath=sse']
+ cxx.cflags += ['-Wno-maybe-uninitialized']
+
+ if builder.options.opt == '1':
+ cxx.cflags += ['-O3']
+
+ # Don't omit the frame pointer.
+ cxx.cflags += ['-fno-omit-frame-pointer']
+
+ def configure_msvc(self, cxx):
+
+ 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',
+ '/std:c++17',
+ ]
+ cxx.linkflags += [
+ 'kernel32.lib',
+ 'user32.lib',
+ 'gdi32.lib',
+ 'winspool.lib',
+ 'comdlg32.lib',
+ 'advapi32.lib',
+ 'shell32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'uuid.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+ ]
+
+ if builder.options.opt == '1':
+ cxx.cflags += ['/Ox', '/Zo']
+ cxx.linkflags += ['/OPT:ICF', '/OPT:REF']
+
+ if builder.options.debug == '1':
+ cxx.cflags += ['/Od', '/RTC1']
+
+ # This needs to be after our optimization flags which could otherwise disable it.
+ # Don't omit the frame pointer.
+ cxx.cflags += ['/Oy-']
+
+ def configure_linux(self, cxx):
+ cxx.defines += ['LINUX', '_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64']
+ cxx.linkflags += ['-lm']
+ if cxx.family == 'gcc':
+ cxx.linkflags += ['-static-libgcc']
+ elif cxx.family == 'clang':
+ cxx.linkflags += ['-lgcc_eh']
+ cxx.linkflags += ['-static-libstdc++']
+
+ def configure_mac(self, cxx):
+ cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL']
+ cxx.cflags += ['-mmacosx-version-min=10.15']
+ cxx.linkflags += [
+ '-mmacosx-version-min=10.15',
+ '-stdlib=libc++',
+ '-lc++',
+ ]
+ cxx.cxxflags += ['-stdlib=libc++']
+
+ def configure_windows(self, cxx):
+ cxx.defines += ['WIN32', '_WINDOWS']
+
+ def LibraryBuilder(self, compiler, name):
+ binary = compiler.Library(name)
+ self.AddVersioning(binary)
+ if binary.compiler.like('msvc'):
+ binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS']
+ self.AddCxxCompat(binary)
+ return binary
+
+ def Library(self, context, compiler, name):
+ compiler = compiler.clone()
+ SetArchFlags(compiler)
+ return self.LibraryBuilder(compiler, name)
+
+ 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'),
+ ]
+ return compiler
+
+ def ExtLibrary(self, context, compiler, name):
+ binary = self.Library(context, compiler, name)
+ SetArchFlags(compiler)
+ self.ConfigureForExtension(context, binary.compiler)
+ return binary
+
+ def AddCxxCompat(self, binary):
+ if binary.compiler.target.platform == 'linux' and os.path.exists( os.path.join(self.sm_root, 'public', 'amtl', 'compat', 'stdcxx.cpp') ):
+ binary.sources += [
+ os.path.join(self.sm_root, 'public', 'amtl', 'compat', 'stdcxx.cpp'),
+ ]
+
+ def AddCDetour(self, binary):
+ sm_public_path = os.path.join(self.sm_root, 'public')
+ binary.sources += [ os.path.join(sm_public_path, 'CDetour', 'detours.cpp') ]
+
+ if SafetyHook.libsafetyhook is not None:
+ # sm1.12+
+ binary.compiler.cxxincludes += [ os.path.join(sm_public_path, 'safetyhook', 'include') ]
+
+ for task in SafetyHook.libsafetyhook:
+ if task.target.arch == binary.compiler.target.arch:
+ binary.compiler.linkflags += [task.binary]
+ return
+ raise Exception('No suitable build of safetyhook was found.')
+ elif os.path.exists( os.path.join(sm_public_path, 'asm') ) and os.path.exists( os.path.join(sm_public_path, 'libudis86') ):
+ # sm1.10+
+ binary.compiler.includes += [ sm_public_path ]
+ binary.sources += [ os.path.join(sm_public_path, 'asm', 'asm.c') ]
+ libudis_folder = os.path.join(sm_public_path, 'libudis86')
+ if os.path.isdir(libudis_folder):
+ binary.compiler.defines += ['HAVE_STRING_H']
+ binary.sources += [
+ os.path.join(libudis_folder, 'decode.c'),
+ os.path.join(libudis_folder, 'itab.c'),
+ os.path.join(libudis_folder, 'syn-att.c'),
+ os.path.join(libudis_folder, 'syn-intel.c'),
+ os.path.join(libudis_folder, 'syn.c'),
+ os.path.join(libudis_folder, 'udis86.c'),
+ ]
+ else:
+ raise Exception('Failed to add CDetour.')
+
+ def ConfigureForHL2(self, context, binary, sdk):
+ compiler = binary.compiler
+ SetArchFlags(compiler)
+
+ compiler.cxxincludes += [
+ os.path.join(self.mms_root, 'core'),
+ os.path.join(self.mms_root, 'core', 'sourcehook'),
+ ]
+
+ for other_sdk in self.sdk_manifests:
+ compiler.defines += ['SE_{}={}'.format(other_sdk['define'], other_sdk['code'])]
+
+ SdkHelpers.configureCxx(context, binary, sdk)
+
+ return binary
+
+ def HL2Library(self, context, compiler, name, sdk):
+ binary = self.Library(context, compiler, name)
+ self.ConfigureForExtension(context, binary.compiler)
+ return self.ConfigureForHL2(context, binary, sdk)
+
+ def HL2Config(self, project, context, compiler, name, sdk):
+ binary = project.Configure(compiler, name,
+ '{0} - {1} {2}'.format(self.tag, sdk['name'], compiler.target.arch))
+ self.AddCxxCompat(binary)
+ return self.ConfigureForHL2(context, binary, sdk)
+
+ def HL2ExtConfig(self, project, context, compiler, name, sdk):
+ binary = project.Configure(compiler, name,
+ '{0} - {1} {2}'.format(self.tag, sdk['name'], compiler.target.arch))
+ self.AddCxxCompat(binary)
+ self.ConfigureForHL2(context, binary, sdk)
+ self.ConfigureForExtension(context, binary.compiler)
+ return binary
+
+Extension = ExtensionConfig()
+Extension.detectSDKs()
+Extension.configure()
+
+class SafetyHookShim(object):
+ def __init__(self):
+ self.all_targets = {}
+ self.libsafetyhook = None
+
+SafetyHook = SafetyHookShim()
+SafetyHookPath = os.path.join(Extension.sm_root, 'public', 'safetyhook')
+
+if os.path.exists(SafetyHookPath):
+ if os.path.exists( os.path.join('safetyhook', 'AMBuilder') ):
+ os.unlink(os.path.join('safetyhook', 'AMBuilder'))
+ shutil.copytree(SafetyHookPath, os.path.join(builder.sourcePath, 'safetyhook'), dirs_exist_ok = True)
+
+ SafetyHook.all_targets = Extension.all_targets
+ SafetyHook.libsafetyhook = {}
+ builder.Build( 'safetyhook/AMBuilder', {'SafetyHook': SafetyHook } )
+
+# This will clone the list and each cxx object as we recurse, preventing child
+# scripts from messing up global state.
+builder.targets = builder.CloneableList(Extension.all_targets)
+
+BuildScripts = [
+ 'extension/AMBuilder',
+ 'PackageScript',
+]
+
+builder.Build(BuildScripts, { 'Extension': Extension })
diff --git a/extension/PackageScript b/PackageScript
similarity index 83%
rename from extension/PackageScript
rename to PackageScript
index 6fdf9d5..29c0140 100644
--- a/extension/PackageScript
+++ b/PackageScript
@@ -1,44 +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/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)
-
-# GameData files
-#CopyFiles('gamedata', 'addons/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:
+# 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)
+
+# GameData files
+CopyFiles('gamedata', 'addons/sourcemod/gamedata',
+ [ 'sendproxy.txt',
+ ]
+)
+CopyFiles('scripting', 'addons/sourcemod/scripting/include',
+ [ 'include/sendproxy.inc',
+ ]
+)
+
+# 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'])
\ No newline at end of file
diff --git a/addons/sourcemod/extensions/sendproxy.ext.2.csgo.dll b/addons/sourcemod/extensions/sendproxy.ext.2.csgo.dll
deleted file mode 100644
index 7b98a3c..0000000
Binary files a/addons/sourcemod/extensions/sendproxy.ext.2.csgo.dll and /dev/null differ
diff --git a/addons/sourcemod/extensions/sendproxy.ext.2.csgo.so b/addons/sourcemod/extensions/sendproxy.ext.2.csgo.so
deleted file mode 100644
index 742425b..0000000
Binary files a/addons/sourcemod/extensions/sendproxy.ext.2.csgo.so and /dev/null differ
diff --git a/addons/sourcemod/extensions/sendproxy.ext.2.tf2.dll b/addons/sourcemod/extensions/sendproxy.ext.2.tf2.dll
deleted file mode 100644
index f47d092..0000000
Binary files a/addons/sourcemod/extensions/sendproxy.ext.2.tf2.dll and /dev/null differ
diff --git a/addons/sourcemod/extensions/sendproxy.ext.2.tf2.so b/addons/sourcemod/extensions/sendproxy.ext.2.tf2.so
deleted file mode 100644
index 2a154c9..0000000
Binary files a/addons/sourcemod/extensions/sendproxy.ext.2.tf2.so and /dev/null differ
diff --git a/addons/sourcemod/gamedata/sendproxy.txt b/addons/sourcemod/gamedata/sendproxy.txt
deleted file mode 100644
index 68e0c2d..0000000
--- a/addons/sourcemod/gamedata/sendproxy.txt
+++ /dev/null
@@ -1,102 +0,0 @@
-"Games"
-{
- "#default"
- {
- "#supported"
- {
- "game" "tf"
- "game" "left4dead2"
- }
-
- "Signatures"
- {
- "CGameClient::ShouldSendMessages"
- {
- "library" "engine"
- "linux" "@_ZN11CGameClient18ShouldSendMessagesEv"
- "windows" "\x55\x8B\xEC\x51\x56\x8B\xF1\x80\xBE\xBC\x00\x00\x00\x00"
- }
- "CGameServer::SendClientMessages"
- {
- "library" "engine"
- "linux" "@_ZN11CGameServer18SendClientMessagesEb"
- "windows" "\x55\x8B\xEC\x81\xEC\xB4\x00\x00\x00\xA1\x2A\x2A\x2A\x2A\x53"
- }
- "SV_ComputeClientPacks"
- {
- "library" "engine"
- "linux" "@_Z21SV_ComputeClientPacksiPP11CGameClientP14CFrameSnapshot"
- "windows" "\x55\x8B\xEC\x83\xEC\x44\xA1\x2A\x2A\x2A\x2A\x53"
- }
- "CFrameSnapshotManager::UsePreviouslySentPacket"
- {
- "library" "engine"
- "linux" "@_ZN21CFrameSnapshotManager23UsePreviouslySentPacketEP14CFrameSnapshotii"
- "windows" "\x55\x8B\xEC\x56\x8B\x75\x0C\x57\x8B\xBC\xB1\x9C\x00\x00\x00"
- }
- "CFrameSnapshotManager::GetPreviouslySentPacket"
- {
- "library" "engine"
- "linux" "@_ZN21CFrameSnapshotManager23GetPreviouslySentPacketEii"
- "windows" "\x55\x8B\xEC\x8B\x55\x08\x8B\x84\x91\x9C\x00\x00\x00"
- }
- "CFrameSnapshotManager::CreatePackedEntity"
- {
- "library" "engine"
- "linux" "@_ZN21CFrameSnapshotManager18CreatePackedEntityEP14CFrameSnapshoti"
- "windows" "\x55\x8B\xEC\x83\xEC\x0C\x53\x8B\xD9\x56"
- }
- "CFrameSnapshotManager::RemoveEntityReference"
- {
- "library" "engine"
- "linux" "@_ZN21CFrameSnapshotManager21RemoveEntityReferenceEi"
- "windows" "\x55\x8B\xEC\x51\x8B\x45\x08\x53\x8B\x18"
- }
- }
- }
- "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"
- }
- }
- }
- "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/addons/sourcemod/scripting/include/sendproxy.inc b/addons/sourcemod/scripting/include/sendproxy.inc
deleted file mode 100644
index 862d613..0000000
--- a/addons/sourcemod/scripting/include/sendproxy.inc
+++ /dev/null
@@ -1,127 +0,0 @@
-#if !defined _SENDPROXYMANAGER_INC_
-#define _SENDPROXYMANAGER_INC_
-
-#define SENDPROXY_LIB "sendproxy13"
-
-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
-};
-
-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
-};
-
-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);
-native bool SendProxy_HookGameRules(const char[] cPropName, const SendPropType stType, const SendProxyCallbackGamerules pCallback);
-native bool SendProxy_HookArrayProp(const int iEntity, const char[] cPropName, const int iElement, const SendPropType stType, const SendProxyCallback pCallback);
-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);
-
-#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
\ No newline at end of file
diff --git a/configure.py b/configure.py
new file mode 100644
index 0000000..88f1cad
--- /dev/null
+++ b/configure.py
@@ -0,0 +1,27 @@
+# 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.
+
+parser = run.BuildParser(sourcePath=sys.path[0], api='2.2')
+parser.options.add_argument('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
+ help='Root search folder for HL2SDKs')
+parser.options.add_argument('--hl2sdk-manifest-path', type=str, dest='hl2sdk_manifest', default=None,
+ help='Path to HL2SDK Manifests')
+parser.options.add_argument('--sm-path', type=str, dest='sm_path', default=None,
+ help='Path to SourceMod')
+parser.options.add_argument('--mms-path', type=str, dest='mms_path', default=None,
+ help='Path to Metamod:Source')
+
+parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug',
+ help='Enable debugging symbols')
+parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt',
+ help='Enable optimization')
+parser.options.add_argument('-s', '--sdks', default='present', dest='sdks',
+ help='Build against specified SDKs; valid args are "none", "all", "present",'
+ ' or comma-delimited list of engine names')
+parser.options.add_argument('--targets', type=str, dest='targets', default=None,
+ help="Override the target architecture (use commas to separate multiple targets).")
+parser.Configure()
+
diff --git a/extension/AMBuildScript b/extension/AMBuildScript
deleted file mode 100644
index 3b30c89..0000000
--- a/extension/AMBuildScript
+++ /dev/null
@@ -1,453 +0,0 @@
-# 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 # Actual path
-
-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', '21', 'CSGO', WinLinuxMac, 'csgo'),
- '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'),
- 'doi': SDK('HL2SDKDOI', '2.doi', '20', 'DOI', WinLinuxMac, 'doi'),
-}
-
-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'):
- self.configure_gcc(cxx)
- elif cxx.vendor == 'msvc':
- self.configure_msvc(cxx)
-
- # Optimization
- if builder.options.opt == '1':
- cxx.defines += ['NDEBUG']
-
- # Debugging
- if builder.options.debug == '1':
- cxx.defines += ['DEBUG', '_DEBUG']
-
- # Platform-specifics
- if builder.target_platform == 'linux':
- self.configure_linux(cxx)
- elif builder.target_platform == 'mac':
- self.configure_mac(cxx)
- elif builder.target_platform == 'windows':
- self.configure_windows(cxx)
-
- # Finish up.
- cxx.includes += [
- os.path.join(self.sm_root, 'public'),
- ]
-
- def configure_gcc(self, cxx):
- 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',
- '-fvisibility=hidden',
- '-Wno-implicit-int-float-conversion',
- ]
- cxx.cxxflags += [
- '-fno-exceptions',
- '-fno-threadsafe-statics',
- '-Wno-non-virtual-dtor',
- '-Wno-overloaded-virtual',
- '-fvisibility-inlines-hidden',
- ]
- cxx.linkflags += ['-m32']
-
- if cxx.version >= 'clang-5':
- cxx.cxxflags += ['-Wno-register','-std=c++17']
- else:
- cxx.cxxflags += ['-std=c++14']
-
- have_gcc = cxx.vendor == 'gcc'
- have_clang = cxx.vendor == 'clang'
- if cxx.version >= 'clang-3.6':
- cxx.cxxflags += ['-Wno-inconsistent-missing-override']
- if have_clang or (cxx.version >= 'gcc-4.6'):
- cxx.cflags += ['-Wno-narrowing']
- if have_clang or (cxx.version >= 'gcc-4.7'):
- cxx.cxxflags += ['-Wno-delete-non-virtual-dtor']
- if cxx.version >= 'gcc-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']
-
- if have_gcc:
- cxx.cflags += ['-mfpmath=sse']
-
- if builder.options.opt == '1':
- cxx.cflags += ['-O3']
-
- def configure_msvc(self, cxx):
- 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',
- ]
-
- if builder.options.opt == '1':
- cxx.cflags += ['/Ox', '/Zo']
- cxx.linkflags += ['/OPT:ICF', '/OPT:REF']
-
- if builder.options.debug == '1':
- cxx.cflags += ['/Od', '/RTC1']
-
- # This needs to be after our optimization flags which could otherwise disable it.
- # Don't omit the frame pointer.
- cxx.cflags += ['/Oy-']
-
- def configure_linux(self, cxx):
- cxx.defines += ['_LINUX', 'POSIX']
- cxx.linkflags += ['-Wl,--exclude-libs,ALL', '-lm']
- if cxx.vendor == 'gcc':
- cxx.linkflags += ['-static-libgcc']
- elif cxx.vendor == 'clang':
- cxx.linkflags += ['-lgcc_eh']
-
- def configure_mac(self, cxx):
- 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++']
-
- def configure_windows(self, cxx):
- cxx.defines += ['WIN32', '_WINDOWS']
-
- 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'),
- ]
- return compiler
-
- 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'):
- 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', 'doi', 'csgo']:
- compiler.defines += ['NETWORK_VARS_ENABLED']
-
- if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2']:
- 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', 'doi', 'csgo']:
- 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', 'insurgency', 'doi']:
- dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
- elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
- 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', 'doi', 'csgo']:
- 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 HL2Library(self, context, name, sdk):
- binary = context.compiler.Library(name)
- self.ConfigureForExtension(context, binary.compiler)
- return self.ConfigureForHL2(binary, sdk)
-
- 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/extension/AMBuilder b/extension/AMBuilder
index 59aa5cc..6d07110 100644
--- a/extension/AMBuilder
+++ b/extension/AMBuilder
@@ -1,22 +1,19 @@
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
-import os, sys
+import os
projectName = 'sendproxy'
# smsdk_ext.cpp will be automatically added later
sourceFiles = [
+ 'sdk/memoverride.cpp',
'extension.cpp',
- 'CDetour/detours.cpp',
- 'asm/asm.c',
'natives.cpp',
- 'interfaceimpl.cpp'
+ 'clientpacks_detours.cpp',
+ 'sendproxy_callback.cpp',
+ 'sendprop_hookmanager.cpp',
]
-###############
-# 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')
+project = builder.LibraryProject(projectName)
if os.path.isfile(os.path.join(builder.currentSourcePath, 'sdk', 'smsdk_ext.cpp')):
# Use the copy included in the project
@@ -29,7 +26,14 @@ project.sources += sourceFiles
for sdk_name in Extension.sdks:
sdk = Extension.sdks[sdk_name]
-
- binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk)
+ if sdk['name'] in ['mock']:
+ continue
+
+ for cxx in builder.targets:
+ if not cxx.target.arch in sdk['platforms'][cxx.target.platform]:
+ continue
+
+ binary = Extension.HL2ExtConfig(project, builder, cxx, projectName + '.ext.' + sdk['extension'], sdk)
+ Extension.AddCDetour(binary)
-Extension.extensions = builder.Add(project)
+Extension.extensions += builder.Add(project)
diff --git a/extension/CDetour/detourhelpers.h b/extension/CDetour/detourhelpers.h
deleted file mode 100644
index 85cda0e..0000000
--- a/extension/CDetour/detourhelpers.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/**
- * vim: set ts=4 :
- * =============================================================================
- * SourceMod
- * Copyright (C) 2004-2007 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: detourhelpers.h 248 2008-08-27 00:56:22Z pred $
- */
-
-#ifndef _INCLUDE_SOURCEMOD_DETOURHELPERS_H_
-#define _INCLUDE_SOURCEMOD_DETOURHELPERS_H_
-
-#if defined PLATFORM_POSIX
-#include
-#define PAGE_SIZE 4096
-#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
-#define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC
-#endif
-
-struct patch_t
-{
- patch_t()
- {
- patch[0] = 0;
- bytes = 0;
- }
- unsigned char patch[20];
- size_t bytes;
-};
-
-inline void ProtectMemory(void *addr, int length, int prot)
-{
-#if defined PLATFORM_POSIX
- void *addr2 = (void *)ALIGN(addr);
- mprotect(addr2, sysconf(_SC_PAGESIZE), prot);
-#elif defined PLATFORM_WINDOWS
- DWORD old_prot;
- VirtualProtect(addr, length, prot, &old_prot);
-#endif
-}
-
-inline void SetMemPatchable(void *address, size_t size)
-{
- ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE);
-}
-
-inline void DoGatePatch(unsigned char *target, void *callback)
-{
- SetMemPatchable(target, 20);
-
- target[0] = 0xFF; /* JMP */
- target[1] = 0x25; /* MEM32 */
- *(void **)(&target[2]) = callback;
-}
-
-inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore)
-{
- ProtectMemory(address, 20, PAGE_EXECUTE_READWRITE);
-
- unsigned char *addr = (unsigned char *)address + offset;
- if (restore)
- {
- for (size_t i=0; ibytes; i++)
- {
- restore->patch[i] = addr[i];
- }
- restore->bytes = patch->bytes;
- }
-
- for (size_t i=0; ibytes; i++)
- {
- addr[i] = patch->patch[i];
- }
-}
-
-#endif //_INCLUDE_SOURCEMOD_DETOURHELPERS_H_
diff --git a/extension/CDetour/detours.cpp b/extension/CDetour/detours.cpp
deleted file mode 100644
index 71aba44..0000000
--- a/extension/CDetour/detours.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/**
-* vim: set ts=4 :
-* =============================================================================
-* SourceMod
-* 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: detours.cpp 248 2008-08-27 00:56:22Z pred $
-*/
-
-#include "detours.h"
-#include
-
-ISourcePawnEngine *CDetourManager::spengine = NULL;
-IGameConfig *CDetourManager::gameconf = NULL;
-
-void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
-{
- CDetourManager::spengine = spengine;
- CDetourManager::gameconf = gameconf;
-}
-
-CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, const char *signame)
-{
- CDetour *detour = new CDetour(callbackfunction, trampoline, signame);
- if (detour)
- {
- if (!detour->Init(spengine, gameconf))
- {
- delete detour;
- return NULL;
- }
-
- return detour;
- }
-
- return NULL;
-}
-
-CDetour::CDetour(void *callbackfunction, void **trampoline, const char *signame)
-{
- enabled = false;
- detoured = false;
- detour_address = NULL;
- detour_trampoline = NULL;
- this->signame = signame;
- this->detour_callback = callbackfunction;
- spengine = NULL;
- gameconf = NULL;
- this->trampoline = trampoline;
-}
-
-bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
-{
- this->spengine = spengine;
- this->gameconf = gameconf;
-
- if (!CreateDetour())
- {
- enabled = false;
- return enabled;
- }
-
- enabled = true;
-
- return enabled;
-}
-
-void CDetour::Destroy()
-{
- DeleteDetour();
- delete this;
-}
-
-bool CDetour::IsEnabled()
-{
- return enabled;
-}
-
-bool CDetour::CreateDetour()
-{
- if (!gameconf->GetMemSig(signame, &detour_address))
- {
- g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame);
- return false;
- }
-
- if (!detour_address)
- {
- g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour to prevent crashes", signame);
- return false;
- }
-
- detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1);
-
- /* First, save restore bits */
- for (size_t i=0; iAllocatePageMemory(CodeSize);
- spengine->SetReadWrite(wr.outbase);
- wr.outptr = wr.outbase;
- detour_trampoline = wr.outbase;
- goto jit_rewind;
- }
-
- spengine->SetReadExecute(wr.outbase);
-
- *trampoline = detour_trampoline;
-
- return true;
-}
-
-void CDetour::DeleteDetour()
-{
- if (detoured)
- {
- DisableDetour();
- }
-
- if (detour_trampoline)
- {
- /* Free the allocated trampoline memory */
- spengine->FreePageMemory(detour_trampoline);
- detour_trampoline = NULL;
- }
-}
-
-void CDetour::EnableDetour()
-{
- if (!detoured)
- {
- DoGatePatch((unsigned char *)detour_address, &detour_callback);
- detoured = true;
- }
-}
-
-void CDetour::DisableDetour()
-{
- if (detoured)
- {
- /* Remove the patch */
- ApplyPatch(detour_address, 0, &detour_restore, NULL);
- detoured = false;
- }
-}
diff --git a/extension/CDetour/detours.h b/extension/CDetour/detours.h
deleted file mode 100644
index 2131dcf..0000000
--- a/extension/CDetour/detours.h
+++ /dev/null
@@ -1,435 +0,0 @@
-/**
-* vim: set ts=4 :
-* =============================================================================
-* SourceMod
-* 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: detours.h 257 2008-09-23 03:12:13Z pred $
-*/
-
-#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_
-#define _INCLUDE_SOURCEMOD_DETOURS_H_
-
-#include "../extension.h"
-#include
-#include
-#include "detourhelpers.h"
-
-/**
- * CDetours class for SourceMod Extensions by pRED*
- * detourhelpers.h entirely stolen from CSS:DM and were written by BAILOPAN (I assume).
- * asm.h/c from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -fPIC thunks correctly
- * Concept by Nephyrin Zey (http://www.doublezen.net/) and Windows Detour Library (http://research.microsoft.com/sn/detours/)
- * Member function pointer ideas by Don Clugston (http://www.codeproject.com/cpp/FastDelegate.asp)
- */
-
-#define DETOUR_MEMBER_CALL(name) (this->*name##_Actual)
-#define DETOUR_STATIC_CALL(name) (name##_Actual)
-
-#define DETOUR_DECL_STATIC0(name, ret) \
-ret (*name##_Actual)(void) = NULL; \
-ret name(void)
-
-#define DETOUR_DECL_STATIC1(name, ret, p1type, p1name) \
-ret (*name##_Actual)(p1type) = NULL; \
-ret name(p1type p1name)
-
-#define DETOUR_DECL_STATIC2(name, ret, p1type, p1name, p2type, p2name) \
- ret (*name##_Actual)(p1type, p2type) = NULL; \
- ret name(p1type p1name, p2type p2name)
-
-
-#define DETOUR_DECL_STATIC3(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \
- ret (*name##_Actual)(p1type, p2type, p3type) = NULL; \
- ret name(p1type p1name, p2type p2name, p3type p3name)
-
-#define DETOUR_DECL_STATIC4(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \
- ret (*name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name)
-
-#define DETOUR_DECL_STATIC5(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name) \
- ret (*name##_Actual)(p1type, p2type, p3type, p4type, p5type) = NULL; \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name)
-
-#define DETOUR_DECL_STATIC6(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name) \
- ret (*name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type) = NULL; \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name)
-
-#define DETOUR_DECL_STATIC7(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name) \
- ret (*name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type) = NULL; \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name)
-
-#define DETOUR_DECL_STATIC0_fastcall(name, ret) \
- ret (__fastcall *name##_Actual)(void) = NULL; \
- ret __fastcall name(void)
-
-#define DETOUR_DECL_STATIC1_fastcall(name, ret, p1type, p1name) \
- ret (__fastcall *name##_Actual)(p1type) = NULL; \
- ret __fastcall name(p1type p1name)
-
-#define DETOUR_DECL_STATIC2_fastcall(name, ret, p1type, p1name, p2type, p2name) \
- ret (__fastcall *name##_Actual)(p1type, p2type) = NULL; \
- ret __fastcall name(p1type p1name, p2type p2name)
-
-#define DETOUR_DECL_STATIC3_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \
- ret (__fastcall *name##_Actual)(p1type, p2type, p3type) = NULL; \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name)
-
-#define DETOUR_DECL_STATIC4_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \
- ret (__fastcall *name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name, p4type p4name)
-
-#define DETOUR_DECL_STATIC5_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name) \
- ret (__fastcall *name##_Actual)(p1type, p2type, p3type, p4type, p5type) = NULL; \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name)
-
-#define DETOUR_DECL_STATIC6_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name) \
- ret (__fastcall *name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type) = NULL; \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name)
-
-#define DETOUR_DECL_STATIC7_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name) \
- ret (__fastcall *name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type) = NULL; \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name)
-
-#define DETOUR_DECL_MEMBER0(name, ret) \
-class name##Class \
-{ \
-public: \
- ret name(); \
- static ret (name##Class::* name##_Actual)(void); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(void) = NULL; \
-ret name##Class::name()
-
-#define DETOUR_DECL_MEMBER1(name, ret, p1type, p1name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name); \
- static ret (name##Class::* name##_Actual)(p1type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type) = NULL; \
-ret name##Class::name(p1type p1name)
-
-#define DETOUR_DECL_MEMBER2(name, ret, p1type, p1name, p2type, p2name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name)
-
-#define DETOUR_DECL_MEMBER3(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name)
-
-#define DETOUR_DECL_MEMBER4(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name)
-
-#define DETOUR_DECL_MEMBER5(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name)
-
-#define DETOUR_DECL_MEMBER7(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name)
-
-#define DETOUR_DECL_MEMBER8(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name)
-
-#define DETOUR_DECL_MEMBER9(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name, p9type, p9name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name)
-
-#define DETOUR_DECL_MEMBER10(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name, p9type, p9name, p10type, p10name) \
-class name##Class \
-{ \
-public: \
- ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name, p10type p10name); \
- static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type, p10type); \
-}; \
-ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type, p9type, p10type) = NULL; \
-ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name, p9type p9name, p10type p10name)
-
-#define DETOUR_DECL_MEMBER0_fastcall(name, ret) \
-class name##Class \
-{ \
-public: \
- ret __fastcall name(); \
- static ret (__fastcall name##Class::* name##_Actual)(void); \
-}; \
-ret (__fastcall name##Class::* name##Class::name##_Actual)(void) = NULL; \
-ret __fastcall name##Class::name()
-
-#define DETOUR_DECL_MEMBER1_fastcall(name, ret, p1type, p1name) \
-class name##Class \
-{ \
-public: \
- ret __fastcall name(p1type p1name); \
- static ret (__fastcall name##Class::* name##_Actual)(p1type); \
-}; \
-ret (__fastcall name##Class::* name##Class::name##_Actual)(p1type) = NULL; \
-ret __fastcall name##Class::name(p1type p1name)
-
-#define DETOUR_DECL_MEMBER2_fastcall(name, ret, p1type, p1name, p2type, p2name) \
-class name##Class \
-{ \
-public: \
- ret __fastcall name(p1type p1name, p2type p2name); \
- static ret (__fastcall name##Class::* name##_Actual)(p1type, p2type); \
-}; \
-ret (__fastcall name##Class::* name##Class::name##_Actual)(p1type, p2type) = NULL; \
-ret __fastcall name##Class::name(p1type p1name, p2type p2name)
-
-#define DETOUR_DECL_MEMBER3_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \
-class name##Class \
-{ \
-public: \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name); \
- static ret (__fastcall name##Class::* name##_Actual)(p1type, p2type, p3type); \
-}; \
-ret (__fastcall name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type) = NULL; \
-ret __fastcall name##Class::name(p1type p1name, p2type p2name, p3type p3name)
-
-#define DETOUR_DECL_MEMBER4_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \
-class name##Class \
-{ \
-public: \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name, p4type p4name); \
- static ret (__fastcall name##Class::* name##_Actual)(p1type, p2type, p3type, p4type); \
-}; \
-ret (__fastcall name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \
-ret __fastcall name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name)
-
-#define DETOUR_DECL_MEMBER5_fastcall(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name) \
-class name##Class \
-{ \
-public: \
- ret __fastcall name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name); \
- static ret (__fastcall name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type); \
-}; \
-ret (__fastcall name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type) = NULL; \
-ret __fastcall name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name)
-
-#define GET_MEMBER_CALLBACK(name) (void *)GetCodeAddress(&name##Class::name)
-#define GET_MEMBER_TRAMPOLINE(name) (void **)(&name##Class::name##_Actual)
-
-#define GET_STATIC_CALLBACK(name) (void *)&name
-#define GET_STATIC_TRAMPOLINE(name) (void **)&name##_Actual
-
-#define DETOUR_CREATE_MEMBER(name, gamedata) CDetourManager::CreateDetour(GET_MEMBER_CALLBACK(name), GET_MEMBER_TRAMPOLINE(name), gamedata);
-#define DETOUR_CREATE_STATIC(name, gamedata) CDetourManager::CreateDetour(GET_STATIC_CALLBACK(name), GET_STATIC_TRAMPOLINE(name), gamedata);
-
-
-class GenericClass {};
-typedef void (GenericClass::*VoidFunc)();
-
-inline void *GetCodeAddr(VoidFunc mfp)
-{
- return *(void **)&mfp;
-}
-
-/**
- * Converts a member function pointer to a void pointer.
- * This relies on the assumption that the code address lies at mfp+0
- * This is the case for both g++ and later MSVC versions on non virtual functions but may be different for other compilers
- * Based on research by Don Clugston : http://www.codeproject.com/cpp/FastDelegate.asp
- */
-#define GetCodeAddress(mfp) GetCodeAddr(reinterpret_cast(mfp))
-
-class CDetourManager;
-
-class CDetour
-{
-public:
-
- bool IsEnabled();
-
- /**
- * These would be somewhat self-explanatory I hope
- */
- void EnableDetour();
- void DisableDetour();
-
- void Destroy();
-
- friend class CDetourManager;
-
-protected:
- CDetour(void *callbackfunction, void **trampoline, const char *signame);
-
- bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf);
-private:
-
- /* These create/delete the allocated memory */
- bool CreateDetour();
- void DeleteDetour();
-
- bool enabled;
- bool detoured;
-
- patch_t detour_restore;
- /* Address of the detoured function */
- void *detour_address;
- /* Address of the allocated trampoline function */
- void *detour_trampoline;
- /* Address of the callback handler */
- void *detour_callback;
- /* The function pointer used to call our trampoline */
- void **trampoline;
-
- const char *signame;
- ISourcePawnEngine *spengine;
- IGameConfig *gameconf;
-};
-
-class CDetourManager
-{
-public:
-
- static void Init(ISourcePawnEngine *spengine, IGameConfig *gameconf);
-
- /**
- * Creates a new detour
- *
- * @param callbackfunction Void pointer to your detour callback function.
- * @param trampoline Address of the trampoline pointer
- * @param signame Section name containing a signature to fetch from the gamedata file.
- * @return A new CDetour pointer to control your detour.
- *
- * Example:
- *
- * CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int)
- *
- * Define a new class with the required function and a member function pointer to the same type:
- *
- * class CBaseServerDetour
- * {
- * public:
- * bool ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int);
- * static bool (CBaseServerDetour::* ConnectClient_Actual)(void *netaddr_s, int, int, int, char const*, char const*, char const*, int);
- * }
- *
- * void *callbackfunc = GetCodeAddress(&CBaseServerDetour::ConnectClient);
- * void **trampoline = (void **)(&CBaseServerDetour::ConnectClient_Actual);
- *
- * Creation:
- * CDetourManager::CreateDetour(callbackfunc, trampoline, "ConnectClient");
- *
- * Usage:
- *
- * CBaseServerDetour::ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int)
- * {
- * //pre hook code
- * bool result = (this->*ConnectClient_Actual)(netaddr_s, rest of params);
- * //post hook code
- * return result;
- * }
- *
- * Note we changed the netadr_s reference into a void* to avoid needing to define the type
- */
- static CDetour *CreateDetour(void *callbackfunction, void **trampoline, const char *signame);
-
- friend class CBlocker;
- friend class CDetour;
-
-private:
- static ISourcePawnEngine *spengine;
- static IGameConfig *gameconf;
-};
-
-#define DECL_DETOUR(name) \
- CDetour *name##_Detour = nullptr;
-
-#define CREATE_DETOUR(name, signname, var) \
- name##_Detour = DETOUR_CREATE_MEMBER(name, signname); \
- if (name##_Detour != NULL) \
- { \
- name##_Detour->EnableDetour(); \
- var = true; \
- } else { \
- g_pSM->LogError(myself, "Failed to create " signname " detour, check error log.\n"); \
- var = false; \
- }
-
-#define CREATE_DETOUR_STATIC(name, signname, var) \
- name##_Detour = DETOUR_CREATE_STATIC(name, signname); \
- if (name##_Detour != NULL) \
- { \
- name##_Detour->EnableDetour(); \
- var = true; \
- } else { \
- g_pSM->LogError(myself, "Failed to create " signname " detour, check error log.\n"); \
- var = false; \
- }
-
-#define DESTROY_DETOUR(name) \
- if (name##_Detour != nullptr) \
-{ \
- name##_Detour->Destroy(); \
- name##_Detour = nullptr; \
-}
-
-#endif // _INCLUDE_SOURCEMOD_DETOURS_H_
diff --git a/extension/ISendProxy.h b/extension/ISendProxy.h
index ca83217..28c96ae 100644
--- a/extension/ISendProxy.h
+++ b/extension/ISendProxy.h
@@ -32,399 +32,14 @@
#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 "ISendProxyInterface133"
-#define SMINTERFACE_SENDPROXY_VERSION 0x133
-
-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_Vector,
+ Prop_EHandle,
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;
- /*
- * 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;
-};
-
-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) = 0;
- virtual bool HookProxy(IExtension * pMyself, const char * pProp, CBaseEntity * pEntity, PropType iType, CallBackType iCallbackType, void * pCallback) = 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) = 0;
- virtual bool HookProxyGamerules(IExtension * pMyself, const char * pProp, PropType iType, CallBackType iCallbackType, void * pCallback) = 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
\ No newline at end of file
diff --git a/extension/asm/asm.c b/extension/asm/asm.c
deleted file mode 100644
index 2facf8d..0000000
--- a/extension/asm/asm.c
+++ /dev/null
@@ -1,421 +0,0 @@
-#include "asm.h"
-
-#ifndef WIN32
-#define _GNU_SOURCE
-#include
-#include
-
-#define REG_EAX 0
-#define REG_ECX 1
-#define REG_EDX 2
-#define REG_EBX 3
-
-#define IA32_MOV_REG_IMM 0xB8 // encoding is +r
-#endif
-
-extern void Msg( const char *, ... );
-
-/**
-* Checks if a call to a fpic thunk has just been written into dest.
-* If found replaces it with a direct mov that sets the required register to the value of pc.
-*
-* @param dest Destination buffer where a call opcode + addr (5 bytes) has just been written.
-* @param pc The program counter value that needs to be set (usually the next address from the source).
-* @noreturn
-*/
-void check_thunks(unsigned char *dest, unsigned char *pc)
-{
-#if defined WIN32
- return;
-#else
- /* Step write address back 4 to the start of the function address */
- unsigned char *writeaddr = dest - 4;
- unsigned char *calloffset = *(unsigned char **)writeaddr;
- unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset);
-
- /* Lookup name of function being called */
- if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3))
- {
- //a thunk maybe?
- char movByte = IA32_MOV_REG_IMM;
-
- /* Calculate the correct mov opcode */
- switch (*(calladdr+1))
- {
- case 0x04:
- {
- movByte += REG_EAX;
- break;
- }
- case 0x1C:
- {
- movByte += REG_EBX;
- break;
- }
- case 0x0C:
- {
- movByte += REG_ECX;
- break;
- }
- case 0x14:
- {
- movByte += REG_EDX;
- break;
- }
- default:
- {
- Msg("Unknown thunk: %c\n", *(calladdr+1));
- break;
- }
- }
-
- /* Move our write address back one to where the call opcode was */
- writeaddr--;
-
-
- /* Write our mov */
- *writeaddr = movByte;
- writeaddr++;
-
- /* Write the value - The provided program counter value */
- *(void **)writeaddr = (void *)pc;
- writeaddr += 4;
- }
-
- return;
-#endif
-}
-
-//if dest is NULL, returns minimum number of bytes needed to be copied
-//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
-//http://www.devmaster.net/forums/showthread.php?t=2311
-int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) {
- int bytecount = 0;
-
- while(bytecount < required_len && *func != 0xCC)
- {
- // prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h
- int operandSize = 4;
- int FPU = 0;
- int twoByte = 0;
- unsigned char opcode = 0x90;
- unsigned char modRM = 0xFF;
- while(*func == 0xF0 ||
- *func == 0xF2 ||
- *func == 0xF3 ||
- (*func & 0xFC) == 0x64 ||
- (*func & 0xF8) == 0xD8 ||
- (*func & 0x7E) == 0x62)
- {
- if(*func == 0x66)
- {
- operandSize = 2;
- }
- else if((*func & 0xF8) == 0xD8)
- {
- FPU = *func;
- if (dest)
- *dest++ = *func++;
- else
- func++;
- bytecount++;
- break;
- }
-
- if (dest)
- *dest++ = *func++;
- else
- func++;
- bytecount++;
- }
-
- // two-byte opcode byte
- if(*func == 0x0F)
- {
- twoByte = 1;
- if (dest)
- *dest++ = *func++;
- else
- func++;
- bytecount++;
- }
-
- // opcode byte
- opcode = *func++;
- if (dest) *dest++ = opcode;
- bytecount++;
-
- // mod R/M byte
- modRM = 0xFF;
- if(FPU)
- {
- if((opcode & 0xC0) != 0xC0)
- {
- modRM = opcode;
- }
- }
- else if(!twoByte)
- {
- if((opcode & 0xC4) == 0x00 ||
- ((opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09)) ||
- (opcode & 0xF0) == 0x80 ||
- ((opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02) ||
- (opcode & 0xFC) == 0xD0 ||
- (opcode & 0xF6) == 0xF6)
- {
- modRM = *func++;
- if (dest) *dest++ = modRM;
- bytecount++;
- }
- }
- else
- {
- if(((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D) ||
- (opcode & 0xF0) == 0x30 ||
- opcode == 0x77 ||
- (opcode & 0xF0) == 0x80 ||
- ((opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02) ||
- (opcode & 0xF8) == 0xC8)
- {
- // No mod R/M byte
- }
- else
- {
- modRM = *func++;
- if (dest) *dest++ = modRM;
- bytecount++;
- }
- }
-
- // SIB
- if((modRM & 0x07) == 0x04 &&
- (modRM & 0xC0) != 0xC0)
- {
- if (dest)
- *dest++ = *func++; //SIB
- else
- func++;
- bytecount++;
- }
-
- // mod R/M displacement
-
- // Dword displacement, no base
- if((modRM & 0xC5) == 0x05) {
- if (dest) {
- *(unsigned int*)dest = *(unsigned int*)func;
- dest += 4;
- }
- func += 4;
- bytecount += 4;
- }
-
- // Byte displacement
- if((modRM & 0xC0) == 0x40) {
- if (dest)
- *dest++ = *func++;
- else
- func++;
- bytecount++;
- }
-
- // Dword displacement
- if((modRM & 0xC0) == 0x80) {
- if (dest) {
- *(unsigned int*)dest = *(unsigned int*)func;
- dest += 4;
- }
- func += 4;
- bytecount += 4;
- }
-
- // immediate
- if(FPU)
- {
- // Can't have immediate operand
- }
- else if(!twoByte)
- {
- if((opcode & 0xC7) == 0x04 ||
- (opcode & 0xFE) == 0x6A || // PUSH/POP/IMUL
- (opcode & 0xF0) == 0x70 || // Jcc
- opcode == 0x80 ||
- opcode == 0x83 ||
- (opcode & 0xFD) == 0xA0 || // MOV
- opcode == 0xA8 || // TEST
- (opcode & 0xF8) == 0xB0 || // MOV
- (opcode & 0xFE) == 0xC0 || // RCL
- opcode == 0xC6 || // MOV
- opcode == 0xCD || // INT
- (opcode & 0xFE) == 0xD4 || // AAD/AAM
- (opcode & 0xF8) == 0xE0 || // LOOP/JCXZ
- opcode == 0xEB ||
- (opcode == 0xF6 && (modRM & 0x30) == 0x00)) // TEST
- {
- if (dest)
- *dest++ = *func++;
- else
- func++;
- bytecount++;
- }
- else if((opcode & 0xF7) == 0xC2) // RET
- {
- if (dest) {
- *(unsigned short*)dest = *(unsigned short*)func;
- dest += 2;
- }
- func += 2;
- bytecount += 2;
- }
- else if((opcode & 0xFC) == 0x80 ||
- (opcode & 0xC7) == 0x05 ||
- (opcode & 0xF8) == 0xB8 ||
- (opcode & 0xFE) == 0xE8 || // CALL/Jcc
- (opcode & 0xFE) == 0x68 ||
- (opcode & 0xFC) == 0xA0 ||
- (opcode & 0xEE) == 0xA8 ||
- opcode == 0xC7 ||
- (opcode == 0xF7 && (modRM & 0x30) == 0x00))
- {
- if (dest) {
- //Fix CALL/JMP offset
- if ((opcode & 0xFE) == 0xE8) {
- if (operandSize == 4)
- {
- *(long*)dest = ((func + *(long*)func) - dest);
-
- //pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc
- check_thunks(dest+4, func+4);
- }
- else
- *(short*)dest = ((func + *(short*)func) - dest);
-
- } else {
- if (operandSize == 4)
- *(unsigned long*)dest = *(unsigned long*)func;
- else
- *(unsigned short*)dest = *(unsigned short*)func;
- }
- dest += operandSize;
- }
- func += operandSize;
- bytecount += operandSize;
-
- }
- }
- else
- {
- if(opcode == 0xBA || // BT
- opcode == 0x0F || // 3DNow!
- (opcode & 0xFC) == 0x70 || // PSLLW
- (opcode & 0xF7) == 0xA4 || // SHLD
- opcode == 0xC2 ||
- opcode == 0xC4 ||
- opcode == 0xC5 ||
- opcode == 0xC6)
- {
- if (dest)
- *dest++ = *func++;
- else
- func++;
- }
- else if((opcode & 0xF0) == 0x80) // Jcc -i
- {
- if (dest) {
- if (operandSize == 4)
- *(unsigned long*)dest = *(unsigned long*)func;
- else
- *(unsigned short*)dest = *(unsigned short*)func;
-
- dest += operandSize;
- }
- func += operandSize;
- bytecount += operandSize;
- }
- }
- }
-
- return bytecount;
-}
-
-//insert a specific JMP instruction at the given location
-void inject_jmp(void* src, void* dest) {
- *(unsigned char*)src = OP_JMP;
- *(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE));
-}
-
-//fill a given block with NOPs
-void fill_nop(void* src, unsigned int len) {
- unsigned char* src2 = (unsigned char*)src;
- while (len) {
- *src2++ = OP_NOP;
- --len;
- }
-}
-
-void* eval_jump(void* src) {
- unsigned char* addr = (unsigned char*)src;
-
- if (!addr) return 0;
-
- //import table jump
- if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) {
- addr += 2;
- addr = *(unsigned char**)addr;
- //TODO: if addr points into the IAT
- return *(void**)addr;
- }
-
- //8bit offset
- else if (addr[0] == OP_JMP_BYTE) {
- addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1];
- //mangled 32bit jump?
- if (addr[0] == OP_JMP) {
- addr = addr + *(int*)&addr[1];
- }
- return addr;
- }
- /*
- //32bit offset
- else if (addr[0] == OP_JMP) {
- addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1];
- }
- */
-
- return addr;
-}
-/*
-from ms detours package
-static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
-{
- MEMORY_BASIC_INFORMATION mbi;
- VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
- __try {
- PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
- if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
- return false;
- }
-
- PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
- pDosHeader->e_lfanew);
- if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
- return false;
- }
-
- if (pbAddress >= ((PBYTE)pDosHeader +
- pNtHeader->OptionalHeader
- .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
- pbAddress < ((PBYTE)pDosHeader +
- pNtHeader->OptionalHeader
- .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
- pNtHeader->OptionalHeader
- .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
- return true;
- }
- return false;
- }
- __except(EXCEPTION_EXECUTE_HANDLER) {
- return false;
- }
-}
-*/
diff --git a/extension/asm/asm.h b/extension/asm/asm.h
deleted file mode 100644
index 6086232..0000000
--- a/extension/asm/asm.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef __ASM_H__
-#define __ASM_H__
-
-#define OP_JMP 0xE9
-#define OP_JMP_SIZE 5
-
-#define OP_NOP 0x90
-#define OP_NOP_SIZE 1
-
-#define OP_PREFIX 0xFF
-#define OP_JMP_SEG 0x25
-
-#define OP_JMP_BYTE 0xEB
-#define OP_JMP_BYTE_SIZE 2
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void check_thunks(unsigned char *dest, unsigned char *pc);
-
-//if dest is NULL, returns minimum number of bytes needed to be copied
-//if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs
-//http://www.devmaster.net/forums/showthread.php?t=2311
-int copy_bytes(unsigned char *func, unsigned char* dest, int required_len);
-
-//insert a specific JMP instruction at the given location
-void inject_jmp(void* src, void* dest);
-
-//fill a given block with NOPs
-void fill_nop(void* src, unsigned int len);
-
-//evaluate a JMP at the target
-void* eval_jump(void* src);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif //__ASM_H__
diff --git a/extension/clientpacks_detours.cpp b/extension/clientpacks_detours.cpp
new file mode 100644
index 0000000..b21665d
--- /dev/null
+++ b/extension/clientpacks_detours.cpp
@@ -0,0 +1,331 @@
+#include "clientpacks_detours.h"
+#include "sendprop_hookmanager.h"
+#include "CDetour/detours.h"
+#include "iclient.h"
+#include
+#include
+#include
+
+#if defined(DEBUG) || defined(_DEBUG)
+#define DEBUG_SENDPROXY_MEMORY
+#endif
+
+DECL_DETOUR(CFrameSnapshotManager_UsePreviouslySentPacket);
+DECL_DETOUR(CFrameSnapshotManager_GetPreviouslySentPacket);
+DECL_DETOUR(CFrameSnapshotManager_CreatePackedEntity);
+DECL_DETOUR(PackEntities_Normal);
+DECL_DETOUR(SV_ComputeClientPacks);
+
+volatile int g_iCurrentClientIndexInLoop = -1; //used for optimization
+
+struct PackedEntityInfo
+{
+ PackedEntityInfo()
+ {
+ handles.fill(INVALID_PACKED_ENTITY_HANDLE);
+ updatebits.set();
+ }
+
+ std::array handles;
+ std::bitset updatebits;
+};
+std::unordered_map g_EntityPackMap;
+
+ConVar ext_sendproxy_frame_callback("ext_sendproxy_frame_callback", "0", FCVAR_NONE, "Invoke hooked proxy every frame.");
+
+/*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_MEMBER3(CFrameSnapshotManager_UsePreviouslySentPacket, bool, CFrameSnapshot*, pSnapshot, int, entity, int, entSerialNumber)
+{
+ if (g_iCurrentClientIndexInLoop != -1)
+ {
+ framesnapshotmanager->m_pLastPackedData[entity] = g_EntityPackMap.at(entity).handles[g_iCurrentClientIndexInLoop];
+ }
+
+ return DETOUR_MEMBER_CALL(CFrameSnapshotManager_UsePreviouslySentPacket)(pSnapshot, entity, entSerialNumber);
+}
+
+DETOUR_DECL_MEMBER2(CFrameSnapshotManager_GetPreviouslySentPacket, PackedEntity*, int, entity, int, entSerialNumber)
+{
+ if (g_iCurrentClientIndexInLoop != -1)
+ {
+ framesnapshotmanager->m_pLastPackedData[entity] = g_EntityPackMap.at(entity).handles[g_iCurrentClientIndexInLoop];
+ }
+
+ return DETOUR_MEMBER_CALL(CFrameSnapshotManager_GetPreviouslySentPacket)(entity, entSerialNumber);
+}
+
+DETOUR_DECL_MEMBER2(CFrameSnapshotManager_CreatePackedEntity, PackedEntity*, CFrameSnapshot*, pSnapshot, int, entity)
+{
+ if (g_iCurrentClientIndexInLoop == -1)
+ {
+ return DETOUR_MEMBER_CALL(CFrameSnapshotManager_CreatePackedEntity)(pSnapshot, entity);
+ }
+
+ framesnapshotmanager->m_pLastPackedData[entity] = g_EntityPackMap.at(entity).handles[g_iCurrentClientIndexInLoop];
+
+ PackedEntity *result = DETOUR_MEMBER_CALL(CFrameSnapshotManager_CreatePackedEntity)(pSnapshot, entity);
+
+ g_EntityPackMap.at(entity).handles[g_iCurrentClientIndexInLoop] = framesnapshotmanager->m_pLastPackedData[entity];
+
+ return result;
+}
+
+static void CopyFrameSnapshot(CFrameSnapshot *dest, const CFrameSnapshot *src);
+static void CopyPackedEntities(CFrameSnapshot *dest, const CFrameSnapshot *src);
+static bool g_bSetupClientPacks = false;
+
+DETOUR_DECL_STATIC3(PackEntities_Normal, void, int, iClientCount, CGameClient **, pClients, CFrameSnapshot *, pSnapShot)
+{
+ if (g_bSetupClientPacks)
+ return;
+
+ return DETOUR_STATIC_CALL(PackEntities_Normal)(iClientCount, pClients, pSnapShot);
+}
+
+DETOUR_DECL_STATIC3(SV_ComputeClientPacks, void, int, iClientCount, CGameClient **, pClients, CFrameSnapshot *, pSnapShot)
+{
+ if (playerhelpers->GetMaxClients() <= 1
+ || !g_pSendPropHookManager->IsAnyEntityHooked()
+ || *g_ppLocalNetworkBackdoor != nullptr)
+ {
+ static bool isLocalBackdoorEncountered = false;
+
+ if (!isLocalBackdoorEncountered && *g_ppLocalNetworkBackdoor != nullptr)
+ {
+ isLocalBackdoorEncountered = true;
+ LogError("SendProxy will NOT work in single player.");
+ }
+
+ return DETOUR_STATIC_CALL(SV_ComputeClientPacks)(iClientCount, pClients, pSnapShot);
+ }
+
+ const int numEntities = pSnapShot->m_nValidEntities;
+ int numHooked = 0;
+
+ // Move all hooked entities to the back
+ for (int i = numEntities-1; i >= 0; --i)
+ {
+ const auto entindex = pSnapShot->m_pValidEntities[i];
+ if (g_pSendPropHookManager->IsEntityHooked(entindex))
+ {
+ const auto tail = pSnapShot->m_nValidEntities - numHooked - 1;
+ std::swap(pSnapShot->m_pValidEntities[i], pSnapShot->m_pValidEntities[tail]);
+ numHooked++;
+
+ if (gamehelpers->EdictOfIndex(entindex)->HasStateChanged() || ext_sendproxy_frame_callback.GetBool())
+ g_EntityPackMap.at(entindex).updatebits.set();
+ }
+ }
+
+ // Make snapshots for each client
+ CUtlVector clientSnapshots(0, iClientCount);
+ clientSnapshots[0] = pSnapShot;
+ for (int i = 1; i < iClientCount; ++i)
+ {
+ clientSnapshots[i] = framesnapshotmanager->CreateEmptySnapshot(pSnapShot->m_nTickCount, pSnapShot->m_nNumEntities);
+ CopyFrameSnapshot(clientSnapshots[i], pSnapShot);
+ }
+
+ // Setup transmit infos
+ g_bSetupClientPacks = true;
+ for (int i = 0; i < iClientCount; ++i)
+ {
+ CGameClient *client = pClients[i];
+ CFrameSnapshot *snapshot = clientSnapshots[i];
+
+ DETOUR_STATIC_CALL(SV_ComputeClientPacks)(1, &client, snapshot);
+ }
+ g_bSetupClientPacks = false;
+
+ // Pack all unhooked entities
+ {
+ g_iCurrentClientIndexInLoop = -1;
+
+ pSnapShot->m_nValidEntities = numEntities - numHooked;
+ DETOUR_STATIC_CALL(PackEntities_Normal)(iClientCount, pClients, pSnapShot);
+ pSnapShot->m_nValidEntities = numEntities;
+
+ for (int i = 1; i < iClientCount; ++i)
+ {
+ CopyPackedEntities(clientSnapshots[i], pSnapShot);
+ }
+ }
+
+ // Pack hooked entities for each client
+ {
+ ConVarScopedSet linearpack(sv_parallel_packentities, "0");
+
+ for (int i = 0; i < iClientCount; ++i)
+ {
+ CGameClient *client = pClients[i];
+ CFrameSnapshot *snapshot = clientSnapshots[i];
+
+ g_iCurrentClientIndexInLoop = client->GetPlayerSlot();
+
+ snapshot->m_pValidEntities += numEntities - numHooked;
+ snapshot->m_nValidEntities = numHooked;
+
+ std::for_each_n(snapshot->m_pValidEntities,
+ snapshot->m_nValidEntities,
+ [](int edictidx)
+ {
+ if (g_EntityPackMap.at(edictidx).updatebits[g_iCurrentClientIndexInLoop])
+ {
+ g_EntityPackMap.at(edictidx).updatebits[g_iCurrentClientIndexInLoop] = false;
+ gamehelpers->EdictOfIndex(edictidx)->m_fStateFlags |= FL_EDICT_CHANGED;
+ }
+ });
+
+ DETOUR_STATIC_CALL(PackEntities_Normal)(1, &client, snapshot);
+
+ snapshot->m_nValidEntities = numEntities;
+ snapshot->m_pValidEntities -= numEntities - numHooked;
+ }
+ }
+
+ g_iCurrentClientIndexInLoop = -1;
+
+ // finally decrement reference of manually created snapshots
+ for (int i = 1; i < iClientCount; ++i)
+ {
+ Assert(clientSnapshots[i]->m_nReferences == 2);
+ clientSnapshots[i]->ReleaseReference();
+ }
+}
+
+int ClientPacksDetour::GetCurrentClientIndex()
+{
+ if (g_iCurrentClientIndexInLoop == -1)
+ return -1;
+
+ return g_iCurrentClientIndexInLoop + 1;
+}
+
+void ClientPacksDetour::OnEntityHooked(int entity)
+{
+ if (g_EntityPackMap.find(entity) == g_EntityPackMap.end())
+ {
+ g_EntityPackMap.emplace(entity, PackedEntityInfo());
+
+ if (framesnapshotmanager->m_pLastPackedData[entity] != INVALID_PACKED_ENTITY_HANDLE)
+ {
+ framesnapshotmanager->RemoveEntityReference(framesnapshotmanager->m_pLastPackedData[entity]);
+ framesnapshotmanager->m_pLastPackedData[entity] = INVALID_PACKED_ENTITY_HANDLE;
+ }
+ }
+}
+
+void ClientPacksDetour::OnEntityUnhooked(int entity)
+{
+ if (g_EntityPackMap.find(entity) == g_EntityPackMap.end())
+ return;
+
+ for (PackedEntityHandle_t handle : g_EntityPackMap.at(entity).handles)
+ {
+ if (handle != INVALID_PACKED_ENTITY_HANDLE)
+ {
+ framesnapshotmanager->RemoveEntityReference(handle);
+ }
+ }
+
+ g_EntityPackMap.erase(entity);
+ framesnapshotmanager->m_pLastPackedData[entity] = INVALID_PACKED_ENTITY_HANDLE;
+}
+
+void ClientPacksDetour::OnClientDisconnect(int client)
+{
+ int index = client - 1;
+
+ for (auto& [_, info] : g_EntityPackMap)
+ {
+ if (info.handles[index] != INVALID_PACKED_ENTITY_HANDLE)
+ {
+ framesnapshotmanager->RemoveEntityReference(info.handles[index]);
+ info.handles[index] = INVALID_PACKED_ENTITY_HANDLE;
+ }
+ }
+}
+
+bool ClientPacksDetour::Init(IGameConfig *gc)
+{
+ CDetourManager::Init(smutils->GetScriptingEngine(), gc);
+
+ bool bDetoursInited = true;
+ CREATE_DETOUR(CFrameSnapshotManager_UsePreviouslySentPacket, "CFrameSnapshotManager::UsePreviouslySentPacket", bDetoursInited);
+ CREATE_DETOUR(CFrameSnapshotManager_GetPreviouslySentPacket, "CFrameSnapshotManager::GetPreviouslySentPacket", bDetoursInited);
+ CREATE_DETOUR(CFrameSnapshotManager_CreatePackedEntity, "CFrameSnapshotManager::CreatePackedEntity", bDetoursInited);
+ CREATE_DETOUR_STATIC(PackEntities_Normal, "PackEntities_Normal", bDetoursInited);
+ CREATE_DETOUR_STATIC(SV_ComputeClientPacks, "SV_ComputeClientPacks", bDetoursInited);
+
+ if (!bDetoursInited)
+ return false;
+
+ return true;
+}
+
+void ClientPacksDetour::Shutdown()
+{
+ DESTROY_DETOUR(CFrameSnapshotManager_UsePreviouslySentPacket);
+ DESTROY_DETOUR(CFrameSnapshotManager_GetPreviouslySentPacket);
+ DESTROY_DETOUR(CFrameSnapshotManager_CreatePackedEntity);
+ DESTROY_DETOUR(PackEntities_Normal);
+ DESTROY_DETOUR(SV_ComputeClientPacks);
+}
+
+void ClientPacksDetour::Clear()
+{
+#ifdef DEBUG_SENDPROXY_MEMORY
+ LogMessage("=== PACKED ENTITIES COUNT (%d) ===", framesnapshotmanager->m_PackedEntities.Count());
+#endif
+
+ g_EntityPackMap.clear();
+}
+
+static void CopyFrameSnapshot(CFrameSnapshot *dest, const CFrameSnapshot *src)
+{
+ Assert(dest->m_nNumEntities == src->m_nNumEntities);
+ Q_memcpy(dest->m_pEntities, src->m_pEntities, dest->m_nNumEntities * sizeof(CFrameSnapshotEntry));
+
+ dest->m_nValidEntities = src->m_nValidEntities;
+ dest->m_pValidEntities = new unsigned short[dest->m_nValidEntities];
+ Q_memcpy(dest->m_pValidEntities, src->m_pValidEntities, dest->m_nValidEntities * sizeof(unsigned short));
+
+ if (src->m_pHLTVEntityData != NULL)
+ {
+ Assert(dest->m_pHLTVEntityData == NULL);
+ dest->m_pHLTVEntityData = new CHLTVEntityData[dest->m_nValidEntities];
+ Q_memset( dest->m_pHLTVEntityData, 0, dest->m_nValidEntities * sizeof(CHLTVEntityData) );
+ }
+
+ dest->m_iExplicitDeleteSlots = src->m_iExplicitDeleteSlots;
+
+ // FIXME: Copy temp entity data
+}
+
+static void CopyPackedEntities(CFrameSnapshot *dest, const CFrameSnapshot *src)
+{
+ int numEntities = dest->m_nNumEntities;
+ for (int i = 0; i < numEntities; ++i)
+ {
+ auto data = src->m_pEntities[i].m_pPackedData;
+ dest->m_pEntities[i].m_pPackedData = data;
+
+ if (data != INVALID_PACKED_ENTITY_HANDLE)
+ framesnapshotmanager->AddEntityReference(data);
+ }
+
+ if (dest->m_pHLTVEntityData && src->m_pHLTVEntityData)
+ {
+ Q_memcpy( dest->m_pHLTVEntityData, src->m_pHLTVEntityData, dest->m_nValidEntities * sizeof(CHLTVEntityData) );
+ }
+}
diff --git a/extension/clientpacks_detours.h b/extension/clientpacks_detours.h
new file mode 100644
index 0000000..df0e216
--- /dev/null
+++ b/extension/clientpacks_detours.h
@@ -0,0 +1,18 @@
+#ifndef _CLIENTPACKS_DETOURS_H
+#define _CLIENTPACKS_DETOURS_H
+
+#include "extension.h"
+
+class ClientPacksDetour
+{
+public:
+ static bool Init(IGameConfig *pGameConf);
+ static void Shutdown();
+ static void Clear();
+ static int GetCurrentClientIndex();
+ static void OnEntityHooked(int entity);
+ static void OnEntityUnhooked(int entity);
+ static void OnClientDisconnect(int client);
+};
+
+#endif
\ No newline at end of file
diff --git a/extension/configure.py b/extension/configure.py
deleted file mode 100644
index 57910e8..0000000
--- a/extension/configure.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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/extension.cpp b/extension/extension.cpp
index d52c608..6f5675a 100644
--- a/extension/extension.cpp
+++ b/extension/extension.cpp
@@ -29,1840 +29,227 @@
* 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>
-
-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(CFrameSnapshotManager_UsePreviouslySentPacket);
-DECL_DETOUR(CFrameSnapshotManager_GetPreviouslySentPacket);
-DECL_DETOUR(CFrameSnapshotManager_CreatePackedEntity);
-// DECL_DETOUR(CFrameSnapshotManager_RemoveEntityReference);
-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 = false;
-IServer * g_pIServer = nullptr;
+#include "clientpacks_detours.h"
+#include "sendprop_hookmanager.h"
SendProxyManager g_SendProxyManager;
-SendProxyManagerInterfaceImpl * g_pMyInterface = nullptr;
SMEXT_LINK(&g_SendProxyManager);
-CThreadFastMutex g_WorkMutex;
-
-CUtlVector g_Hooks;
-CUtlVector g_HooksGamerules;
-CUtlVector g_ChangeHooks;
-CUtlVector g_ChangeHooksGamerules;
-
-CUtlVector g_vHookedEdicts;
+static SendPropHookManager s_SendPropHookManager;
+SendPropHookManager *g_pSendPropHookManager = &s_SendPropHookManager;
+std::string g_szGameRulesProxy;
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;
-int g_iGameRulesProxyIndex = -1;
-PackedEntityHandle_t g_PlayersPackedGameRules[SM_MAXPLAYERS] = {INVALID_PACKED_ENTITY_HANDLE};
-void * g_pGameRules = nullptr;
-bool g_bShouldChangeGameRulesState = false;
-bool g_bSendSnapshots = false;
-
-bool g_bEdictChanged[MAX_EDICTS] = {false};
-
-CGlobalVars * g_pGlobals = nullptr;
-
-static CBaseEntity * FindEntityByServerClassname(int, const char *);
-static void CallChangeCallbacks(PropChangeHook * pInfo, void * pOldValue, void * pNewValue);
-static void CallChangeGamerulesCallbacks(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
-*/
-
-#ifndef DEBUG
-// #define _FORCE_DEBUG
-
-#ifdef _FORCE_DEBUG
-#define DEBUG
-#endif
+CGlobalVars *gpGlobals = nullptr;
+IBinTools* bintools = nullptr;
+ISDKHooks * sdkhooks = nullptr;
+ConVar *sv_parallel_packentities = nullptr;
+
+CFrameSnapshotManager* framesnapshotmanager = nullptr;
+void* CFrameSnapshotManager::s_pfnCreateEmptySnapshot = nullptr;
+ICallWrapper* CFrameSnapshotManager::s_callCreateEmptySnapshot = nullptr;
+void* CFrameSnapshotManager::s_pfnRemoveEntityReference = nullptr;
+ICallWrapper* CFrameSnapshotManager::s_callRemoveEntityReference = nullptr;
+void* CFrameSnapshot::s_pfnReleaseReference = nullptr;
+ICallWrapper *CFrameSnapshot::s_callReleaseReference = nullptr;
+
+void **g_ppLocalNetworkBackdoor = nullptr;
+
+bool SendProxyManager::SDK_OnLoad(char *error, size_t maxlen, bool late)
+{
+ auto gc_sdktools = *AutoGameConfig::Load("sdktools.games");
+ auto gc = *AutoGameConfig::Load("sendproxy");
+ if (!gc_sdktools || !gc)
+ return false;
-#endif // #ifndef DEBUG
+ g_szGameRulesProxy = gc_sdktools->GetKeyValue("GameRulesProxy");
-DETOUR_DECL_MEMBER3(CFrameSnapshotManager_UsePreviouslySentPacket, bool, CFrameSnapshot*, pSnapshot, int, entity, int, entSerialNumber)
-{
- if (g_iCurrentClientIndexInLoop == -1
- || !g_bCurrentGameClientCallFwd
- || entity != g_iGameRulesProxyIndex)
- {
- return DETOUR_MEMBER_CALL(CFrameSnapshotManager_UsePreviouslySentPacket)(pSnapshot, entity, entSerialNumber);
- }
+ GAMECONF_GETSIGNATURE(gc, "CFrameSnapshotManager::CreateEmptySnapshot", &CFrameSnapshotManager::s_pfnCreateEmptySnapshot);
+ GAMECONF_GETSIGNATURE(gc, "CFrameSnapshotManager::RemoveEntityReference", &CFrameSnapshotManager::s_pfnRemoveEntityReference);
+ GAMECONF_GETSIGNATURE(gc, "CFrameSnapshot::ReleaseReference", &CFrameSnapshot::s_pfnReleaseReference);
+ GAMECONF_GETADDRESS(gc, "framesnapshotmanager", &framesnapshotmanager);
+ GAMECONF_GETADDRESS(gc, "g_ppLocalNetworkBackdoor", &g_ppLocalNetworkBackdoor);
- if (g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop] == INVALID_PACKED_ENTITY_HANDLE)
+ if (!ClientPacksDetour::Init(gc))
return false;
- CFrameSnapshotManager *framesnapshotmanager = (CFrameSnapshotManager *)this;
- framesnapshotmanager->m_pLastPackedData[entity] = g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop];
- return DETOUR_MEMBER_CALL(CFrameSnapshotManager_UsePreviouslySentPacket)(pSnapshot, entity, entSerialNumber);
-}
-
-DETOUR_DECL_MEMBER2(CFrameSnapshotManager_GetPreviouslySentPacket, PackedEntity*, int, entity, int, entSerialNumber)
-{
- if (g_iCurrentClientIndexInLoop == -1
- || !g_bCurrentGameClientCallFwd
- || entity != g_iGameRulesProxyIndex)
- {
- return DETOUR_MEMBER_CALL(CFrameSnapshotManager_GetPreviouslySentPacket)(entity, entSerialNumber);
- }
-
- CFrameSnapshotManager *framesnapshotmanager = (CFrameSnapshotManager *)this;
+ sharesys->AddDependency(myself, "sdkhooks.ext", true, true);
+ sharesys->AddDependency(myself, "bintools.ext", true, true);
-#ifdef DEBUG
- char buffer[128];
- smutils->Format(buffer, sizeof(buffer), "GetPreviouslySentPacket (%d / %d)", framesnapshotmanager->m_pLastPackedData[entity], g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop]);
- gamehelpers->TextMsg(g_iCurrentClientIndexInLoop+1, 3, buffer);
-#endif
-
- framesnapshotmanager->m_pLastPackedData[entity] = g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop];
- return DETOUR_MEMBER_CALL(CFrameSnapshotManager_GetPreviouslySentPacket)(entity, entSerialNumber);
-}
-
-DETOUR_DECL_MEMBER2(CFrameSnapshotManager_CreatePackedEntity, PackedEntity*, CFrameSnapshot*, pSnapshot, int, entity)
-{
- if (g_iCurrentClientIndexInLoop == -1
- || !g_bCurrentGameClientCallFwd
- || entity != g_iGameRulesProxyIndex)
- {
- return DETOUR_MEMBER_CALL(CFrameSnapshotManager_CreatePackedEntity)(pSnapshot, entity);
- }
+ sharesys->RegisterLibrary(myself, "sendproxy2");
- CFrameSnapshotManager *framesnapshotmanager = (CFrameSnapshotManager *)this;
- PackedEntityHandle_t origHandle = framesnapshotmanager->m_pLastPackedData[entity];
+ plsys->AddPluginsListener(this);
+ playerhelpers->AddClientListener(this);
+ ConVar_Register(0, this);
- if (g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop] != INVALID_PACKED_ENTITY_HANDLE)
- framesnapshotmanager->m_pLastPackedData[entity] = g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop];
- PackedEntity *result = DETOUR_MEMBER_CALL(CFrameSnapshotManager_CreatePackedEntity)(pSnapshot, entity);
- g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop] = framesnapshotmanager->m_pLastPackedData[entity];
-
-#ifdef DEBUG
- char buffer[128];
- smutils->Format(buffer, sizeof(buffer), "CreatePackedEntity (%d / %d / %d)", origHandle, g_PlayersPackedGameRules[g_iCurrentClientIndexInLoop], framesnapshotmanager->m_pLastPackedData[entity]);
- gamehelpers->TextMsg(g_iCurrentClientIndexInLoop+1, 3, buffer);
-#endif
-
- return result;
+ return true;
}
-// DETOUR_DECL_MEMBER1(CFrameSnapshotManager_RemoveEntityReference, void, PackedEntityHandle_t, handle)
-// {
-// CFrameSnapshotManager *framesnapshotmanager = (CFrameSnapshotManager *)this;
-
-// PackedEntity *packedEntity = framesnapshotmanager->m_PackedEntities[handle];
-// if ( packedEntity->m_ReferenceCount <= 1)
-// {
-// for (int i = 0; i < (sizeof(g_PlayersPackedGameRules) / sizeof(g_PlayersPackedGameRules[0])); ++i)
-// {
-// if (g_PlayersPackedGameRules[i] == handle)
-// {
-// g_PlayersPackedGameRules[i] = INVALID_PACKED_ENTITY_HANDLE;
-
-// #ifdef DEBUG
-// char buffer[128];
-// for (int client = 1; client <= playerhelpers->GetMaxClients(); client++)
-// {
-// IGamePlayer *plr = playerhelpers->GetGamePlayer(client);
-// if (plr && plr->IsInGame() && !plr->IsFakeClient())
-// {
-// smutils->Format(buffer, sizeof(buffer), "RemoveEntityReference: (%d / %d)", handle, i + 1);
-// gamehelpers->TextMsg(client, 3, buffer);
-// }
-// }
-// #endif
-// }
-// }
-// }
-
-// DETOUR_MEMBER_CALL(CFrameSnapshotManager_RemoveEntityReference)(handle);
-// }
-
-#ifdef _FORCE_DEBUG
-#undef DEBUG
-#endif
-
-DETOUR_DECL_MEMBER1(CGameServer_SendClientMessages, void, bool, bSendSnapshots)
+void SendProxyManager::SDK_OnAllLoaded()
{
- 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;
- }
+ SM_GET_LATE_IFACE(SDKHOOKS, sdkhooks);
+ SM_GET_LATE_IFACE(BINTOOLS, bintools);
- for (int i = 0; i < MAX_EDICTS; ++i)
+ if (sdkhooks)
{
- g_bEdictChanged[i] = false;
-
- edict_t *edict = gamehelpers->EdictOfIndex(i);
- if (!edict || !edict->GetUnknown() || edict->IsFree())
- continue;
-
- if (i > 0 && i <= playerhelpers->GetMaxClients())
- {
- if (!g_pIServer->GetClient(i-1)->IsActive())
- continue;
- }
-
- if (!edict->HasStateChanged())
- continue;
-
- g_bEdictChanged[i] = true;
+ sdkhooks->AddEntityListener(this);
}
- bool bCalledForNullIClientsThisTime = false;
- for (int iClients = 1; iClients <= playerhelpers->GetMaxClients(); iClients++)
+ if (bintools)
{
- IGamePlayer * pPlayer = playerhelpers->GetGamePlayer(iClients);
- bool bFake = (pPlayer->IsFakeClient() && !(pPlayer->IsSourceTV()
-#if SOURCE_ENGINE != SE_CSGO
- || 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;
-}
+ SourceMod::PassInfo params[] = {
+ { PassType_Basic, PASSFLAG_BYVAL, sizeof(int), NULL, 0 },
+ { PassType_Basic, PASSFLAG_BYVAL, sizeof(int), NULL, 0 },
+ { PassType_Basic, PASSFLAG_BYVAL, sizeof(CFrameSnapshot*), NULL, 0 },
+ { PassType_Basic, PASSFLAG_BYVAL, sizeof(PackedEntityHandle_t), NULL, 0 }
+ };
-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
+ CFrameSnapshot::s_callReleaseReference = bintools->CreateCall(CFrameSnapshot::s_pfnReleaseReference, CallConv_ThisCall, NULL, params, 0);
+ if (CFrameSnapshot::s_callReleaseReference == NULL) {
+ LogError("Unable to create ICallWrapper for \"CFrameSnapshot::ReleaseReference\"!");
+ return;
}
- 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 (int i = 0; i < g_vHookedEdicts.Count(); i++)
- {
- edict_t * pEdict = g_vHookedEdicts[i];
- if (pEdict && !pEdict->IsFree() && pEdict->GetUnknown() && !(pEdict->m_fStateFlags & FL_EDICT_CHANGED))
- pEdict->m_fStateFlags |= FL_EDICT_CHANGED;
- }
- for (int i = 0; i < MAX_EDICTS; ++i)
- {
- if (!g_bEdictChanged[i])
- continue;
-
- edict_t *pEdict = gamehelpers->EdictOfIndex(i);
- 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 SendProxyManager::OnEntityDestroyed(CBaseEntity* pEnt)
-{
- int idx = gamehelpers->EntityToBCompatRef(pEnt);
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].objectID == idx)
- {
- g_SendProxyManager.UnhookProxy(i);
+ CFrameSnapshotManager::s_callCreateEmptySnapshot = bintools->CreateCall(CFrameSnapshotManager::s_pfnCreateEmptySnapshot, CallConv_ThisCall, ¶ms[2], ¶ms[0], 2);
+ if (CFrameSnapshotManager::s_callCreateEmptySnapshot == NULL) {
+ LogError("Unable to create ICallWrapper for \"CFrameSnapshotManager::CreateEmptySnapshot\"!");
+ return;
}
- }
-
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
- {
- if (g_ChangeHooks[i].objectID == idx)
- g_ChangeHooks.Remove(i--);
- }
-}
-
-void Hook_ClientDisconnect(edict_t * pEnt)
-{
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].objectID == gamehelpers->IndexOfEdict(pEnt))
- g_SendProxyManager.UnhookProxy(i);
- }
-
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
- {
- if (g_ChangeHooks[i].objectID == gamehelpers->IndexOfEdict(pEnt))
- g_ChangeHooks.Remove(i--);
- }
- if (gamehelpers->IndexOfEdict(pEnt) != -1)
- g_PlayersPackedGameRules[gamehelpers->IndexOfEdict(pEnt)] = INVALID_PACKED_ENTITY_HANDLE;
-
- RETURN_META(MRES_IGNORED);
-}
-
-void Hook_GameFrame(bool simulating)
-{
- if (simulating)
- {
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
- {
- switch(g_ChangeHooks[i].PropType)
- {
- case PropType::Prop_Int:
- {
- edict_t * pEnt = gamehelpers->EdictOfIndex(g_ChangeHooks[i].objectID);
- CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt);
- int iCurrent = *(int *)((unsigned char *)pEntity + g_ChangeHooks[i].Offset);
- if (iCurrent != g_ChangeHooks[i].iLastValue)
- {
- CallChangeCallbacks(&g_ChangeHooks[i], (void *)&g_ChangeHooks[i].iLastValue, (void *)&iCurrent);
- g_ChangeHooks[i].iLastValue = iCurrent;
- }
- break;
- }
- case PropType::Prop_Float:
- {
- edict_t * pEnt = gamehelpers->EdictOfIndex(g_ChangeHooks[i].objectID);
- CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt);
- float flCurrent = *(float *)((unsigned char *)pEntity + g_ChangeHooks[i].Offset);
- if (flCurrent != g_ChangeHooks[i].flLastValue)
- {
- CallChangeCallbacks(&g_ChangeHooks[i], (void *)&g_ChangeHooks[i].flLastValue, (void *)&flCurrent);
- g_ChangeHooks[i].flLastValue = flCurrent;
- }
- break;
- }
- case PropType::Prop_String:
- {
- edict_t * pEnt = gamehelpers->EdictOfIndex(g_ChangeHooks[i].objectID);
- CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt);
- const char * szCurrent = (const char *)((unsigned char *)pEntity + g_ChangeHooks[i].Offset);
- if (strcmp(szCurrent, g_ChangeHooks[i].cLastValue) != 0)
- {
- CallChangeCallbacks(&g_ChangeHooks[i], (void *)g_ChangeHooks[i].cLastValue, (void *)szCurrent);
- memset(g_ChangeHooks[i].cLastValue, 0, sizeof(g_ChangeHooks[i].cLastValue));
- strncpynull(g_ChangeHooks[i].cLastValue, szCurrent, sizeof(g_ChangeHooks[i].cLastValue));
- }
- break;
- }
- case PropType::Prop_Vector:
- {
- edict_t * pEnt = gamehelpers->EdictOfIndex(g_ChangeHooks[i].objectID);
- CBaseEntity * pEntity = gameents->EdictToBaseEntity(pEnt);
- Vector * pVec = (Vector *)((unsigned char *)pEntity + g_ChangeHooks[i].Offset);
- if (*pVec != g_ChangeHooks[i].vecLastValue)
- {
- CallChangeCallbacks(&g_ChangeHooks[i], (void *)&g_ChangeHooks[i].vecLastValue, (void *)pVec);
- g_ChangeHooks[i].vecLastValue = *pVec;
- }
- break;
- }
- default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, g_ChangeHooks[i].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 (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
- {
- switch(g_ChangeHooksGamerules[i].PropType)
- {
- case PropType::Prop_Int:
- {
- int iCurrent = *(int *)((unsigned char *)g_pGameRules + g_ChangeHooksGamerules[i].Offset);
- if (iCurrent != g_ChangeHooksGamerules[i].iLastValue)
- {
- CallChangeGamerulesCallbacks(&g_ChangeHooksGamerules[i], (void *)&g_ChangeHooksGamerules[i].iLastValue, (void *)&iCurrent);
- g_ChangeHooksGamerules[i].iLastValue = iCurrent;
- }
- break;
- }
- case PropType::Prop_Float:
- {
- float flCurrent = *(float *)((unsigned char *)g_pGameRules + g_ChangeHooksGamerules[i].Offset);
- if (flCurrent != g_ChangeHooksGamerules[i].flLastValue)
- {
- CallChangeGamerulesCallbacks(&g_ChangeHooksGamerules[i], (void *)&g_ChangeHooksGamerules[i].flLastValue, (void *)&flCurrent);
- g_ChangeHooksGamerules[i].flLastValue = flCurrent;
- }
- break;
- }
- case PropType::Prop_String:
- {
- const char * szCurrent = (const char *)((unsigned char *)g_pGameRules + g_ChangeHooksGamerules[i].Offset);
- if (strcmp(szCurrent, g_ChangeHooksGamerules[i].cLastValue) != 0)
- {
- CallChangeGamerulesCallbacks(&g_ChangeHooksGamerules[i], (void *)g_ChangeHooksGamerules[i].cLastValue, (void *)szCurrent);
- memset(g_ChangeHooks[i].cLastValue, 0, sizeof(g_ChangeHooks[i].cLastValue));
- strncpynull(g_ChangeHooks[i].cLastValue, szCurrent, sizeof(g_ChangeHooks[i].cLastValue));
- }
- break;
- }
- case PropType::Prop_Vector:
- {
- Vector * pVec = (Vector *)((unsigned char *)g_pGameRules + g_ChangeHooksGamerules[i].Offset);
- if (*pVec != g_ChangeHooksGamerules[i].vecLastValue)
- {
- CallChangeGamerulesCallbacks(&g_ChangeHooksGamerules[i], (void *)&g_ChangeHooksGamerules[i].vecLastValue, (void *)pVec);
- g_ChangeHooksGamerules[i].vecLastValue = *pVec;
- }
- break;
- }
- default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, g_ChangeHooksGamerules[i].pVar->GetName());
- }
+ CFrameSnapshotManager::s_callRemoveEntityReference = bintools->CreateCall(CFrameSnapshotManager::s_pfnRemoveEntityReference, CallConv_ThisCall, NULL, ¶ms[3], 1);
+ if (CFrameSnapshotManager::s_callRemoveEntityReference == NULL) {
+ LogError("Unable to create ICallWrapper for \"CFrameSnapshotManager::RemoveEntityReference\"!");
+ return;
}
}
- 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);
-
- bool bDetoursInited = false;
- CREATE_DETOUR(CGameServer_SendClientMessages, "CGameServer::SendClientMessages", bDetoursInited);
- CREATE_DETOUR(CGameClient_ShouldSendMessages, "CGameClient::ShouldSendMessages", bDetoursInited);
- CREATE_DETOUR(CFrameSnapshotManager_UsePreviouslySentPacket, "CFrameSnapshotManager::UsePreviouslySentPacket", bDetoursInited);
- CREATE_DETOUR(CFrameSnapshotManager_GetPreviouslySentPacket, "CFrameSnapshotManager::GetPreviouslySentPacket", bDetoursInited);
- CREATE_DETOUR(CFrameSnapshotManager_CreatePackedEntity, "CFrameSnapshotManager::CreatePackedEntity", bDetoursInited);
- // CREATE_DETOUR(CFrameSnapshotManager_RemoveEntityReference, "CFrameSnapshotManager::RemoveEntityReference", bDetoursInited);
- CREATE_DETOUR_STATIC(SV_ComputeClientPacks, "SV_ComputeClientPacks", bDetoursInited);
-
- if (!bDetoursInited)
- {
- snprintf(error, maxlength, "Could not create detours, see error log!");
- return false;
- }
-
- 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, "sendproxy13");
- plsys->AddPluginsListener(&g_SendProxyManager);
-
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- g_Hooks[i].pVar->SetProxyFn(g_Hooks[i].pRealProxy);
- }
-
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- g_HooksGamerules[i].pVar->SetProxyFn(g_HooksGamerules[i].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(CFrameSnapshotManager_UsePreviouslySentPacket);
- DESTROY_DETOUR(CFrameSnapshotManager_GetPreviouslySentPacket);
- DESTROY_DETOUR(CFrameSnapshotManager_CreatePackedEntity);
- // DESTROY_DETOUR(CFrameSnapshotManager_RemoveEntityReference);
- DESTROY_DETOUR(SV_ComputeClientPacks);
-
- gameconfs->CloseGameConfigFile(g_pGameConf);
- gameconfs->CloseGameConfigFile(g_pGameConfSDKTools);
-
- plsys->RemovePluginsListener(&g_SendProxyManager);
- if( g_pSDKHooks )
- {
- g_pSDKHooks->RemoveEntityListener(this);
- }
- delete g_pMyInterface;
-}
-
-void SendProxyManager::OnCoreMapEnd()
-{
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- UnhookProxyGamerules(i);
- i--;
- }
-
- g_pGameRulesProxyEdict = nullptr;
- g_iGameRulesProxyIndex = -1;
}
-void SendProxyManager::OnCoreMapStart(edict_t * pEdictList, int edictCount, int clientMax)
-{
- for (int i = 0; i < (sizeof(g_PlayersPackedGameRules) / sizeof(g_PlayersPackedGameRules[0])); ++i)
- {
- g_PlayersPackedGameRules[i] = INVALID_PACKED_ENTITY_HANDLE;
- }
-
- 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_pGameRulesProxyEdict)
- {
- g_iGameRulesProxyIndex = gamehelpers->IndexOfEdict(g_pGameRulesProxyEdict);
- }
-}
-
-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)
+bool SendProxyManager::QueryInterfaceDrop(SMInterface* pInterface)
{
- IPluginContext * pCtx = plugin->GetBaseContext();
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && ((IPluginFunction *)g_Hooks[i].sCallbackInfo.pCallback)->GetParentContext() == pCtx)
- {
- UnhookProxy(i);
- i--;
- }
- }
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && ((IPluginFunction *)g_HooksGamerules[i].sCallbackInfo.pCallback)->GetParentContext() == pCtx)
- {
- UnhookProxyGamerules(i);
- i--;
- }
- }
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
- {
- auto pCallbacks = g_ChangeHooks[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == CallBackType::Callback_PluginFunction && (IPluginContext *)(*pCallbacks)[j].pOwner == pCtx)
- {
- pCallbacks->Remove(j--);
- }
- }
- //else do not needed here
- if (!pCallbacks->Count())
- g_ChangeHooks.Remove(i);
- }
- for (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
- {
- auto pCallbacks = g_ChangeHooksGamerules[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == CallBackType::Callback_PluginFunction && (IPluginContext *)(*pCallbacks)[j].pOwner == pCtx)
- {
- pCallbacks->Remove(j--);
- }
- }
- //else do not needed here
- if (!pCallbacks->Count())
- g_ChangeHooksGamerules.Remove(i);
- }
-}
-
-//functions
+ std::string_view name = pInterface->GetInterfaceName();
+ if (name == SMINTERFACE_SDKHOOKS_NAME || name == SMINTERFACE_BINTOOLS_NAME)
+ return false;
-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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].objectID == hook.objectID)
- {
- //we don't care anymore
- //if (g_Hooks[i].pVar == hook.pVar)
- // return false;
- //else
- bEdictHooked = true;
- }
- }
- g_Hooks.AddToTail(hook);
- if (!bEdictHooked)
- g_vHookedEdicts.AddToTail(hook.pEnt);
return true;
}
-bool SendProxyManager::AddHookToListGamerules(SendPropHookGamerules hook)
+void SendProxyManager::NotifyInterfaceDrop(SMInterface* pInterface)
{
- //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.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == hook.pVar)
- return false;
- }*/
- g_HooksGamerules.AddToTail(hook);
- return true;
-}
+ std::string_view name = pInterface->GetInterfaceName();
-bool SendProxyManager::AddChangeHookToList(PropChangeHook sHook, CallBackInfo * pInfo)
-{
- decltype(&g_ChangeHooks[0]) pHookInfo = nullptr;
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
+ if (name == SMINTERFACE_SDKHOOKS_NAME)
{
- if (g_ChangeHooks[i].pVar == sHook.pVar)
- {
- pHookInfo = &g_ChangeHooks[i];
- 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->AddToTail(*pInfo);
+ sdkhooks = nullptr;
}
- else
+ else if (name == SMINTERFACE_BINTOOLS_NAME)
{
- 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;
- }
-
- CallBackInfo sCallInfo = *pInfo;
- sHook.vCallbacksInfo->AddToTail(sCallInfo);
- g_ChangeHooks.AddToTail(sHook);
+ bintools = nullptr;
}
- return true;
+
+ SDK_OnUnload();
}
-bool SendProxyManager::AddChangeHookToListGamerules(PropChangeHookGamerules sHook, CallBackInfo * pInfo)
+bool SendProxyManager::QueryRunning(char* error, size_t maxlength)
{
- decltype(&g_ChangeHooksGamerules[0]) pHookInfo = nullptr;
- for (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
- {
- if (g_ChangeHooksGamerules[i].pVar == sHook.pVar)
- {
- pHookInfo = &g_ChangeHooksGamerules[i];
- 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->AddToTail(*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;
- }
+ SM_CHECK_IFACE(SDKHOOKS, sdkhooks);
+ SM_CHECK_IFACE(BINTOOLS, bintools);
- CallBackInfo sCallInfo = *pInfo;
- sHook.vCallbacksInfo->AddToTail(sCallInfo);
- g_ChangeHooksGamerules.AddToTail(sHook);
- }
return true;
}
-void SendProxyManager::UnhookProxy(int i)
+bool SendProxyManager::RegisterConCommandBase(ConCommandBase* pVar)
{
- //if there are other hooks for this prop, don't change the proxy, just remove it from our list
- for (int j = 0; j < g_Hooks.Count(); j++)
- {
- if (g_Hooks[j].pVar == g_Hooks[i].pVar && i != j)
- {
- CallListenersForHookID(i);
- g_Hooks.Remove(i); //for others: this not a mistake
- return;
- }
- }
- for (int j = 0; j < g_vHookedEdicts.Count(); j++)
- if (g_vHookedEdicts[j] == g_Hooks[i].pEnt)
- {
- g_vHookedEdicts.Remove(j);
- break;
- }
- CallListenersForHookID(i);
- g_Hooks[i].pVar->SetProxyFn(g_Hooks[i].pRealProxy);
- g_Hooks.Remove(i);
+ // Notify metamod of ownership
+ return META_REGCVAR(pVar);
}
-void SendProxyManager::UnhookProxyGamerules(int i)
+void SendProxyManager::SDK_OnUnload()
{
- //if there are other hooks for this prop, don't change the proxy, just remove it from our list
- for (int j = 0; j < g_HooksGamerules.Count(); j++)
- {
- if (g_HooksGamerules[j].pVar == g_HooksGamerules[i].pVar && i != j)
- {
- CallListenersForHookIDGamerules(i);
- g_HooksGamerules.Remove(i);
- return;
- }
- }
- CallListenersForHookIDGamerules(i);
- g_HooksGamerules[i].pVar->SetProxyFn(g_HooksGamerules[i].pRealProxy);
- g_HooksGamerules.Remove(i);
-}
+ g_pSendPropHookManager->Clear();
+ ClientPacksDetour::Shutdown();
-void SendProxyManager::UnhookChange(int i, CallBackInfo * pInfo)
-{
- if (i < 0 || i >= g_ChangeHooks.Count())
- return;
- auto pCallbacks = g_ChangeHooks[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == pInfo->iCallbackType && (*pCallbacks)[j].pCallback == (void *)pInfo->pCallback)
- {
- pCallbacks->Remove(j--);
- }
- }
- //if there no any callbacks anymore, then remove all info about this hook
- if (!pCallbacks->Count())
- g_ChangeHooks.Remove(i);
-}
+ plsys->RemovePluginsListener(this);
+ playerhelpers->RemoveClientListener(this);
-void SendProxyManager::UnhookChangeGamerules(int i, CallBackInfo * pInfo)
-{
- if (i < 0 || i >= g_ChangeHooksGamerules.Count())
- return;
- auto pCallbacks = g_ChangeHooksGamerules[i].vCallbacksInfo;
- if (pCallbacks->Count())
+ if (sdkhooks)
{
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == pInfo->iCallbackType && (*pCallbacks)[j].pCallback == (void *)pInfo->pCallback)
- {
- pCallbacks->Remove(j--);
- }
+ sdkhooks->RemoveEntityListener(this);
}
- //if there no any callbacks anymore, then remove all info about this hook
- if (!pCallbacks->Count())
- g_ChangeHooksGamerules.Remove(i);
-}
-
-//callbacks
-//Change
+ ConVar_Unregister();
-void CallChangeCallbacks(PropChangeHook * pInfo, void * pOldValue, void * pNewValue)
-{
- for (int i = 0; i < pInfo->vCallbacksInfo->Count(); i++)
+ if (CFrameSnapshot::s_callReleaseReference != nullptr)
{
- auto sCallback = (*pInfo->vCallbacksInfo)[i];
- 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.x);
- vector[1][1] = sp_ftoc(pInfo->vecLastValue.y);
- vector[1][2] = sp_ftoc(pInfo->vecLastValue.z);
- 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;
- }
+ CFrameSnapshot::s_callReleaseReference->Destroy();
+ CFrameSnapshot::s_callReleaseReference = nullptr;
}
-}
-void CallChangeGamerulesCallbacks(PropChangeHookGamerules * pInfo, void * pOldValue, void * pNewValue)
-{
- for (int i = 0; i < pInfo->vCallbacksInfo->Count(); i++)
+ if (CFrameSnapshotManager::s_callCreateEmptySnapshot != nullptr)
{
- auto sCallback = (*pInfo->vCallbacksInfo)[i];
- 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.x);
- vector[1][1] = sp_ftoc(pInfo->vecLastValue.y);
- vector[1][2] = sp_ftoc(pInfo->vecLastValue.z);
- pCallBack->PushString(pInfo->pVar->GetName());
- pCallBack->PushArray(vector[1], 3);
- pCallBack->PushArray(vector[0], 3);
- pCallBack->PushCell(pInfo->Element);
- pCallBack->Execute(0);
- }
- break;
- }
- }
- break;
- }
+ CFrameSnapshotManager::s_callCreateEmptySnapshot->Destroy();
+ CFrameSnapshotManager::s_callCreateEmptySnapshot = nullptr;
}
-}
-
-//Proxy
-bool CallInt(SendPropHook &hook, int *ret, int iElement)
-{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
-
- switch (hook.sCallbackInfo.iCallbackType)
+ if (CFrameSnapshotManager::s_callRemoveEntityReference != nullptr)
{
- 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(iElement);
- 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 = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&iValue, hook.PropType, iElement);
- if (bChange)
- {
- *ret = iValue;
- return true;
- }
- break;
- }
+ CFrameSnapshotManager::s_callRemoveEntityReference->Destroy();
+ CFrameSnapshotManager::s_callRemoveEntityReference = nullptr;
}
- return false;
}
-bool CallIntGamerules(SendPropHookGamerules &hook, int *ret, int iElement)
+void SendProxyManager::OnEntityDestroyed(CBaseEntity* pEnt)
{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
-
- 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(iElement);
- 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 = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&iValue, hook.PropType, iElement);
- if (bChange)
- {
- *ret = iValue;
- return true;
- }
- break;
- }
- }
- return false;
+ int index = gamehelpers->EntityToBCompatRef(pEnt);
+ g_pSendPropHookManager->UnhookEntityAll(index);
}
-bool CallFloat(SendPropHook &hook, float *ret, int iElement)
+void SendProxyManager::OnClientDisconnected(int client)
{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
-
- 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(iElement);
- 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 = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&flValue, hook.PropType, iElement);
- if (bChange)
- {
- *ret = flValue;
- return true;
- }
- break;
- }
- }
- return false;
+ g_pSendPropHookManager->UnhookEntityAll(client);
+ ClientPacksDetour::OnClientDisconnect(client);
}
-bool CallFloatGamerules(SendPropHookGamerules &hook, float *ret, int iElement)
+void SendProxyManager::OnCoreMapEnd()
{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
-
- 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(iElement);
- 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 = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&flValue, hook.PropType, iElement);
- if (bChange)
- {
- *ret = flValue;
- return true;
- }
- break;
- }
- }
- return false;
+ g_pSendPropHookManager->Clear();
}
-bool CallString(SendPropHook &hook, char **ret, int iElement)
+bool SendProxyManager::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
+ // if (!engine->IsDedicatedServer())
+ // {
+ // ke::SafeStrcpy(error, maxlen, "Local server support is deprecated.");
+ // 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(iElement);
- 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 = pCallbacks->OnEntityPropProxyFunctionCalls(gameents->EdictToBaseEntity(hook.pEnt), hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)value, hook.PropType, iElement);
- if (bChange)
- {
- *ret = value;
- return true;
- }
- break;
- }
- }
- return false;
-}
-
-bool CallStringGamerules(SendPropHookGamerules &hook, char **ret, int iElement)
-{
- if (!g_bSVComputePacksDone)
- return false;
+ GET_V_IFACE_ANY(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION);
+ gpGlobals = ismm->GetCGlobals();
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
+ GET_CONVAR(sv_parallel_packentities);
- 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(iElement);
- 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 = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)value, hook.PropType, iElement);
- if (bChange)
- {
- *ret = value;
- return true;
- }
- break;
- }
- }
- return false;
+ return true;
}
-bool CallVector(SendPropHook &hook, Vector &vec, int iElement)
+void SendProxyManager::OnPluginUnloaded(IPlugin * plugin)
{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
-
- 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(iElement);
- 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 = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&vNewVec, hook.PropType, iElement);
- if (bChange)
- {
- vec.x = vNewVec.x;
- vec.y = vNewVec.y;
- vec.z = vNewVec.z;
- return true;
- }
- break;
- }
- }
- return false;
+ g_pSendPropHookManager->OnPluginUnloaded(plugin);
}
-bool CallVectorGamerules(SendPropHookGamerules &hook, Vector &vec, int iElement)
+CBaseEntity* GetGameRulesProxyEnt()
{
- if (!g_bSVComputePacksDone)
- return false;
-
- AUTO_LOCK_FM(g_WorkMutex);
-
- if (!hook.pVar->IsInsideArray())
- iElement = hook.Element;
-
- switch (hook.sCallbackInfo.iCallbackType)
+ static cell_t proxyEntRef = -1;
+ CBaseEntity *pProxy;
+ if (proxyEntRef == -1 || (pProxy = gamehelpers->ReferenceToEntity(proxyEntRef)) == NULL)
{
- 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(iElement);
- 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 = pCallbacks->OnGamerulesPropProxyFunctionCalls(hook.pVar, (CBasePlayer *)gamehelpers->ReferenceToEntity(g_iCurrentClientIndexInLoop + 1), (void *)&vNewVec, hook.PropType, iElement);
- if (bChange)
- {
- vec.x = vNewVec.x;
- vec.y = vNewVec.y;
- vec.z = vNewVec.z;
- return true;
- }
- break;
- }
+ pProxy = FindEntityByNetClass(playerhelpers->GetMaxClients(), g_szGameRulesProxy.c_str());
+ if (pProxy)
+ proxyEntRef = gamehelpers->EntityToReference(pProxy);
}
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].objectID == objectID && g_Hooks[i].pVar == pProp && pEnt == g_Hooks[i].pEnt && (!pProp->IsInsideArray() || g_Hooks[i].Element == iElement))
- {
- switch (g_Hooks[i].PropType)
- {
- case PropType::Prop_Int:
- {
- int result = *(int *)pData;
-
- if (CallInt(g_Hooks[i], &result, iElement))
- {
- long data = result;
- g_Hooks[i].pRealProxy(pProp, pStructBase, &data, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- case PropType::Prop_Float:
- {
- float result = *(float *)pData;
-
- if (CallFloat(g_Hooks[i], &result, iElement))
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- case PropType::Prop_String:
- {
- const char * result = (char*)pData;
- if (!result) //there can be null;
- result = "";
-
- if (CallString(g_Hooks[i], const_cast(&result), iElement))
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, result, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- case PropType::Prop_Vector:
- {
- Vector result = *(Vector *)pData;
-
- if (CallVector(g_Hooks[i], result, iElement))
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_Hooks[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, g_Hooks[i].pVar->GetName());
- }
- }
- }
- if (!bHandled)
- {
- //perhaps we aren't hooked, but we can still find the real proxy for this prop
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].pVar == pProp)
- {
- g_Hooks[i].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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == pProp && (!pProp->IsInsideArray() || g_HooksGamerules[i].Element == iElement))
- {
- switch (g_HooksGamerules[i].PropType)
- {
- case PropType::Prop_Int:
- {
- int result = *(int *)pData;
-
- if (CallIntGamerules(g_HooksGamerules[i], &result, iElement))
- {
- long data = result;
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, &data, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- case PropType::Prop_Float:
- {
- float result = *(float *)pData;
-
- if (CallFloatGamerules(g_HooksGamerules[i], &result, iElement))
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- case PropType::Prop_String:
- {
- const char *result = (char*)pData; //We need to use const because of C++11 restriction
- if (!result) //there can be null;
- result = "";
-
- if (CallStringGamerules(g_HooksGamerules[i], const_cast(&result), iElement))
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, result, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- case PropType::Prop_Vector:
- {
- Vector result = *(Vector *)pData;
-
- if (CallVectorGamerules(g_HooksGamerules[i], result, iElement))
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, &result, pOut, iElement, objectID);
- return; // If somebody already handled this call, do not call other hooks for this entity & prop
- }
- else
- {
- g_HooksGamerules[i].pRealProxy(pProp, pStructBase, pData, pOut, iElement, objectID);
- }
- bHandled = true;
- continue;
- }
- default: rootconsole->ConsolePrint("%s: SendProxy report: Unknown prop type (%s).", __func__, g_HooksGamerules[i].pVar->GetName());
- }
- }
- }
- if (!bHandled)
- {
- //perhaps we aren't hooked, but we can still find the real proxy for this prop
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == pProp)
- {
- g_HooksGamerules[i].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;
+ return pProxy;
}
-
-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)
-{
- strncpy(pDestination, pSource, szCount);
- pDestination[szCount - 1] = 0;
- return pDestination;
-}
\ No newline at end of file
diff --git a/extension/extension.h b/extension/extension.h
index b61f285..62d10bd 100644
--- a/extension/extension.h
+++ b/extension/extension.h
@@ -32,13 +32,6 @@
#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
@@ -50,176 +43,88 @@
#include
#include
#include "wrappers.h"
+#include
+#include
+#include
+#include "util.h"
-#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() { memset(this, 0, sizeof(CallBackInfo)); }
- CallBackInfo(const CallBackInfo & rObj) { memcpy(this, &rObj, sizeof(CallBackInfo)); }
- const CallBackInfo & operator=(const CallBackInfo & rObj) { return *(new CallBackInfo(rObj)); }
- void * pOwner; //Pointer to plugin context or IExtension *
- void * pCallback;
- CallBackType iCallbackType;
-};
-
-struct SendPropHook
-{
- SendPropHook() { vListeners = new CUtlVector(); }
- SendPropHook(const SendPropHook & rObj)
- {
- memcpy(this, &rObj, sizeof(SendPropHook));
- vListeners = new CUtlVector();
- *vListeners = *rObj.vListeners;
- }
- ~SendPropHook() { delete vListeners; }
- CallBackInfo sCallbackInfo;
- SendProp * pVar;
- edict_t * pEnt;
- SendVarProxyFn pRealProxy;
- int objectID;
- PropType PropType;
- int Offset;
- int Element{0};
- CUtlVector * vListeners;
-};
-
-struct SendPropHookGamerules
-{
- SendPropHookGamerules() { vListeners = new CUtlVector(); }
- SendPropHookGamerules(const SendPropHookGamerules & rObj)
- {
- memcpy(this, &rObj, sizeof(SendPropHookGamerules));
- vListeners = new CUtlVector();
- *vListeners = *rObj.vListeners;
- }
- ~SendPropHookGamerules() { delete vListeners; }
- CallBackInfo sCallbackInfo;
- SendProp * pVar;
- SendVarProxyFn pRealProxy;
- PropType PropType;
- int Element{0};
- CUtlVector * vListeners;
-};
-
-struct PropChangeHook
-{
- PropChangeHook() { vCallbacksInfo = new CUtlVector(); }
- PropChangeHook(const PropChangeHook & rObj)
- {
- memcpy(this, &rObj, sizeof(PropChangeHook));
- vCallbacksInfo = new CUtlVector();
- *vCallbacksInfo = *rObj.vCallbacksInfo;
- }
- ~PropChangeHook() { delete vCallbacksInfo; }
- union //unfortunately we MUST use union instead of std::variant cuz we should prevent libstdc++ linking in linux =|
- {
- int iLastValue;
- float flLastValue;
- Vector vecLastValue;
- char cLastValue[4096];
- };
- SendProp * pVar;
- PropType PropType;
- unsigned int Offset;
- int objectID;
- int Element{0};
- CUtlVector * vCallbacksInfo;
-};
-
-struct PropChangeHookGamerules
-{
- PropChangeHookGamerules() { vCallbacksInfo = new CUtlVector(); }
- PropChangeHookGamerules(const PropChangeHookGamerules & rObj)
- {
- memcpy(this, &rObj, sizeof(PropChangeHookGamerules));
- vCallbacksInfo = new CUtlVector();
- *vCallbacksInfo = *rObj.vCallbacksInfo;
- }
- ~PropChangeHookGamerules() { delete vCallbacksInfo; }
- union //unfortunately we MUST use union instead of std::variant cuz we should prevent libstdc++ linking in linux =|
- {
- int iLastValue;
- float flLastValue;
- Vector vecLastValue;
- char cLastValue[4096];
- };
- SendProp * pVar;
- PropType PropType;
- unsigned int Offset;
- int Element{0};
- CUtlVector * vCallbacksInfo;
-};
-
class SendProxyManager :
public SDKExtension,
public IPluginsListener,
+ public IConCommandBaseAccessor,
+ public IClientListener,
public ISMEntityListener
{
-public: //sm
+public:
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);
- void UnhookProxy(int i);
- void UnhookProxyGamerules(int i);
-
- void UnhookChange(int i, CallBackInfo * pInfo);
- void UnhookChangeGamerules(int i, CallBackInfo * pInfo);
- virtual int GetClientCount() const;
-public: // ISMEntityListener
- virtual void OnEntityDestroyed(CBaseEntity * pEntity);
+ /**
+ * @brief Asks the extension whether it's safe to remove an external
+ * interface it's using. If it's not safe, return false, and the
+ * extension will be unloaded afterwards.
+ *
+ * NOTE: It is important to also hook NotifyInterfaceDrop() in order to clean
+ * up resources.
+ *
+ * @param pInterface Pointer to interface being dropped. This
+ * pointer may be opaque, and it should not
+ * be queried using SMInterface functions unless
+ * it can be verified to match an existing
+ * pointer of known type.
+ * @return True to continue, false to unload this
+ * extension afterwards.
+ */
+ bool QueryInterfaceDrop(SMInterface* pInterface) override;
+
+ /**
+ * @brief Notifies the extension that an external interface it uses is being removed.
+ *
+ * @param pInterface Pointer to interface being dropped. This
+ * pointer may be opaque, and it should not
+ * be queried using SMInterface functions unless
+ * it can be verified to match an existing
+ */
+ void NotifyInterfaceDrop(SMInterface* pInterface) override;
+
+ /**
+ * @brief Return false to tell Core that your extension should be considered unusable.
+ *
+ * @param error Error buffer.
+ * @param maxlength Size of error buffer.
+ * @return True on success, false otherwise.
+ */
+ bool QueryRunning(char* error, size_t maxlength) override;
+
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
+
+public: //SDKExtension
+ void OnCoreMapEnd() override;
+
+public: //IPluginsListener
+ void OnPluginUnloaded(IPlugin * plugin) override;
+
+public: //IClientListener
+ void OnClientDisconnected(int client) override;
+
+public: //ISMEntityListener
+ void OnEntityDestroyed(CBaseEntity *pEntity) override;
+
+public: //IConCommandBaseAccessor
+ bool RegisterConCommandBase(ConCommandBase* pVar) override;
};
extern SendProxyManager g_SendProxyManager;
-extern IServerGameEnts * gameents;
-extern CUtlVector g_Hooks;
-extern CUtlVector g_HooksGamerules;
-extern CUtlVector g_ChangeHooks;
-extern CUtlVector g_ChangeHooksGamerules;
-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;
+extern ConVar *sv_parallel_packentities;
+extern CFrameSnapshotManager *framesnapshotmanager;
+extern void **g_ppLocalNetworkBackdoor;
+
+CBaseEntity *GetGameRulesProxyEnt();
#endif // _EXTENSION_H_INC_
diff --git a/extension/interfaceimpl.cpp b/extension/interfaceimpl.cpp
deleted file mode 100644
index 3f34731..0000000
--- a/extension/interfaceimpl.cpp
+++ /dev/null
@@ -1,803 +0,0 @@
-/**
- * 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);
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- //remove all listeners for this extension
- if (g_Hooks[i].vListeners->Count())
- for (int j = 0; j < g_Hooks[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_Hooks[i].vListeners)[j];
- if (info.m_pExtAPI == pExtAPI)
- g_Hooks[i].vListeners->Remove(j);
- }
- //remove hook for this extension
- if (g_Hooks[i].sCallbackInfo.iCallbackType == CallBackType::Callback_CPPCallbackInterface && static_cast(g_Hooks[i].sCallbackInfo.pOwner)->GetAPI() == pExtAPI)
- g_SendProxyManager.UnhookProxy(i);
- }
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- //remove all listeners for this extension
- if (g_HooksGamerules[i].vListeners->Count())
- for (int j = 0; j < g_HooksGamerules[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_HooksGamerules[i].vListeners)[j];
- if (info.m_pExtAPI == pExtAPI)
- g_HooksGamerules[i].vListeners->Remove(j);
- }
- //remove hook for this extension
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == CallBackType::Callback_CPPCallbackInterface && static_cast(g_HooksGamerules[i].sCallbackInfo.pOwner)->GetAPI() == pExtAPI)
- g_SendProxyManager.UnhookProxyGamerules(i);
- }
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].sCallbackInfo.pOwner == pExt)
- {
- bHookedAlready = true;
- break;
- }
- else if (g_Hooks[i].vListeners->Count())
- for (int j = 0; j < g_Hooks[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_Hooks[i].vListeners)[j];
- if (info.m_pExtAPI == pExt->GetAPI())
- {
- bHookedAlready = true;
- break;
- }
- }
- }
- if (!bHookedAlready)
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == pExt)
- {
- bHookedAlready = true;
- break;
- }
- else if (g_HooksGamerules[i].vListeners->Count())
- for (int j = 0; j < g_HooksGamerules[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_HooksGamerules[i].vListeners)[j];
- if (info.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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].sCallbackInfo.pOwner == pExt)
- {
- bHaveHooks = true;
- break;
- }
- else if (g_Hooks[i].vListeners->Count())
- for (int j = 0; j < g_Hooks[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_Hooks[i].vListeners)[j];
- if (info.m_pExtAPI == pExt->GetAPI())
- {
- bHaveHooks = true;
- break;
- }
- }
- }
- if (!bHaveHooks)
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == pExt)
- {
- bHaveHooks = true;
- break;
- }
- else if (g_HooksGamerules[i].vListeners->Count())
- for (int j = 0; j < g_HooksGamerules[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_HooksGamerules[i].vListeners)[j];
- if (info.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 CallListenersForHookID(int iHookID)
-{
- SendPropHook Info = g_Hooks[iHookID];
- for (int i = 0; i < Info.vListeners->Count(); i++)
- {
- ListenerCallbackInfo sInfo = (*Info.vListeners)[i];
- sInfo.m_pCallBack->OnEntityPropHookRemoved(gameents->EdictToBaseEntity(Info.pEnt), Info.pVar, Info.PropType, Info.sCallbackInfo.iCallbackType, Info.sCallbackInfo.pCallback);
- }
-}
-
-void CallListenersForHookIDGamerules(int iHookID)
-{
- SendPropHookGamerules Info = g_HooksGamerules[iHookID];
- for (int i = 0; i < Info.vListeners->Count(); i++)
- {
- ListenerCallbackInfo sInfo = (*Info.vListeners)[i];
- sInfo.m_pCallBack->OnGamerulesPropHookRemoved(Info.pVar, Info.PropType, Info.sCallbackInfo.iCallbackType, Info.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)
-{
- 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;
- bool bHookedAlready = false;
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].pVar == pProp)
- {
- hook.pRealProxy = g_Hooks[i].pRealProxy;
- bHookedAlready = true;
- break;
- }
- }
- if (!bHookedAlready)
- hook.pRealProxy = pProp->GetProxyFn();
- HookExtensionUnload(pExt);
- if (g_SendProxyManager.AddHookToList(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)
-{
- 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);
- return false;
-}
-
-bool SendProxyManagerInterfaceImpl::HookProxyGamerules(IExtension * pExt, SendProp * pProp, PropType iType, CallBackType iCallbackType, void * pCallback)
-{
- 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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == pProp)
- {
- hook.pRealProxy = g_HooksGamerules[i].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);
-
- //if this prop has been hooked already, don't set the proxy again
- HookExtensionUnload(pExt);
- if (bHookedAlready)
- {
- if (g_SendProxyManager.AddHookToListGamerules(hook))
- return true;
- UnhookExtensionUnload(pExt);
- return false;
- }
- if (g_SendProxyManager.AddHookToListGamerules(hook))
- {
- pProp->SetProxyFn(GlobalProxyGamerules);
- return true;
- }
- UnhookExtensionUnload(pExt);
- return false;
-}
-
-bool SendProxyManagerInterfaceImpl::HookProxyGamerules(IExtension * pExt, const char * pProp, PropType iType, 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 HookProxyGamerules(pExt, pSendProp, iType, iCallbackType, pCallback);
- 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);
- for (int i = 0; i < g_Hooks.Count(); i++)
- if (g_Hooks[i].sCallbackInfo.pOwner == pExt /*Allow to extension remove only its hooks*/ && pEdict == g_Hooks[i].pEnt && g_Hooks[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_Hooks[i].pVar->GetName(), pProp) && pCallback == g_Hooks[i].sCallbackInfo.pCallback)
- {
- g_SendProxyManager.UnhookProxy(i);
- UnhookExtensionUnload(pExt);
- return true;
- }
- 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;
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == pExt /*Allow to extension remove only its hooks*/ && g_HooksGamerules[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_HooksGamerules[i].pVar->GetName(), pProp) && pCallback == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- g_SendProxyManager.UnhookProxyGamerules(i);
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- if (pEdict == g_Hooks[i].pEnt && g_Hooks[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_Hooks[i].pVar->GetName(), pProp) && pCallback == g_Hooks[i].sCallbackInfo.pCallback)
- {
- ListenerCallbackInfo info;
- info.m_pExt = pExt;
- info.m_pExtAPI = pExt->GetAPI();
- info.m_pCallBack = pListener;
- HookExtensionUnload(pExt);
- g_Hooks[i].vListeners->AddToTail(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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_HooksGamerules[i].pVar->GetName(), pProp) && pCallback == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- ListenerCallbackInfo info;
- info.m_pExt = pExt;
- info.m_pExtAPI = pExt->GetAPI();
- info.m_pCallBack = pListener;
- HookExtensionUnload(pExt);
- g_HooksGamerules[i].vListeners->AddToTail(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 (int i = 0; i < g_Hooks.Count(); i++)
- if (g_Hooks[i].pEnt == pEdict && g_Hooks[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_Hooks[i].pVar->GetName(), pProp) && pCallback == g_Hooks[i].sCallbackInfo.pCallback)
- {
- for (int j = 0; j < g_Hooks[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_Hooks[i].vListeners)[j];
- if (info.m_pExt == pExt && info.m_pCallBack == pListener)
- {
- g_Hooks[i].vListeners->Remove(j);
- UnhookExtensionUnload(pExt);
- return true;
- }
- }
- }
- 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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_HooksGamerules[i].pVar->GetName(), pProp) && pCallback == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- for (int j = 0; j < g_HooksGamerules[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_HooksGamerules[i].vListeners)[j];
- if (info.m_pExt == pExt && info.m_pCallBack == pListener)
- {
- g_HooksGamerules[i].vListeners->Remove(j);
- UnhookExtensionUnload(pExt);
- return true;
- }
- }
- }
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].pVar == pPropElem)
- {
- hook.pRealProxy = g_Hooks[i].pRealProxy;
- bHookedAlready = true;
- break;
- }
- }
- if (!bHookedAlready)
- hook.pRealProxy = pPropElem->GetProxyFn();
- HookExtensionUnload(pExt);
- if (g_SendProxyManager.AddHookToList(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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == pProp)
- {
- hook.pRealProxy = g_HooksGamerules[i].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(hook))
- return true;
- UnhookExtensionUnload(pExt);
- return false;
- }
- if (g_SendProxyManager.AddHookToListGamerules(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);
- for (int i = 0; i < g_Hooks.Count(); i++)
- if (g_Hooks[i].sCallbackInfo.pOwner == pExt /*Allow to extension remove only its hooks*/ && g_Hooks[i].Element == iElement && pEdict == g_Hooks[i].pEnt && g_Hooks[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_Hooks[i].pVar->GetName(), pProp) && pCallback == g_Hooks[i].sCallbackInfo.pCallback)
- {
- g_SendProxyManager.UnhookProxy(i);
- 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;
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == pExt /*Allow to extension remove only its hooks*/ && g_HooksGamerules[i].Element == iElement && g_HooksGamerules[i].sCallbackInfo.iCallbackType == iCallbackType && !strcmp(g_HooksGamerules[i].pVar->GetName(), pProp) && pCallback == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- g_SendProxyManager.UnhookProxyGamerules(i);
- UnhookExtensionUnload(pExt);
- return true;
- }
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- if (pEdict == g_Hooks[i].pEnt && g_Hooks[i].sCallbackInfo.iCallbackType == iCallbackType && g_Hooks[i].Element == iElement && !strcmp(g_Hooks[i].pVar->GetName(), pProp) && pCallback == g_Hooks[i].sCallbackInfo.pCallback)
- {
- ListenerCallbackInfo info;
- info.m_pExt = pExt;
- info.m_pExtAPI = pExt->GetAPI();
- info.m_pCallBack = pListener;
- HookExtensionUnload(pExt);
- g_Hooks[i].vListeners->AddToTail(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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == iCallbackType && g_HooksGamerules[i].Element == iElement && !strcmp(g_HooksGamerules[i].pVar->GetName(), pProp) && pCallback == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- ListenerCallbackInfo info;
- info.m_pExt = pExt;
- info.m_pExtAPI = pExt->GetAPI();
- info.m_pCallBack = pListener;
- HookExtensionUnload(pExt);
- g_HooksGamerules[i].vListeners->AddToTail(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 (int i = 0; i < g_Hooks.Count(); i++)
- if (g_Hooks[i].pEnt == pEdict && g_Hooks[i].sCallbackInfo.iCallbackType == iCallbackType && g_Hooks[i].Element == iElement && !strcmp(g_Hooks[i].pVar->GetName(), pProp) && pCallback == g_Hooks[i].sCallbackInfo.pCallback)
- {
- for (int j = 0; j < g_Hooks[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_Hooks[i].vListeners)[j];
- if (info.m_pExt == pExt && info.m_pCallBack == pListener)
- {
- g_Hooks[i].vListeners->Remove(j);
- UnhookExtensionUnload(pExt);
- return true;
- }
- }
- }
- 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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == iCallbackType && g_HooksGamerules[i].Element == iElement && !strcmp(g_HooksGamerules[i].pVar->GetName(), pProp) && pCallback == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- for (int j = 0; j < g_HooksGamerules[i].vListeners->Count(); j++)
- {
- ListenerCallbackInfo info = (*g_HooksGamerules[i].vListeners)[j];
- if (info.m_pExt == pExt && info.m_pCallBack == pListener)
- {
- g_HooksGamerules[i].vListeners->Remove(j);
- UnhookExtensionUnload(pExt);
- return true;
- }
- }
- }
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- if (g_Hooks[i].sCallbackInfo.pOwner == (void *)pExt /*Check if is hooked for this extension*/ && g_Hooks[i].pEnt == pEdict && !strcmp(pProp, g_Hooks[i].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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == (void *)pExt /*Check if is hooked for this extension*/ && !strcmp(pProp, g_HooksGamerules[i].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 (int i = 0; i < g_Hooks.Count(); i++)
- if (g_Hooks[i].sCallbackInfo.pOwner == (void *)pExt /*Check if is hooked for this extension*/ && g_Hooks[i].pEnt == pEdict && g_Hooks[i].Element == iElement && !strcmp(pProp, g_Hooks[i].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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == (void *)pExt /*Check if is hooked for this extension*/ && g_HooksGamerules[i].Element == iElement && !strcmp(pProp, g_HooksGamerules[i].pVar->GetName()))
- return true;
- return false;
-}
\ No newline at end of file
diff --git a/extension/interfaceimpl.h b/extension/interfaceimpl.h
deleted file mode 100644
index 0e812e6..0000000
--- a/extension/interfaceimpl.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * 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 CallListenersForHookID(int iID);
-void CallListenersForHookIDGamerules(int iID);
-
-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 *) override;
- virtual bool HookProxy(IExtension *, const char *, CBaseEntity *, PropType, CallBackType, void *) override;
- virtual bool HookProxyGamerules(IExtension *, SendProp *, PropType, CallBackType, void *) override;
- virtual bool HookProxyGamerules(IExtension *, const char *, PropType, CallBackType, void *) 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
\ No newline at end of file
diff --git a/extension/natives.cpp b/extension/natives.cpp
index b019b1a..55b63ee 100644
--- a/extension/natives.cpp
+++ b/extension/natives.cpp
@@ -30,998 +30,277 @@
*/
#include "natives.h"
+#include "sendprop_hookmanager.h"
-static cell_t Native_UnhookPropChange(IPluginContext * pContext, const cell_t * params)
+static bool IsPropValid(const SendProp *prop, PropType type)
{
- 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]);
-
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
- {
- if (g_ChangeHooks[i].objectID == entity && g_ChangeHooks[i].pVar == info.prop)
- {
- CallBackInfo sInfo;
- sInfo.pCallback = callback;
- sInfo.pOwner = (void *)pContext;
- sInfo.iCallbackType = CallBackType::Callback_PluginFunction;
- g_SendProxyManager.UnhookChange(i, &sInfo);
- 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);
-
- for (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
+ switch (type)
{
- if (g_ChangeHooksGamerules[i].pVar == info.prop)
- {
- CallBackInfo sInfo;
- sInfo.pCallback = callback;
- sInfo.pOwner = (void *)pContext;
- sInfo.iCallbackType = CallBackType::Callback_PluginFunction;
- g_SendProxyManager.UnhookChangeGamerules(i, &sInfo);
- 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]);
+ case PropType::Prop_Int:
+ return prop->GetType() == DPT_Int;
- 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);
+ case PropType::Prop_EHandle:
+ return prop->GetType() == DPT_Int && prop->m_nBits == NUM_NETWORKED_EHANDLE_BITS;
- sm_sendprop_info_t info;
- gamehelpers->FindSendPropInfo(sc->GetName(), name, &info);
- SendProp * pProp = info.prop;
+ case PropType::Prop_Float:
+ return prop->GetType() == DPT_Float;
- if (!pProp)
- return pContext->ThrowNativeError("Could not find prop %s", name);
+ case PropType::Prop_Vector:
+ return prop->GetType() == DPT_Vector || prop->GetType() == DPT_VectorXY;
- IPluginFunction * callback = nullptr;
- PropType propType = PropType::Prop_Max;
- if (bSafeCheck)
- {
- propType = static_cast(params[3]);
- callback = pContext->GetFunctionById(params[4]);
+ case PropType::Prop_String:
+ return prop->GetType() == DPT_String;
}
- 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(hook, &sCallInfo))
- return pContext->ThrowNativeError("Entity %d isn't valid", entity);
- return 1;
+ return false;
}
-static cell_t Native_HookPropChangeGameRules(IPluginContext * pContext, const cell_t * params)
+void UTIL_FindSendProp(SendProp* &ret, IPluginContext *pContext, int index, const char* propname, bool checkType, PropType type, int element)
{
- 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;
+ edict_t *edict = UTIL_EdictOfIndex(index);
+ if (!edict)
+ return pContext->ReportError("Invalid edict index (%d)", index);
- 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(hook, &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();
-
+ ServerClass *sc = edict->GetNetworkable()->GetServerClass();
if (!sc)
- return pContext->ThrowNativeError("Cannot find ServerClass for entity %d", entity);
+ return pContext->ReportError("Cannot find ServerClass for entity %d", index);
sm_sendprop_info_t info;
- gamehelpers->FindSendPropInfo(sc->GetName(), name, &info);
- SendProp * pProp = info.prop;
+ gamehelpers->FindSendPropInfo(sc->GetName(), propname, &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);
- }
+ return pContext->ReportError("Could not find prop %s", propname);
- 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;
- bool bHookedAlready = false;
- for (int i = 0; i < g_Hooks.Count(); i++)
+ if (pProp->GetType() == DPT_Array)
{
- if (g_Hooks[i].pVar == pProp)
- {
- hook.pRealProxy = g_Hooks[i].pRealProxy;
- bHookedAlready = true;
- break;
- }
+ pProp = pProp->GetArrayProp();
+ if (!pProp)
+ return pContext->ReportError("Unexpected: Prop %s is an array but has no array prop", propname);
+
+ if (element < 0 || element > info.prop->GetNumElements())
+ return pContext->ReportError("Unable to find element %d of prop %s", element, propname);
}
- 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)
+ else if (pProp->GetType() == DPT_DataTable)
{
- if (g_SendProxyManager.AddHookToList(hook))
- return 1;
- return 0;
- }
- if (g_SendProxyManager.AddHookToList(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->GetDataTable())
+ return pContext->ReportError("Unexpected: Prop %s is a datatable but has no data table", propname);
- if (!pProp)
- return pContext->ThrowNativeError("Could not find prop %s", name);
+ pProp = pProp->GetDataTable()->GetProp(element);
+ if (!pProp)
+ return pContext->ReportError("Unable to find element %d of prop %s", element, propname);
+ }
- PropType propType = static_cast(params[2]);
- if (!IsPropValid(pProp, propType))
- switch (propType)
+ if (checkType && !IsPropValid(pProp, type))
+ {
+ switch (type)
{
case PropType::Prop_Int:
- return pContext->ThrowNativeError("Prop %s is not an int!", pProp->GetName());
+ return pContext->ReportError("Prop %s is not an int!", propname);
case PropType::Prop_Float:
- return pContext->ThrowNativeError("Prop %s is not a float!", pProp->GetName());
+ return pContext->ReportError("Prop %s is not a float!", propname);
case PropType::Prop_String:
- return pContext->ThrowNativeError("Prop %s is not a string!", pProp->GetName());
+ return pContext->ReportError("Prop %s is not a string!", propname);
case PropType::Prop_Vector:
- return pContext->ThrowNativeError("Prop %s is not a vector!", pProp->GetName());
+ return pContext->ReportError("Prop %s is not a vector!", propname);
+ case PropType::Prop_EHandle:
+ return pContext->ReportError("Prop %s is not an EHandle!", propname);
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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == pProp)
- {
- hook.pRealProxy = g_HooksGamerules[i].pRealProxy;
- bHookedAlready = true;
- break;
+ return pContext->ReportError("Unsupported prop type %d", type);
}
}
- 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.AddHookToListGamerules(hook))
- return 1;
- return 0;
- }
- if (g_SendProxyManager.AddHookToListGamerules(hook))
- {
- pProp->SetProxyFn(GlobalProxyGamerules);
- return 1;
- }
- return 0;
+
+ ret = pProp;
}
-static cell_t Native_HookArrayProp(IPluginContext * pContext, const cell_t * params)
+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 * 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);
-
- int element = params[3];
- PropType propType = static_cast(params[4]);
- SendProp *pProp = NULL;
- switch (info.prop->GetType())
+ constexpr cell_t PARAM_COUNT = 5;
+ if (params[0] < PARAM_COUNT)
{
- case DPT_Array:
- {
- pProp = info.prop->GetArrayProp();
- if (!pProp)
- return pContext->ThrowNativeError("Prop %s does not contain any elements", propName);
-
- if (element > info.prop->GetNumElements())
- return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName());
-
- 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);
- }
-
- break;
- }
-
- case DPT_DataTable:
- {
- SendTable * st = info.prop->GetDataTable();
-
- if (!st)
- return pContext->ThrowNativeError("Prop %s does not contain any elements", propName);
+ pContext->ReportError("Expected %d params, found %d", PARAM_COUNT, params[0]);
+ return false;
+ }
- pProp = st->GetProp(element);
- if (!pProp)
- return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName());
+ char *propname = nullptr;
+ SendProp *pProp = nullptr;
- 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);
- }
+ int index = params[1];
+ pContext->LocalToString(params[2], &propname);
+ PropType type = static_cast(params[3]);
+ IPluginFunction *pFunc = pContext->GetFunctionById(params[4]);
+ int element = params[5];
- break;
- }
-
- default:
- return pContext->ThrowNativeError("Prop %s does not contain any elements", propName);
- }
-
- 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 (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].pVar == pProp)
- {
- hook.pRealProxy = g_Hooks[i].pRealProxy;
- bHookedAlready = true;
- break;
- }
- }
- if (!bHookedAlready)
- hook.pRealProxy = pProp->GetProxyFn();
- hook.PropType = propType;
- hook.pVar = pProp;
+ UTIL_FindSendProp(pProp, pContext, index, propname, true, type, element);
+ if (pProp == nullptr)
+ return false;
- if (bHookedAlready)
- {
- if (g_SendProxyManager.AddHookToList(hook))
- return 1;
- return 0;
- }
- if (g_SendProxyManager.AddHookToList(hook))
- {
- pProp->SetProxyFn(GlobalProxy);
- return 1;
- }
- return 0;
-}
+ if (g_pSendPropHookManager->IsEntityHooked(index, pProp, element, pFunc))
+ return true;
-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]);
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- //we check callback here, so, we do not need to check owner
- if (g_Hooks[i].Element == element && g_Hooks[i].sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && g_Hooks[i].PropType == propType && g_Hooks[i].sCallbackInfo.pCallback == (void *)callback && !strcmp(g_Hooks[i].pVar->GetName(), propName) && g_Hooks[i].objectID == entity)
- {
- g_SendProxyManager.UnhookProxy(i);
- return 1;
- }
- }
- return 0;
+ return g_pSendPropHookManager->HookEntity(index, pProp, element, type, pFunc);
}
static cell_t Native_Unhook(IPluginContext * pContext, const cell_t * params)
{
- char * propName;
- pContext->LocalToString(params[2], &propName);
- IPluginFunction * pFunction = pContext->GetFunctionById(params[3]);
- for (int i = 0; i < g_Hooks.Count(); i++)
+ constexpr cell_t PARAM_COUNT = 4;
+ if (params[0] < PARAM_COUNT)
{
- //we check callback here, so, we do not need to check owner
- if (params[1] == g_Hooks[i].objectID && g_Hooks[i].sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && strcmp(g_Hooks[i].pVar->GetName(), propName) == 0 && (void *)pFunction == g_Hooks[i].sCallbackInfo.pCallback)
- {
- g_SendProxyManager.UnhookProxy(i);
- return 1;
- }
+ pContext->ReportError("Expected %d params, found %d", PARAM_COUNT, params[0]);
+ return false;
}
- 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]);
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- //we check callback here, so, we do not need to check owner
- if (g_HooksGamerules[i].sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && strcmp(g_HooksGamerules[i].pVar->GetName(), propName) == 0 && (void *)pFunction == g_HooksGamerules[i].sCallbackInfo.pCallback)
- {
- g_SendProxyManager.UnhookProxyGamerules(i);
- return 1;
- }
- }
- return 0;
-}
+ char *propname = nullptr;
+ SendProp *pProp = nullptr;
-static cell_t Native_IsHooked(IPluginContext * pContext, const cell_t * params)
-{
- int objectID = params[1];
- char * propName;
- pContext->LocalToString(params[2], &propName);
+ int index = params[1];
+ pContext->LocalToString(params[2], &propname);
+ IPluginFunction *pFunc = pContext->GetFunctionById(params[3]);
+ int element = params[4];
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_Hooks[i].objectID == objectID && g_Hooks[i].sCallbackInfo.pOwner == (void *)pContext && strcmp(propName, g_Hooks[i].pVar->GetName()) == 0)
- return 1;
- }
- return 0;
-}
+ UTIL_FindSendProp(pProp, pContext, index, propname, false, PropType::Prop_Max, element);
+ if (pProp == nullptr)
+ return false;
-static cell_t Native_IsHookedGameRules(IPluginContext * pContext, const cell_t * params)
-{
- char * propName;
- pContext->LocalToString(params[1], &propName);
+ if (!g_pSendPropHookManager->IsEntityHooked(index, pProp, element, pFunc))
+ return false;
- for (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == (void *)pContext && strcmp(propName, g_HooksGamerules[i].pVar->GetName()) == 0)
- return 1;
- }
- return 0;
+ g_pSendPropHookManager->UnhookEntity(index, pProp, element, pFunc);
+ return true;
}
-static cell_t Native_HookArrayPropGamerules(IPluginContext * pContext, const cell_t * params)
+static cell_t Native_IsHooked(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);
-
- int element = params[2];
- PropType propType = static_cast(params[3]);
- SendProp *pProp = NULL;
- switch (info.prop->GetType())
+ constexpr cell_t PARAM_COUNT = 4;
+ if (params[0] < PARAM_COUNT)
{
- case DPT_Array:
- {
- pProp = info.prop->GetArrayProp();
- if (!pProp)
- return pContext->ThrowNativeError("Prop %s does not contain any elements", propName);
-
- if (element > info.prop->GetNumElements())
- return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName());
-
- 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);
- }
-
- break;
- }
-
- case DPT_DataTable:
- {
- SendTable * st = info.prop->GetDataTable();
+ pContext->ReportError("Expected %d params, found %d", PARAM_COUNT, params[0]);
+ return false;
+ }
- if (!st)
- return pContext->ThrowNativeError("Prop %s does not contain any elements", propName);
+ char *propname = nullptr;
+ SendProp *pProp = nullptr;
- pProp = st->GetProp(element);
- if (!pProp)
- return pContext->ThrowNativeError("Could not find element %d in %s", element, info.prop->GetName());
+ int index = params[1];
+ pContext->LocalToString(params[2], &propname);
+ IPluginFunction *pFunc = pContext->GetFunctionById(params[3]);
+ int element = 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);
- }
+ UTIL_FindSendProp(pProp, pContext, index, propname, false, PropType::Prop_Max, element);
+ if (pProp == nullptr)
+ return false;
- break;
- }
-
- default:
- return pContext->ThrowNativeError("Prop %s does not contain any elements", propName);
- }
-
- 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 (int i = 0; i < g_HooksGamerules.Count(); i++)
- {
- if (g_HooksGamerules[i].pVar == pProp)
- {
- hook.pRealProxy = g_HooksGamerules[i].pRealProxy;
- bHookedAlready = true;
- break;
- }
- }
- if (!bHookedAlready)
- hook.pRealProxy = pProp->GetProxyFn();
- hook.PropType = propType;
- hook.pVar = pProp;
-
- if (bHookedAlready)
- {
- if (g_SendProxyManager.AddHookToListGamerules(hook))
- return 1;
- return 0;
- }
- if (g_SendProxyManager.AddHookToListGamerules(hook))
- {
- pProp->SetProxyFn(GlobalProxyGamerules);
- 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]);
- for (int i = 0; i < g_Hooks.Count(); i++)
- {
- if (g_HooksGamerules[i].Element == iElement && g_HooksGamerules[i].sCallbackInfo.iCallbackType == CallBackType::Callback_PluginFunction && g_HooksGamerules[i].PropType == iPropType && g_HooksGamerules[i].sCallbackInfo.pCallback == (void *)pFunction && !strcmp(g_HooksGamerules[i].pVar->GetName(), propName))
- {
- g_SendProxyManager.UnhookProxyGamerules(i);
- return 1;
- }
- }
- return 0;
+ return g_pSendPropHookManager->IsEntityHooked(index, pProp, element, pFunc);
}
-static cell_t Native_IsHookedArray(IPluginContext * pContext, const cell_t * params)
+static cell_t Native_HookGameRules(IPluginContext * pContext, const cell_t * params)
{
- int objectID = params[1];
- char * propName;
- pContext->LocalToString(params[2], &propName);
- int iElement = params[3];
-
- for (int i = 0; i < g_Hooks.Count(); i++)
+ constexpr cell_t PARAM_COUNT = 4;
+ if (params[0] < PARAM_COUNT)
{
- if (g_Hooks[i].sCallbackInfo.pOwner == (void *)pContext && g_Hooks[i].objectID == objectID && g_Hooks[i].Element == iElement && strcmp(propName, g_Hooks[i].pVar->GetName()) == 0)
- return 1;
+ pContext->ReportError("Expected %d params, found %d", PARAM_COUNT, params[0]);
+ return false;
}
- 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 (int i = 0; i < g_HooksGamerules.Count(); i++)
+ CBaseEntity *pGameRulesProxy = GetGameRulesProxyEnt();
+ if (!pGameRulesProxy)
{
- if (g_HooksGamerules[i].sCallbackInfo.pOwner == (void *)pContext && g_HooksGamerules[i].Element == iElement && strcmp(propName, g_HooksGamerules[i].pVar->GetName()) == 0)
- return 1;
+ pContext->ReportError("MGameRulesProxy entity not found. (Maybe try hooking later after \"round_start\").");
+ return false;
}
- 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]);
+ char *propname = nullptr;
+ SendProp *pProp = nullptr;
+ int index = gamehelpers->EntityToBCompatRef(pGameRulesProxy);
- int entity = params[1];
- char * name;
- edict_t * pEnt = gamehelpers->EdictOfIndex(entity);
- pContext->LocalToString(params[2], &name);
- ServerClass * sc = pEnt->GetNetworkable()->GetServerClass();
+ pContext->LocalToString(params[1], &propname);
+ PropType type = static_cast(params[2]);
+ IPluginFunction *pFunc = pContext->GetFunctionById(params[3]);
+ int element = params[4];
- 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);
- }
+ UTIL_FindSendProp(pProp, pContext, index, propname, true, type, element);
+ if (pProp == nullptr)
+ return false;
- 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(hook, &sCallInfo))
- return pContext->ThrowNativeError("Entity %d isn't valid", entity);
- return 1;
+ if (g_pSendPropHookManager->IsEntityHooked(index, pProp, element, pFunc))
+ return true;
+
+ return g_pSendPropHookManager->HookEntity(index, pProp, element, type, pFunc);
}
-static cell_t Native_UnhookPropChangeArray(IPluginContext * pContext, const cell_t * params)
+static cell_t Native_UnhookGameRules(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]);
-
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
+ constexpr cell_t PARAM_COUNT = 3;
+ if (params[0] < PARAM_COUNT)
{
- if (g_ChangeHooks[i].objectID == entity && g_ChangeHooks[i].pVar == info.prop)
- {
- CallBackInfo sInfo;
- sInfo.pCallback = callback;
- sInfo.pOwner = (void *)pContext;
- sInfo.iCallbackType = CallBackType::Callback_PluginFunction;
- g_SendProxyManager.UnhookChange(i, &sInfo);
- break;
- }
+ pContext->ReportError("Expected %d params, found %d", PARAM_COUNT, params[0]);
+ return false;
}
- 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;
+ CBaseEntity *pGameRulesProxy = GetGameRulesProxyEnt();
+ if (!pGameRulesProxy)
+ {
+ pContext->ReportError("MGameRulesProxy entity not found. (Maybe try hooking later after \"round_start\").");
+ return false;
+ }
- gamehelpers->FindSendPropInfo(g_szGameRulesProxy, name, &info);
+ char *propname = nullptr;
+ SendProp *pProp = nullptr;
+ int index = gamehelpers->EntityToBCompatRef(pGameRulesProxy);
- if (!info.prop)
- return pContext->ThrowNativeError("Could not find prop %s", name);
-
- SendTable * st = info.prop->GetDataTable();
+ pContext->LocalToString(params[1], &propname);
+ IPluginFunction *pFunc = pContext->GetFunctionById(params[2]);
+ int element = params[3];
- if (!st)
- return pContext->ThrowNativeError("Prop %s does not contain any elements", name);
-
+ UTIL_FindSendProp(pProp, pContext, index, propname, false, PropType::Prop_Max, element);
- int element = params[2];
- SendProp * pProp = st->GetProp(element);
+ if (pProp == nullptr)
+ return false;
- 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);
- }
+ if (!g_pSendPropHookManager->IsEntityHooked(index, pProp, element, pFunc))
+ return false;
- 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(hook, &sCallInfo))
- return pContext->ThrowNativeError("Prop type %d isn't valid", pProp->GetType()); //should never happen
- return 1;
+ g_pSendPropHookManager->UnhookEntity(index, pProp, element, pFunc);
+ return true;
}
-static cell_t Native_UnhookPropChangeArrayGameRules(IPluginContext * pContext, const cell_t * params)
+static cell_t Native_IsHookedGameRules(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]);
-
- for (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
+ constexpr cell_t PARAM_COUNT = 3;
+ if (params[0] < PARAM_COUNT)
{
- if (g_ChangeHooksGamerules[i].pVar == info.prop)
- {
- CallBackInfo sInfo;
- sInfo.pCallback = callback;
- sInfo.pOwner = (void *)pContext;
- sInfo.iCallbackType = CallBackType::Callback_PluginFunction;
- g_SendProxyManager.UnhookChangeGamerules(i, &sInfo);
- break;
- }
+ pContext->ReportError("Expected %d params, found %d", PARAM_COUNT, params[0]);
+ return false;
}
- 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 (int i = 0; i < g_ChangeHooks.Count(); i++)
+ CBaseEntity *pGameRulesProxy = GetGameRulesProxyEnt();
+ if (!pGameRulesProxy)
{
- if (g_ChangeHooks[i].objectID == objectID && strcmp(propName, g_ChangeHooks[i].pVar->GetName()) == 0)
- {
- auto pCallbacks = g_ChangeHooks[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == CallBackType::Callback_PluginFunction && (*pCallbacks)[j].pOwner == (void *)pContext)
- {
- return 1;
- }
- }
- break;
- }
+ pContext->ReportError("MGameRulesProxy entity not found. (Maybe try hooking later after \"round_start\").");
+ return false;
}
- return 0;
-}
-static cell_t Native_IsPropChangeHookedGameRules(IPluginContext * pContext, const cell_t * params)
-{
- char * propName;
- pContext->LocalToString(params[1], &propName);
+ char *propname = nullptr;
+ SendProp *pProp = nullptr;
+ int index = gamehelpers->EntityToBCompatRef(pGameRulesProxy);
- for (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
- {
- if (strcmp(propName, g_ChangeHooksGamerules[i].pVar->GetName()) == 0)
- {
- auto pCallbacks = g_ChangeHooksGamerules[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == CallBackType::Callback_PluginFunction && (*pCallbacks)[j].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);
+ pContext->LocalToString(params[1], &propname);
+ IPluginFunction *pFunc = pContext->GetFunctionById(params[2]);
int element = params[3];
- for (int i = 0; i < g_ChangeHooks.Count(); i++)
- {
- if (g_ChangeHooks[i].Element == element && g_ChangeHooks[i].objectID == objectID && strcmp(propName, g_ChangeHooks[i].pVar->GetName()) == 0)
- {
- auto pCallbacks = g_ChangeHooks[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == CallBackType::Callback_PluginFunction && (*pCallbacks)[j].pOwner == (void *)pContext)
- {
- return 1;
- }
- }
- break;
- }
- }
- return 0;
-}
+ UTIL_FindSendProp(pProp, pContext, index, propname, false, PropType::Prop_Max, element);
-static cell_t Native_IsPropChangeArrayHookedGameRules(IPluginContext * pContext, const cell_t * params)
-{
- char * propName;
- pContext->LocalToString(params[1], &propName);
- int element = params[2];
+ if (pProp == nullptr)
+ return false;
- for (int i = 0; i < g_ChangeHooksGamerules.Count(); i++)
- {
- if (g_ChangeHooksGamerules[i].Element == element && strcmp(propName, g_ChangeHooksGamerules[i].pVar->GetName()) == 0)
- {
- auto pCallbacks = g_ChangeHooksGamerules[i].vCallbacksInfo;
- if (pCallbacks->Count())
- {
- for (int j = 0; j < pCallbacks->Count(); j++)
- if ((*pCallbacks)[j].iCallbackType == CallBackType::Callback_PluginFunction && (*pCallbacks)[j].pOwner == (void *)pContext)
- {
- return 1;
- }
- }
- break;
- }
- }
- return 0;
+ return g_pSendPropHookManager->IsEntityHooked(index, pProp, element, pFunc);
}
const sp_nativeinfo_t g_MyNatives[] = {
- {"SendProxy_Hook", Native_Hook},
+ {"SendProxy_HookEntity", Native_Hook},
{"SendProxy_HookGameRules", Native_HookGameRules},
- {"SendProxy_HookArrayProp", Native_HookArrayProp},
- {"SendProxy_UnhookArrayProp", Native_UnhookArrayProp},
- {"SendProxy_Unhook", Native_Unhook},
+ {"SendProxy_UnhookEntity", Native_Unhook},
{"SendProxy_UnhookGameRules", Native_UnhookGameRules},
- {"SendProxy_IsHooked", Native_IsHooked},
+ {"SendProxy_IsHookedEntity", 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}
-};
\ No newline at end of file
+ {nullptr, nullptr}};
\ No newline at end of file
diff --git a/extension/sdk/memoverride.cpp b/extension/sdk/memoverride.cpp
new file mode 100644
index 0000000..14704c6
--- /dev/null
+++ b/extension/sdk/memoverride.cpp
@@ -0,0 +1,1589 @@
+//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Insert this file into all projects using the memory system
+// It will cause that project to use the shader memory allocator
+//
+// $NoKeywords: $
+//=============================================================================//
+
+
+#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
+
+#undef PROTECTED_THINGS_ENABLE // allow use of _vsnprintf
+
+#if defined( _WIN32 ) && !defined( _X360 )
+#define WIN_32_LEAN_AND_MEAN
+#include
+#endif
+
+#include "tier0/dbg.h"
+#include "tier0/memalloc.h"
+#include
+#include
+#include "memdbgoff.h"
+
+// Tags this DLL as debug
+#if defined( _DEBUG ) && !defined( _X360 )
+DLL_EXPORT void BuiltDebug() {}
+#endif
+
+#ifdef _WIN32
+// ARG: crtdbg is necessary for certain definitions below,
+// but it also redefines malloc as a macro in release.
+// To disable this, we gotta define _DEBUG before including it.. BLEAH!
+#define _DEBUG 1
+#include "crtdbg.h"
+#ifdef NDEBUG
+#undef _DEBUG
+#endif
+
+// Turn this back off in release mode.
+#ifdef NDEBUG
+#undef _DEBUG
+#endif
+#elif _LINUX
+#define __cdecl
+#endif
+
+#if defined( _WIN32 ) && !defined( _X360 )
+
+
+// Need to be able to define these if we link to a debug static lib!
+#undef _malloc_dbg
+#undef _calloc_dbg
+#undef _realloc_dbg
+#undef _expand_dbg
+#undef _free_dbg
+#undef _msize_dbg
+#undef _CrtDbgReport
+
+
+
+const char *MakeModuleFileName()
+{
+ if ( g_pMemAlloc->IsDebugHeap() )
+ {
+ char *pszModuleName = (char *)HeapAlloc( GetProcessHeap(), 0, MAX_PATH ); // small leak, debug only
+
+ MEMORY_BASIC_INFORMATION mbi;
+ static int dummy;
+ VirtualQuery( &dummy, &mbi, sizeof(mbi) );
+
+ GetModuleFileName( reinterpret_cast(mbi.AllocationBase), pszModuleName, MAX_PATH );
+ char *pDot = strrchr( pszModuleName, '.' );
+ if ( pDot )
+ {
+ char *pSlash = strrchr( pszModuleName, '\\' );
+ if ( pSlash )
+ {
+ pszModuleName = pSlash + 1;
+ *pDot = 0;
+ }
+ }
+
+ return pszModuleName;
+ }
+ return NULL;
+}
+
+static void *AllocUnattributed( size_t nSize )
+{
+ static const char *pszOwner = MakeModuleFileName();
+
+ if ( !pszOwner )
+ return g_pMemAlloc->Alloc(nSize);
+ else
+ return g_pMemAlloc->Alloc(nSize, pszOwner, 0);
+}
+
+static void *ReallocUnattributed( void *pMem, size_t nSize )
+{
+ static const char *pszOwner = MakeModuleFileName();
+
+ if ( !pszOwner )
+ return g_pMemAlloc->Realloc(pMem, nSize);
+ else
+ return g_pMemAlloc->Realloc(pMem, nSize, pszOwner, 0);
+}
+
+#else
+#define MakeModuleFileName() NULL
+inline void *AllocUnattributed( size_t nSize )
+{
+ return g_pMemAlloc->Alloc(nSize);
+}
+
+inline void *ReallocUnattributed( void *pMem, size_t nSize )
+{
+ return g_pMemAlloc->Realloc(pMem, nSize);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Standard functions in the CRT that we're going to override to call our allocator
+//-----------------------------------------------------------------------------
+#if defined(_WIN32) && !defined(_STATIC_LINKED)
+// this magic only works under win32
+// under linux this malloc() overrides the libc malloc() and so we
+// end up in a recursion (as g_pMemAlloc->Alloc() calls malloc)
+#if _MSC_VER >= 1930
+#define ALLOC_CALL _CRTRESTRICT
+#define FREE_CALL
+#elif _MSC_VER >= 1400
+#define ALLOC_CALL _CRTNOALIAS _CRTRESTRICT
+#define FREE_CALL _CRTNOALIAS
+#else
+#define ALLOC_CALL
+#define FREE_CALL
+#endif
+
+extern "C"
+{
+
+ALLOC_CALL void *malloc( size_t nSize )
+{
+ return AllocUnattributed( nSize );
+}
+
+FREE_CALL void free( void *pMem )
+{
+ g_pMemAlloc->Free(pMem);
+}
+
+ALLOC_CALL void *realloc( void *pMem, size_t nSize )
+{
+ return ReallocUnattributed( pMem, nSize );
+}
+
+ALLOC_CALL void *calloc( size_t nCount, size_t nElementSize )
+{
+ void *pMem = AllocUnattributed( nElementSize * nCount );
+ memset(pMem, 0, nElementSize * nCount);
+ return pMem;
+}
+
+} // end extern "C"
+
+//-----------------------------------------------------------------------------
+// Non-standard MSVC functions that we're going to override to call our allocator
+//-----------------------------------------------------------------------------
+extern "C"
+{
+#if _MSC_VER >= 1930
+
+
+_CRTRESTRICT void* __cdecl _malloc_base(size_t nSize)
+{
+ return AllocUnattributed(nSize);
+}
+
+_CRTRESTRICT void* __cdecl _calloc_base(size_t nCount, size_t nSize)
+{
+ size_t nBytes = nCount * nSize;
+ void* pMem = AllocUnattributed(nBytes);
+ memset(pMem, 0, nBytes);
+ return pMem;
+}
+
+_CRTRESTRICT void* __cdecl _realloc_base( void *pMem, size_t nSize )
+{
+ return ReallocUnattributed( pMem, nSize );
+}
+
+_CRTRESTRICT void* __cdecl _recalloc_base( void *pMem, size_t nCount, size_t nSize)
+{
+ size_t nBytes = nCount * nSize;
+ size_t nBytesOld = _msize(pMem);
+
+ void *pMemOut = ReallocUnattributed( pMem, nBytes );
+ if(nBytes > nBytesOld)
+ memset(reinterpret_cast(pMemOut) + nBytesOld , 0, nBytes - nBytesOld);
+
+ return pMemOut;
+}
+
+#else
+
+// 64-bit
+#ifdef _WIN64
+void* __cdecl _malloc_base( size_t nSize )
+{
+ return AllocUnattributed( nSize );
+}
+#else
+void *_malloc_base( size_t nSize )
+{
+ return AllocUnattributed( nSize );
+}
+#endif
+
+void *_calloc_base( size_t nSize )
+{
+ void *pMem = AllocUnattributed( nSize );
+ memset(pMem, 0, nSize);
+ return pMem;
+}
+
+void *_realloc_base( void *pMem, size_t nSize )
+{
+ return ReallocUnattributed( pMem, nSize );
+}
+
+void *_recalloc_base( void *pMem, size_t nSize )
+{
+ void *pMemOut = ReallocUnattributed( pMem, nSize );
+ memset(pMemOut, 0, nSize);
+ return pMemOut;
+}
+#endif
+
+
+void _free_base( void *pMem )
+{
+ g_pMemAlloc->Free(pMem);
+}
+
+void *__cdecl _expand_base( void *pMem, size_t nNewSize, int nBlockUse )
+{
+ Assert( 0 );
+ return NULL;
+}
+
+// crt
+void * __cdecl _malloc_crt(size_t size)
+{
+ return AllocUnattributed( size );
+}
+
+void * __cdecl _calloc_crt(size_t count, size_t size)
+{
+#if _MSC_VER >= 1930
+ return _calloc_base( count, size );
+#else
+ return _calloc_base( count * size );
+#endif
+}
+
+void * __cdecl _realloc_crt(void *ptr, size_t size)
+{
+ return _realloc_base( ptr, size );
+}
+
+void * __cdecl _recalloc_crt(void *ptr, size_t count, size_t size)
+{
+#if _MSC_VER >= 1930
+ return _recalloc_base(ptr, count, size);
+#else
+ return _recalloc_base( ptr, count * size );
+#endif
+}
+
+ALLOC_CALL void * __cdecl _recalloc ( void * memblock, size_t count, size_t size )
+{
+ void *pMem = ReallocUnattributed( memblock, size * count );
+ memset( pMem, 0, size * count );
+ return pMem;
+}
+
+size_t _msize_base( void *pMem )
+#ifdef _CRT_NOEXCEPT
+_CRT_NOEXCEPT
+#endif
+{
+ return g_pMemAlloc->GetSize(pMem);
+}
+
+size_t _msize( void *pMem )
+{
+ return _msize_base(pMem);
+}
+
+size_t msize( void *pMem )
+{
+ return g_pMemAlloc->GetSize(pMem);
+}
+
+void *__cdecl _heap_alloc( size_t nSize )
+{
+ return AllocUnattributed( nSize );
+}
+
+void *__cdecl _nh_malloc( size_t nSize, int )
+{
+ return AllocUnattributed( nSize );
+}
+
+void *__cdecl _expand( void *pMem, size_t nSize )
+{
+ Assert( 0 );
+ return NULL;
+}
+
+unsigned int _amblksiz = 16; //BYTES_PER_PARA;
+
+#if _MSC_VER >= 1400
+HANDLE _crtheap = (HANDLE)1; // PatM Can't be 0 or CRT pukes
+int __active_heap = 1;
+#endif // _MSC_VER >= 1400
+
+size_t __cdecl _get_sbh_threshold( void )
+{
+ return 0;
+}
+
+int __cdecl _set_sbh_threshold( size_t )
+{
+ return 0;
+}
+
+int _heapchk()
+{
+ return g_pMemAlloc->heapchk();
+}
+
+int _heapmin()
+{
+ return 1;
+}
+
+int __cdecl _heapadd( void *, size_t )
+{
+ return 0;
+}
+
+int __cdecl _heapset( unsigned int )
+{
+ return 0;
+}
+
+size_t __cdecl _heapused( size_t *, size_t * )
+{
+ return 0;
+}
+
+#ifdef _WIN32
+int __cdecl _heapwalk( _HEAPINFO * )
+{
+ return 0;
+}
+#endif
+
+} // end extern "C"
+
+
+//-----------------------------------------------------------------------------
+// Debugging functions that we're going to override to call our allocator
+// NOTE: These have to be here for release + debug builds in case we
+// link to a debug static lib!!!
+//-----------------------------------------------------------------------------
+
+extern "C"
+{
+
+void *malloc_db( size_t nSize, const char *pFileName, int nLine )
+{
+ return g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+}
+
+void free_db( void *pMem, const char *pFileName, int nLine )
+{
+ g_pMemAlloc->Free(pMem, pFileName, nLine);
+}
+
+void *realloc_db( void *pMem, size_t nSize, const char *pFileName, int nLine )
+{
+ return g_pMemAlloc->Realloc(pMem, nSize, pFileName, nLine);
+}
+
+} // end extern "C"
+
+//-----------------------------------------------------------------------------
+// These methods are standard MSVC heap initialization + shutdown methods
+//-----------------------------------------------------------------------------
+extern "C"
+{
+
+#if !defined( _X360 )
+ int __cdecl _heap_init()
+ {
+ return g_pMemAlloc != NULL;
+ }
+
+ void __cdecl _heap_term()
+ {
+ }
+#endif
+
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Prevents us from using an inappropriate new or delete method,
+// ensures they are here even when linking against debug or release static libs
+//-----------------------------------------------------------------------------
+#ifndef NO_MEMOVERRIDE_NEW_DELETE
+void *__cdecl operator new( unsigned int nSize )
+{
+ return AllocUnattributed( nSize );
+}
+
+void *__cdecl operator new( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+}
+
+void __cdecl operator delete( void *pMem )
+{
+ g_pMemAlloc->Free( pMem );
+}
+
+void *__cdecl operator new[] ( unsigned int nSize )
+{
+ return AllocUnattributed( nSize );
+}
+
+void *__cdecl operator new[] ( unsigned int nSize, int nBlockUse, const char *pFileName, int nLine )
+{
+ return g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+}
+
+void __cdecl operator delete[] ( void *pMem )
+{
+ g_pMemAlloc->Free( pMem );
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Override some debugging allocation methods in MSVC
+// NOTE: These have to be here for release + debug builds in case we
+// link to a debug static lib!!!
+//-----------------------------------------------------------------------------
+#ifndef _STATIC_LINKED
+#ifdef _WIN32
+
+// This here just hides the internal file names, etc of allocations
+// made in the c runtime library
+#define CRT_INTERNAL_FILE_NAME "C-runtime internal"
+
+class CAttibCRT
+{
+public:
+ CAttibCRT(int nBlockUse) : m_nBlockUse(nBlockUse)
+ {
+ if (m_nBlockUse == _CRT_BLOCK)
+ {
+ g_pMemAlloc->PushAllocDbgInfo(CRT_INTERNAL_FILE_NAME, 0);
+ }
+ }
+
+ ~CAttibCRT()
+ {
+ if (m_nBlockUse == _CRT_BLOCK)
+ {
+ g_pMemAlloc->PopAllocDbgInfo();
+ }
+ }
+
+private:
+ int m_nBlockUse;
+};
+
+
+#define AttribIfCrt() CAttibCRT _attrib(nBlockUse)
+#elif defined(_LINUX)
+#define AttribIfCrt()
+#endif // _WIN32
+
+
+extern "C"
+{
+
+void *__cdecl _nh_malloc_dbg( size_t nSize, int nFlag, int nBlockUse,
+ const char *pFileName, int nLine )
+{
+ AttribIfCrt();
+ return g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+}
+
+void *__cdecl _malloc_dbg( size_t nSize, int nBlockUse,
+ const char *pFileName, int nLine )
+{
+ AttribIfCrt();
+ return g_pMemAlloc->Alloc(nSize, pFileName, nLine);
+}
+
+void *__cdecl _calloc_dbg( size_t nNum, size_t nSize, int nBlockUse,
+ const char *pFileName, int nLine )
+{
+ AttribIfCrt();
+ void *pMem = g_pMemAlloc->Alloc(nSize * nNum, pFileName, nLine);
+ memset(pMem, 0, nSize * nNum);
+ return pMem;
+}
+
+void *__cdecl _realloc_dbg( void *pMem, size_t nNewSize, int nBlockUse,
+ const char *pFileName, int nLine )
+{
+ AttribIfCrt();
+ return g_pMemAlloc->Realloc(pMem, nNewSize, pFileName, nLine);
+}
+
+void *__cdecl _expand_dbg( void *pMem, size_t nNewSize, int nBlockUse,
+ const char *pFileName, int nLine )
+{
+ Assert( 0 );
+ return NULL;
+}
+
+void __cdecl _free_dbg( void *pMem, int nBlockUse )
+{
+ AttribIfCrt();
+ g_pMemAlloc->Free(pMem);
+}
+
+size_t __cdecl _msize_dbg( void *pMem, int nBlockUse )
+{
+#ifdef _WIN32
+ return _msize(pMem);
+#elif _LINUX
+ Assert( "_msize_dbg unsupported" );
+ return 0;
+#endif
+}
+
+
+#ifdef _WIN32
+
+#if defined(_DEBUG) && _MSC_VER >= 1300
+// X360TBD: aligned and offset allocations may be important on the 360
+
+// aligned base
+ALLOC_CALL void *__cdecl _aligned_malloc_base( size_t size, size_t align )
+{
+ return MemAlloc_AllocAligned( size, align );
+}
+
+ALLOC_CALL void *__cdecl _aligned_realloc_base( void *ptr, size_t size, size_t align )
+{
+ return MemAlloc_ReallocAligned( ptr, size, align );
+}
+
+ALLOC_CALL void *__cdecl _aligned_recalloc_base( void *ptr, size_t size, size_t align )
+{
+ Error( "Unsupported function\n" );
+ return NULL;
+}
+
+FREE_CALL void __cdecl _aligned_free_base( void *ptr )
+{
+ MemAlloc_FreeAligned( ptr );
+}
+
+// aligned
+ALLOC_CALL void * __cdecl _aligned_malloc( size_t size, size_t align )
+{
+ return _aligned_malloc_base(size, align);
+}
+
+ALLOC_CALL void *__cdecl _aligned_realloc(void *memblock, size_t size, size_t align)
+{
+ return _aligned_realloc_base(memblock, size, align);
+}
+
+ALLOC_CALL void * __cdecl _aligned_recalloc( void * memblock, size_t count, size_t size, size_t align )
+{
+ return _aligned_recalloc_base(memblock, count * size, align);
+}
+
+FREE_CALL void __cdecl _aligned_free( void *memblock )
+{
+ _aligned_free_base(memblock);
+}
+
+// aligned offset base
+ALLOC_CALL void * __cdecl _aligned_offset_malloc_base( size_t size, size_t align, size_t offset )
+{
+ Assert( IsPC() || 0 );
+ return NULL;
+}
+
+ALLOC_CALL void * __cdecl _aligned_offset_realloc_base( void * memblock, size_t size, size_t align, size_t offset)
+{
+ Assert( IsPC() || 0 );
+ return NULL;
+}
+
+ALLOC_CALL void * __cdecl _aligned_offset_recalloc_base( void * memblock, size_t size, size_t align, size_t offset)
+{
+ Assert( IsPC() || 0 );
+ return NULL;
+}
+
+// aligned offset
+ALLOC_CALL void *__cdecl _aligned_offset_malloc(size_t size, size_t align, size_t offset)
+{
+ return _aligned_offset_malloc_base( size, align, offset );
+}
+
+ALLOC_CALL void *__cdecl _aligned_offset_realloc(void *memblock, size_t size, size_t align, size_t offset)
+{
+ return _aligned_offset_realloc_base( memblock, size, align, offset );
+}
+
+ALLOC_CALL void * __cdecl _aligned_offset_recalloc( void * memblock, size_t count, size_t size, size_t align, size_t offset )
+{
+ return _aligned_offset_recalloc_base( memblock, count * size, align, offset );
+}
+
+#endif // _MSC_VER >= 1400
+
+#endif
+
+} // end extern "C"
+
+
+//-----------------------------------------------------------------------------
+// Override some the _CRT debugging allocation methods in MSVC
+//-----------------------------------------------------------------------------
+#ifdef _WIN32
+
+extern "C"
+{
+
+int _CrtDumpMemoryLeaks(void)
+{
+ return 0;
+}
+
+_CRT_DUMP_CLIENT _CrtSetDumpClient( _CRT_DUMP_CLIENT dumpClient )
+{
+ return NULL;
+}
+
+int _CrtSetDbgFlag( int nNewFlag )
+{
+ return g_pMemAlloc->CrtSetDbgFlag( nNewFlag );
+}
+
+// 64-bit port.
+#define AFNAME(var) __p_ ## var
+#define AFRET(var) &var
+#if _MSC_VER >= 1930
+#undef _crtDbgFlag
+#undef _crtBreakAlloc
+#endif
+
+int _crtDbgFlag = _CRTDBG_ALLOC_MEM_DF;
+int* AFNAME(_crtDbgFlag)(void)
+{
+ return AFRET(_crtDbgFlag);
+}
+
+long _crtBreakAlloc; /* Break on this allocation */
+long* AFNAME(_crtBreakAlloc) (void)
+{
+ return AFRET(_crtBreakAlloc);
+}
+
+void __cdecl _CrtSetDbgBlockType( void *pMem, int nBlockUse )
+{
+ DebuggerBreak();
+}
+
+_CRT_ALLOC_HOOK __cdecl _CrtSetAllocHook( _CRT_ALLOC_HOOK pfnNewHook )
+{
+ DebuggerBreak();
+ return NULL;
+}
+
+long __cdecl _CrtSetBreakAlloc( long lNewBreakAlloc )
+{
+ return g_pMemAlloc->CrtSetBreakAlloc( lNewBreakAlloc );
+}
+
+int __cdecl _CrtIsValidHeapPointer( const void *pMem )
+{
+ return g_pMemAlloc->CrtIsValidHeapPointer( pMem );
+}
+
+int __cdecl _CrtIsValidPointer( const void *pMem, unsigned int size, int access )
+{
+ return g_pMemAlloc->CrtIsValidPointer( pMem, size, access );
+}
+
+int __cdecl _CrtCheckMemory( void )
+{
+ // FIXME: Remove this when we re-implement the heap
+ return g_pMemAlloc->CrtCheckMemory( );
+}
+
+int __cdecl _CrtIsMemoryBlock( const void *pMem, unsigned int nSize,
+ long *plRequestNumber, char **ppFileName, int *pnLine )
+{
+ DebuggerBreak();
+ return 1;
+}
+
+int __cdecl _CrtMemDifference( _CrtMemState *pState, const _CrtMemState * oldState, const _CrtMemState * newState )
+{
+ DebuggerBreak();
+ return FALSE;
+}
+
+void __cdecl _CrtMemDumpStatistics( const _CrtMemState *pState )
+{
+ DebuggerBreak();
+}
+
+void __cdecl _CrtMemCheckpoint( _CrtMemState *pState )
+{
+ // FIXME: Remove this when we re-implement the heap
+ g_pMemAlloc->CrtMemCheckpoint( pState );
+}
+
+void __cdecl _CrtMemDumpAllObjectsSince( const _CrtMemState *pState )
+{
+ DebuggerBreak();
+}
+
+void __cdecl _CrtDoForAllClientObjects( void (*pfn)(void *, void *), void * pContext )
+{
+ DebuggerBreak();
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods in dbgrpt.cpp
+//-----------------------------------------------------------------------------
+long _crtAssertBusy = -1;
+
+int __cdecl _CrtSetReportMode( int nReportType, int nReportMode )
+{
+ return g_pMemAlloc->CrtSetReportMode( nReportType, nReportMode );
+}
+
+_HFILE __cdecl _CrtSetReportFile( int nRptType, _HFILE hFile )
+{
+ return (_HFILE)g_pMemAlloc->CrtSetReportFile( nRptType, hFile );
+}
+
+_CRT_REPORT_HOOK __cdecl _CrtSetReportHook( _CRT_REPORT_HOOK pfnNewHook )
+{
+ return (_CRT_REPORT_HOOK)g_pMemAlloc->CrtSetReportHook( pfnNewHook );
+}
+
+int __cdecl _CrtDbgReport( int nRptType, const char * szFile,
+ int nLine, const char * szModule, const char * szFormat, ... )
+{
+ static char output[1024];
+ va_list args;
+ va_start( args, szFormat );
+ _vsnprintf( output, sizeof( output )-1, szFormat, args );
+ va_end( args );
+
+ return g_pMemAlloc->CrtDbgReport( nRptType, szFile, nLine, szModule, output );
+}
+
+#if _MSC_VER >= 1400
+
+#if defined( _DEBUG )
+
+// wrapper which passes no debug info; not available in debug
+void __cdecl _invalid_parameter_noinfo(void)
+{
+ Assert(0);
+}
+
+#endif /* defined( _DEBUG ) */
+
+#if defined( _DEBUG ) || defined( USE_MEM_DEBUG )
+
+int __cdecl __crtMessageWindowW( int nRptType, const wchar_t * szFile, const wchar_t * szLine,
+ const wchar_t * szModule, const wchar_t * szUserMessage )
+{
+ Assert(0);
+ return 0;
+}
+
+int __cdecl _CrtDbgReportV( int nRptType, const wchar_t *szFile, int nLine,
+ const wchar_t *szModule, const wchar_t *szFormat, va_list arglist )
+{
+ Assert(0);
+ return 0;
+}
+
+int __cdecl _CrtDbgReportW( int nRptType, const wchar_t *szFile, int nLine,
+ const wchar_t *szModule, const wchar_t *szFormat, ...)
+{
+ Assert(0);
+ return 0;
+}
+
+int __cdecl _VCrtDbgReportA( int nRptType, const wchar_t * szFile, int nLine,
+ const wchar_t * szModule, const wchar_t * szFormat, va_list arglist )
+{
+ Assert(0);
+ return 0;
+}
+
+int __cdecl _CrtSetReportHook2( int mode, _CRT_REPORT_HOOK pfnNewHook )
+{
+ _CrtSetReportHook( pfnNewHook );
+ return 0;
+}
+
+
+#endif /* defined( _DEBUG ) || defined( USE_MEM_DEBUG ) */
+
+extern "C" int __crtDebugCheckCount = FALSE;
+
+extern "C" int __cdecl _CrtSetCheckCount( int fCheckCount )
+{
+ int oldCheckCount = __crtDebugCheckCount;
+ return oldCheckCount;
+}
+
+extern "C" int __cdecl _CrtGetCheckCount( void )
+{
+ return __crtDebugCheckCount;
+}
+
+// aligned offset debug
+extern "C" void * __cdecl _aligned_offset_recalloc_dbg( void * memblock, size_t count, size_t size, size_t align, size_t offset, const char * f_name, int line_n )
+{
+ Assert( IsPC() || 0 );
+ void *pMem = ReallocUnattributed( memblock, size * count );
+ memset( pMem, 0, size * count );
+ return pMem;
+}
+
+extern "C" void * __cdecl _aligned_recalloc_dbg( void *memblock, size_t count, size_t size, size_t align, const char * f_name, int line_n )
+{
+ return _aligned_offset_recalloc_dbg(memblock, count, size, align, 0, f_name, line_n);
+}
+
+extern "C" void * __cdecl _recalloc_dbg ( void * memblock, size_t count, size_t size, int nBlockUse, const char * szFileName, int nLine )
+{
+ return _aligned_offset_recalloc_dbg(memblock, count, size, 0, 0, szFileName, nLine);
+}
+
+_CRT_REPORT_HOOK __cdecl _CrtGetReportHook( void )
+{
+ return NULL;
+}
+
+#endif
+int __cdecl _CrtReportBlockType(const void * pUserData)
+{
+ return 0;
+}
+
+
+} // end extern "C"
+#endif // _WIN32
+
+// Most files include this file, so when it's used it adds an extra .ValveDbg section,
+// to help identify debug binaries.
+#ifdef _WIN32
+ #ifndef NDEBUG // _DEBUG
+ #pragma data_seg("ValveDBG")
+ volatile const char* DBG = "*** DEBUG STUB ***";
+ #endif
+#endif
+
+#endif
+
+// Extras added prevent dbgheap.obj from being included - DAL
+#ifdef _WIN32
+
+extern "C"
+{
+size_t __crtDebugFillThreshold = 0;
+
+extern "C" void * __cdecl _heap_alloc_base (size_t size) {
+ assert(0);
+ return NULL;
+}
+
+
+void * __cdecl _heap_alloc_dbg( size_t nSize, int nBlockUse, const char * szFileName, int nLine)
+{
+ return _heap_alloc(nSize);
+}
+
+// 64-bit
+#ifdef _WIN64
+static void * __cdecl realloc_help( void * pUserData, size_t * pnNewSize, int nBlockUse,const char * szFileName,
+ int nLine, int fRealloc )
+{
+ assert(0); // Shouldn't be needed
+ return NULL;
+}
+#else
+static void * __cdecl realloc_help( void * pUserData, size_t nNewSize, int nBlockUse, const char * szFileName,
+ int nLine, int fRealloc)
+{
+ assert(0); // Shouldn't be needed
+ return NULL;
+}
+#endif
+
+void __cdecl _free_nolock( void * pUserData)
+{
+ // I don't think the second param is used in memoverride
+ _free_dbg(pUserData, 0);
+}
+
+void __cdecl _free_dbg_nolock( void * pUserData, int nBlockUse)
+{
+ _free_dbg(pUserData, 0);
+}
+
+_CRT_ALLOC_HOOK __cdecl _CrtGetAllocHook ( void)
+{
+ assert(0);
+ return NULL;
+}
+
+static int __cdecl CheckBytes( unsigned char * pb, unsigned char bCheck, size_t nSize)
+{
+ int bOkay = TRUE;
+ return bOkay;
+}
+
+
+_CRT_DUMP_CLIENT __cdecl _CrtGetDumpClient ( void)
+{
+ assert(0);
+ return NULL;
+}
+
+#if _MSC_VER >= 1400
+static void __cdecl _printMemBlockData( _locale_t plocinfo, _CrtMemBlockHeader * pHead)
+{
+}
+
+static void __cdecl _CrtMemDumpAllObjectsSince_stat( const _CrtMemState * state, _locale_t plocinfo)
+{
+}
+#endif
+void * __cdecl _aligned_malloc_dbg( size_t size, size_t align, const char * f_name, int line_n)
+{
+ return _aligned_malloc(size, align);
+}
+
+void * __cdecl _aligned_realloc_dbg( void *memblock, size_t size, size_t align,
+ const char * f_name, int line_n)
+{
+ return _aligned_realloc(memblock, size, align);
+}
+
+void * __cdecl _aligned_offset_malloc_dbg( size_t size, size_t align, size_t offset,
+ const char * f_name, int line_n)
+{
+ return _aligned_offset_malloc(size, align, offset);
+}
+
+void * __cdecl _aligned_offset_realloc_dbg( void * memblock, size_t size, size_t align,
+ size_t offset, const char * f_name, int line_n)
+{
+ return _aligned_offset_realloc(memblock, size, align, offset);
+}
+
+void __cdecl _aligned_free_dbg( void * memblock)
+{
+ _aligned_free(memblock);
+}
+
+size_t __cdecl _CrtSetDebugFillThreshold( size_t _NewDebugFillThreshold)
+{
+ assert(0);
+ return 0;
+}
+
+//===========================================
+// NEW!!! 64-bit
+
+char * __cdecl _strdup ( const char * string )
+{
+ int nSize = strlen(string) + 1;
+ char *pCopy = (char*)AllocUnattributed( nSize );
+ if ( pCopy )
+ memcpy( pCopy, string, nSize );
+ return pCopy;
+}
+
+#if 0
+_TSCHAR * __cdecl _tfullpath_dbg ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen, int nBlockUse, const char * szFileName, int nLine )
+{
+ Assert(0);
+ return NULL;
+}
+
+_TSCHAR * __cdecl _tfullpath ( _TSCHAR *UserBuf, const _TSCHAR *path, size_t maxlen )
+{
+ Assert(0);
+ return NULL;
+}
+
+_TSCHAR * __cdecl _tgetdcwd_lk_dbg ( int drive, _TSCHAR *pnbuf, int maxlen, int nBlockUse, const char * szFileName, int nLine )
+{
+ Assert(0);
+ return NULL;
+}
+
+_TSCHAR * __cdecl _tgetdcwd_nolock ( int drive, _TSCHAR *pnbuf, int maxlen )
+{
+ Assert(0);
+ return NULL;
+}
+
+errno_t __cdecl _tdupenv_s_helper ( _TSCHAR **pBuffer, size_t *pBufferSizeInTChars, const _TSCHAR *varname, int nBlockUse, const char * szFileName, int nLine )
+{
+ Assert(0);
+ return 0;
+}
+
+errno_t __cdecl _tdupenv_s_helper ( _TSCHAR **pBuffer, size_t *pBufferSizeInTChars, const _TSCHAR *varname )
+{
+ Assert(0);
+ return 0;
+}
+
+_TSCHAR * __cdecl _ttempnam_dbg ( const _TSCHAR *dir, const _TSCHAR *pfx, int nBlockUse, const char * szFileName, int nLine )
+{
+ Assert(0);
+ return 0;
+}
+
+_TSCHAR * __cdecl _ttempnam ( const _TSCHAR *dir, const _TSCHAR *pfx )
+{
+ Assert(0);
+ return 0;
+}
+#endif
+
+wchar_t * __cdecl _wcsdup_dbg ( const wchar_t * string, int nBlockUse, const char * szFileName, int nLine )
+{
+ Assert(0);
+ return 0;
+}
+
+wchar_t * __cdecl _wcsdup ( const wchar_t * string )
+{
+ Assert(0);
+ return 0;
+}
+
+} // end extern "C"
+
+#if _MSC_VER >= 1400
+
+//-----------------------------------------------------------------------------
+// XBox Memory Allocator Override
+//-----------------------------------------------------------------------------
+#if defined( _X360 )
+#if defined( _DEBUG ) || defined( USE_MEM_DEBUG )
+#include "UtlMap.h"
+
+MEMALLOC_DEFINE_EXTERNAL_TRACKING( XMem );
+
+CThreadFastMutex g_XMemAllocMutex;
+
+void XMemAlloc_RegisterAllocation( void *p, DWORD dwAllocAttributes )
+{
+ if ( !g_pMemAlloc )
+ {
+ // core xallocs cannot be journaled until system is ready
+ return;
+ }
+
+ AUTO_LOCK_FM( g_XMemAllocMutex );
+ int size = XMemSize( p, dwAllocAttributes );
+ MemAlloc_RegisterExternalAllocation( XMem, p, size );
+}
+
+void XMemAlloc_RegisterDeallocation( void *p, DWORD dwAllocAttributes )
+{
+ if ( !g_pMemAlloc )
+ {
+ // core xallocs cannot be journaled until system is ready
+ return;
+ }
+
+ AUTO_LOCK_FM( g_XMemAllocMutex );
+ int size = XMemSize( p, dwAllocAttributes );
+ MemAlloc_RegisterExternalDeallocation( XMem, p, size );
+}
+
+#else
+
+#define XMemAlloc_RegisterAllocation( p, a ) ((void)0)
+#define XMemAlloc_RegisterDeallocation( p, a ) ((void)0)
+
+#endif
+
+//-----------------------------------------------------------------------------
+// XMemAlloc
+//
+// XBox Memory Allocator Override
+//-----------------------------------------------------------------------------
+LPVOID WINAPI XMemAlloc( SIZE_T dwSize, DWORD dwAllocAttributes )
+{
+ LPVOID ptr;
+ XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes;
+ bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL );
+
+ if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI )
+ {
+ MEM_ALLOC_CREDIT();
+ switch ( pAttribs->dwAlignment )
+ {
+ case XALLOC_ALIGNMENT_4:
+ ptr = g_pMemAlloc->Alloc( dwSize );
+ break;
+ case XALLOC_ALIGNMENT_8:
+ ptr = MemAlloc_AllocAligned( dwSize, 8 );
+ break;
+ case XALLOC_ALIGNMENT_DEFAULT:
+ case XALLOC_ALIGNMENT_16:
+ default:
+ ptr = MemAlloc_AllocAligned( dwSize, 16 );
+ break;
+ }
+ if ( pAttribs->dwZeroInitialize != 0 )
+ {
+ memset( ptr, 0, XMemSize( ptr, dwAllocAttributes ) );
+ }
+ return ptr;
+ }
+
+ ptr = XMemAllocDefault( dwSize, dwAllocAttributes );
+ if ( ptr )
+ {
+ XMemAlloc_RegisterAllocation( ptr, dwAllocAttributes );
+ }
+
+ return ptr;
+}
+
+//-----------------------------------------------------------------------------
+// XMemFree
+//
+// XBox Memory Allocator Override
+//-----------------------------------------------------------------------------
+VOID WINAPI XMemFree( PVOID pAddress, DWORD dwAllocAttributes )
+{
+ if ( !pAddress )
+ {
+ return;
+ }
+
+ XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes;
+ bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL );
+
+ if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI )
+ {
+ switch ( pAttribs->dwAlignment )
+ {
+ case XALLOC_ALIGNMENT_4:
+ return g_pMemAlloc->Free( pAddress );
+ default:
+ return MemAlloc_FreeAligned( pAddress );
+ }
+ return;
+ }
+
+ XMemAlloc_RegisterDeallocation( pAddress, dwAllocAttributes );
+
+ XMemFreeDefault( pAddress, dwAllocAttributes );
+}
+
+//-----------------------------------------------------------------------------
+// XMemSize
+//
+// XBox Memory Allocator Override
+//-----------------------------------------------------------------------------
+SIZE_T WINAPI XMemSize( PVOID pAddress, DWORD dwAllocAttributes )
+{
+ XALLOC_ATTRIBUTES *pAttribs = (XALLOC_ATTRIBUTES *)&dwAllocAttributes;
+ bool bPhysical = ( pAttribs->dwMemoryType == XALLOC_MEMTYPE_PHYSICAL );
+
+ if ( !bPhysical && !pAttribs->dwHeapTracksAttributes && pAttribs->dwAllocatorId != eXALLOCAllocatorId_XUI )
+ {
+ switch ( pAttribs->dwAlignment )
+ {
+ case XALLOC_ALIGNMENT_4:
+ return g_pMemAlloc->GetSize( pAddress );
+ default:
+ return MemAlloc_GetSizeAligned( pAddress );
+ }
+ }
+
+ return XMemSizeDefault( pAddress, dwAllocAttributes );
+}
+#endif // _X360
+
+#define MAX_LANG_LEN 64 /* max language name length */
+#define MAX_CTRY_LEN 64 /* max country name length */
+#define MAX_MODIFIER_LEN 0 /* max modifier name length - n/a */
+#define MAX_LC_LEN (MAX_LANG_LEN+MAX_CTRY_LEN+MAX_MODIFIER_LEN+3)
+
+#if _MSC_VER >= 1700 // VS 11
+// Copied from C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\crt\src\mtdll.h
+#ifndef _SETLOC_STRUCT_DEFINED
+struct _is_ctype_compatible {
+ unsigned long id;
+ int is_clike;
+};
+
+typedef struct setloc_struct {
+ /* getqloc static variables */
+ wchar_t *pchLanguage;
+ wchar_t *pchCountry;
+ int iLocState;
+ int iPrimaryLen;
+ BOOL bAbbrevLanguage;
+ BOOL bAbbrevCountry;
+ UINT _cachecp;
+ wchar_t _cachein[MAX_LC_LEN];
+ wchar_t _cacheout[MAX_LC_LEN];
+ /* _setlocale_set_cat (LC_CTYPE) static variable */
+ struct _is_ctype_compatible _Loc_c[5];
+ wchar_t _cacheLocaleName[LOCALE_NAME_MAX_LENGTH];
+} _setloc_struct, *_psetloc_struct;
+#define _SETLOC_STRUCT_DEFINED
+#endif /* _SETLOC_STRUCT_DEFINED */
+
+_CRTIMP extern unsigned long __cdecl __threadid(void);
+#define _threadid (__threadid())
+_CRTIMP extern uintptr_t __cdecl __threadhandle(void);
+#define _threadhandle (__threadhandle())
+
+/* Structure for each thread's data */
+
+struct _tiddata {
+ unsigned long _tid; /* thread ID */
+
+
+ uintptr_t _thandle; /* thread handle */
+
+ int _terrno; /* errno value */
+ unsigned long _tdoserrno; /* _doserrno value */
+ unsigned int _fpds; /* Floating Point data segment */
+ unsigned long _holdrand; /* rand() seed value */
+ char * _token; /* ptr to strtok() token */
+ wchar_t * _wtoken; /* ptr to wcstok() token */
+ unsigned char * _mtoken; /* ptr to _mbstok() token */
+
+ /* following pointers get malloc'd at runtime */
+ char * _errmsg; /* ptr to strerror()/_strerror() buff */
+ wchar_t * _werrmsg; /* ptr to _wcserror()/__wcserror() buff */
+ char * _namebuf0; /* ptr to tmpnam() buffer */
+ wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */
+ char * _namebuf1; /* ptr to tmpfile() buffer */
+ wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */
+ char * _asctimebuf; /* ptr to asctime() buffer */
+ wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */
+ void * _gmtimebuf; /* ptr to gmtime() structure */
+ char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */
+ unsigned char _con_ch_buf[MB_LEN_MAX];
+ /* ptr to putch() buffer */
+ unsigned short _ch_buf_used; /* if the _con_ch_buf is used */
+
+ /* following fields are needed by _beginthread code */
+ void * _initaddr; /* initial user thread address */
+ void * _initarg; /* initial user thread argument */
+
+ /* following three fields are needed to support signal handling and
+ * runtime errors */
+ void * _pxcptacttab; /* ptr to exception-action table */
+ void * _tpxcptinfoptrs; /* ptr to exception info pointers */
+ int _tfpecode; /* float point exception code */
+
+#if _MSC_VER >= 1930
+
+ __crt_locale_data* ptmbcinfo;
+ __crt_multibyte_data* ptlocinfo;
+#else
+
+ /* pointer to the copy of the multibyte character information used by
+ * the thread */
+ pthreadmbcinfo ptmbcinfo;
+
+ /* pointer to the copy of the locale informaton used by the thead */
+ pthreadlocinfo ptlocinfo;
+#endif
+
+ int _ownlocale; /* if 1, this thread owns its own locale */
+
+ /* following field is needed by NLG routines */
+ unsigned long _NLG_dwCode;
+
+ /*
+ * Per-Thread data needed by C++ Exception Handling
+ */
+ void * _terminate; /* terminate() routine */
+ void * _unexpected; /* unexpected() routine */
+ void * _translator; /* S.E. translator */
+ void * _purecall; /* called when pure virtual happens */
+ void * _curexception; /* current exception */
+ void * _curcontext; /* current exception context */
+ int _ProcessingThrow; /* for uncaught_exception */
+ void * _curexcspec; /* for handling exceptions thrown from std::unexpected */
+#if defined (_M_X64) || defined (_M_ARM)
+ void * _pExitContext;
+ void * _pUnwindContext;
+ void * _pFrameInfoChain;
+#if defined (_WIN64)
+ unsigned __int64 _ImageBase;
+ unsigned __int64 _ThrowImageBase;
+#else /* defined (_WIN64) */
+ unsigned __int32 _ImageBase;
+ unsigned __int32 _ThrowImageBase;
+#endif /* defined (_WIN64) */
+ void * _pForeignException;
+#elif defined (_M_IX86)
+ void * _pFrameInfoChain;
+#endif /* defined (_M_IX86) */
+ _setloc_struct _setloc_data;
+
+ void * _reserved1; /* nothing */
+ void * _reserved2; /* nothing */
+ void * _reserved3; /* nothing */
+#ifdef _M_IX86
+ void * _reserved4; /* nothing */
+ void * _reserved5; /* nothing */
+#endif /* _M_IX86 */
+
+ int _cxxReThrow; /* Set to True if it's a rethrown C++ Exception */
+
+ unsigned long __initDomain; /* initial domain used by _beginthread[ex] for managed function */
+};
+#else
+struct _is_ctype_compatible {
+ unsigned long id;
+ int is_clike;
+};
+typedef struct setloc_struct {
+ /* getqloc static variables */
+ char *pchLanguage;
+ char *pchCountry;
+ int iLcidState;
+ int iPrimaryLen;
+ BOOL bAbbrevLanguage;
+ BOOL bAbbrevCountry;
+ LCID lcidLanguage;
+ LCID lcidCountry;
+ /* expand_locale static variables */
+ LC_ID _cacheid;
+ UINT _cachecp;
+ char _cachein[MAX_LC_LEN];
+ char _cacheout[MAX_LC_LEN];
+ /* _setlocale_set_cat (LC_CTYPE) static variable */
+ struct _is_ctype_compatible _Lcid_c[5];
+} _setloc_struct, *_psetloc_struct;
+
+struct _tiddata {
+ unsigned long _tid; /* thread ID */
+
+
+ uintptr_t _thandle; /* thread handle */
+
+ int _terrno; /* errno value */
+ unsigned long _tdoserrno; /* _doserrno value */
+ unsigned int _fpds; /* Floating Point data segment */
+ unsigned long _holdrand; /* rand() seed value */
+ char * _token; /* ptr to strtok() token */
+ wchar_t * _wtoken; /* ptr to wcstok() token */
+ unsigned char * _mtoken; /* ptr to _mbstok() token */
+
+ /* following pointers get malloc'd at runtime */
+ char * _errmsg; /* ptr to strerror()/_strerror() buff */
+ wchar_t * _werrmsg; /* ptr to _wcserror()/__wcserror() buff */
+ char * _namebuf0; /* ptr to tmpnam() buffer */
+ wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */
+ char * _namebuf1; /* ptr to tmpfile() buffer */
+ wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */
+ char * _asctimebuf; /* ptr to asctime() buffer */
+ wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */
+ void * _gmtimebuf; /* ptr to gmtime() structure */
+ char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */
+ unsigned char _con_ch_buf[MB_LEN_MAX];
+ /* ptr to putch() buffer */
+ unsigned short _ch_buf_used; /* if the _con_ch_buf is used */
+
+ /* following fields are needed by _beginthread code */
+ void * _initaddr; /* initial user thread address */
+ void * _initarg; /* initial user thread argument */
+
+ /* following three fields are needed to support signal handling and
+ * runtime errors */
+ void * _pxcptacttab; /* ptr to exception-action table */
+ void * _tpxcptinfoptrs; /* ptr to exception info pointers */
+ int _tfpecode; /* float point exception code */
+
+ /* pointer to the copy of the multibyte character information used by
+ * the thread */
+ pthreadmbcinfo ptmbcinfo;
+
+ /* pointer to the copy of the locale informaton used by the thead */
+ pthreadlocinfo ptlocinfo;
+ int _ownlocale; /* if 1, this thread owns its own locale */
+
+ /* following field is needed by NLG routines */
+ unsigned long _NLG_dwCode;
+
+ /*
+ * Per-Thread data needed by C++ Exception Handling
+ */
+ void * _terminate; /* terminate() routine */
+ void * _unexpected; /* unexpected() routine */
+ void * _translator; /* S.E. translator */
+ void * _purecall; /* called when pure virtual happens */
+ void * _curexception; /* current exception */
+ void * _curcontext; /* current exception context */
+ int _ProcessingThrow; /* for uncaught_exception */
+ void * _curexcspec; /* for handling exceptions thrown from std::unexpected */
+#if defined (_M_IA64) || defined (_M_AMD64)
+ void * _pExitContext;
+ void * _pUnwindContext;
+ void * _pFrameInfoChain;
+ unsigned __int64 _ImageBase;
+#if defined (_M_IA64)
+ unsigned __int64 _TargetGp;
+#endif /* defined (_M_IA64) */
+ unsigned __int64 _ThrowImageBase;
+ void * _pForeignException;
+#elif defined (_M_IX86)
+ void * _pFrameInfoChain;
+#endif /* defined (_M_IX86) */
+ _setloc_struct _setloc_data;
+
+ void * _encode_ptr; /* EncodePointer() routine */
+ void * _decode_ptr; /* DecodePointer() routine */
+
+ void * _reserved1; /* nothing */
+ void * _reserved2; /* nothing */
+ void * _reserved3; /* nothing */
+
+ int _cxxReThrow; /* Set to True if it's a rethrown C++ Exception */
+
+ unsigned long __initDomain; /* initial domain used by _beginthread[ex] for managed function */
+};
+#endif
+
+typedef struct _tiddata * _ptiddata;
+
+class _LocaleUpdate
+{
+#if _MSC_VER >= 1930
+ __crt_locale_pointers localeinfo;
+#else
+ _locale_tstruct localeinfo;
+#endif
+ _ptiddata ptd;
+ bool updated;
+ public:
+ _LocaleUpdate(_locale_t plocinfo)
+ : updated(false)
+ {
+ /*
+ if (plocinfo == NULL)
+ {
+ ptd = _getptd();
+ localeinfo.locinfo = ptd->ptlocinfo;
+ localeinfo.mbcinfo = ptd->ptmbcinfo;
+
+ __UPDATE_LOCALE(ptd, localeinfo.locinfo);
+ __UPDATE_MBCP(ptd, localeinfo.mbcinfo);
+ if (!(ptd->_ownlocale & _PER_THREAD_LOCALE_BIT))
+ {
+ ptd->_ownlocale |= _PER_THREAD_LOCALE_BIT;
+ updated = true;
+ }
+ }
+ else
+ {
+ localeinfo=*plocinfo;
+ }
+ */
+ }
+ ~_LocaleUpdate()
+ {
+// if (updated)
+// ptd->_ownlocale = ptd->_ownlocale & ~_PER_THREAD_LOCALE_BIT;
+ }
+ _locale_t GetLocaleT()
+ {
+ return &localeinfo;
+ }
+};
+
+
+#pragma warning(push)
+#pragma warning(disable: 4483)
+#if _MSC_FULL_VER >= 140050415
+#define _NATIVE_STARTUP_NAMESPACE __identifier("")
+#else /* _MSC_FULL_VER >= 140050415 */
+#define _NATIVE_STARTUP_NAMESPACE __CrtImplementationDetails
+#endif /* _MSC_FULL_VER >= 140050415 */
+
+namespace _NATIVE_STARTUP_NAMESPACE
+{
+ class NativeDll
+ {
+ private:
+ static const unsigned int ProcessDetach = 0;
+ static const unsigned int ProcessAttach = 1;
+ static const unsigned int ThreadAttach = 2;
+ static const unsigned int ThreadDetach = 3;
+ static const unsigned int ProcessVerifier = 4;
+
+ public:
+
+ inline static bool IsInDllMain()
+ {
+ return false;
+ }
+
+ inline static bool IsInProcessAttach()
+ {
+ return false;
+ }
+
+ inline static bool IsInProcessDetach()
+ {
+ return false;
+ }
+
+ inline static bool IsInVcclrit()
+ {
+ return false;
+ }
+
+ inline static bool IsSafeForManagedCode()
+ {
+ if (!IsInDllMain())
+ {
+ return true;
+ }
+
+ if (IsInVcclrit())
+ {
+ return true;
+ }
+
+ return !IsInProcessAttach() && !IsInProcessDetach();
+ }
+ };
+}
+#pragma warning(pop)
+
+#endif // _MSC_VER >= 1400
+
+#endif // !STEAM && !NO_MALLOC_OVERRIDE
+
+#endif // _WIN32
diff --git a/extension/sdk/typeinfo.h b/extension/sdk/typeinfo.h
new file mode 100644
index 0000000..70cdb57
--- /dev/null
+++ b/extension/sdk/typeinfo.h
@@ -0,0 +1,2 @@
+// Simple hack for missing include "typeinfo.h"
+#include
\ No newline at end of file
diff --git a/extension/sendprop_hookmanager.cpp b/extension/sendprop_hookmanager.cpp
new file mode 100644
index 0000000..feec2f4
--- /dev/null
+++ b/extension/sendprop_hookmanager.cpp
@@ -0,0 +1,266 @@
+#include "sendprop_hookmanager.h"
+#include "clientpacks_detours.h"
+#include
+
+void GlobalProxy(const SendProp *pProp, const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID);
+
+SendProxyHook::SendProxyHook(SendProp *pProp, SendVarProxyFn pfnProxy)
+{
+ m_pProp = pProp;
+ m_fnRealProxy = m_pProp->GetProxyFn();
+ m_pProp->SetProxyFn( pfnProxy );
+}
+
+SendProxyHook::~SendProxyHook()
+{
+ m_pProp->SetProxyFn( m_fnRealProxy );
+ g_pSendPropHookManager->RemoveHook(m_pProp);
+}
+
+SendPropHookManager::SendPropHookManager()
+{
+ m_propHooks.init();
+ m_entityInfos.init();
+}
+
+void SendPropHookManager::Clear()
+{
+ m_propHooks.clear();
+ m_entityInfos.clear();
+ ClientPacksDetour::Clear();
+}
+
+void SendPropHookManager::RemoveHook(const SendProp *pProp)
+{
+ m_propHooks.removeIfExists(pProp);
+}
+
+void SendPropHookManager::RemoveEntity(int entity, std::function pred)
+{
+ auto r = m_entityInfos.find(entity);
+ if (!r.found())
+ return;
+
+ r->value.list.remove_if(pred);
+
+ if (r->value.list.empty())
+ {
+ OnEntityLeaveHook(entity);
+ m_entityInfos.remove(r);
+ }
+}
+
+void SendPropHookManager::RemoveEntity(SendPropEntityInfoMap::iterator it, std::function pred)
+{
+ it->value.list.remove_if(pred);
+
+ if (it->value.list.empty())
+ {
+ OnEntityLeaveHook(it->key);
+ it.erase();
+ }
+}
+
+void SendPropHookManager::OnEntityEnterHook(int entity)
+{
+ ClientPacksDetour::OnEntityHooked(entity);
+}
+
+void SendPropHookManager::OnEntityLeaveHook(int entity)
+{
+ ClientPacksDetour::OnEntityUnhooked(entity);
+}
+
+bool SendPropHookManager::HookEntity(int entity, SendProp *pProp, int element, PropType type, IPluginFunction *pFunc)
+{
+ SendPropHook hook;
+ hook.element = element;
+ hook.type = type;
+ hook.fnProcess = SendProxyPluginCallback;
+ hook.pCallback = pFunc;
+ hook.pOwner = pFunc->GetParentRuntime();
+
+ auto i = m_propHooks.findForAdd(pProp);
+ Assert(!i.found() || !i->value.expired());
+
+ if (i.found())
+ {
+ hook.proxy = i->value.lock();
+ }
+
+ if (!hook.proxy)
+ {
+ hook.proxy = std::make_shared(pProp, GlobalProxy);
+ m_propHooks.add(i, pProp, hook.proxy);
+ }
+
+ auto ii = m_entityInfos.findForAdd(entity);
+ if (!ii.found())
+ {
+ m_entityInfos.add(ii, entity);
+ OnEntityEnterHook(entity);
+ }
+ ii->value.list.emplace_front(std::move(hook));
+
+ return true;
+}
+
+void SendPropHookManager::UnhookEntity(int entity, const SendProp *pProp, int element, const void *pCallback)
+{
+ RemoveEntity(entity, [&](const SendPropHook &hook)
+ { return hook.proxy->GetProp() == pProp && hook.element == element && hook.pCallback == pCallback; });
+}
+
+void SendPropHookManager::UnhookEntityAll(int entity)
+{
+ RemoveEntity(entity, [&](const SendPropHook &hook)
+ { return true; });
+}
+
+void SendPropHookManager::OnPluginUnloaded(IPlugin *plugin)
+{
+ for (auto it = m_entityInfos.iter(); !it.empty(); it.next())
+ {
+ RemoveEntity(it, [pOwner = plugin->GetRuntime()](const SendPropHook &hook)
+ { return hook.pOwner == pOwner; });
+ }
+}
+
+void SendPropHookManager::OnExtentionUnloaded(IExtension *ext)
+{
+ for (auto it = m_entityInfos.iter(); !it.empty(); it.next())
+ {
+ RemoveEntity(it, [pOwner = ext](const SendPropHook &hook)
+ { return hook.pOwner == pOwner; });
+ }
+}
+
+SendPropEntityInfo* SendPropHookManager::GetEntityHooks(int entity)
+{
+ auto r = m_entityInfos.find(entity);
+ if (!r.found())
+ return nullptr;
+
+ return &r->value;
+}
+
+std::shared_ptr SendPropHookManager::GetPropHook(const SendProp *pProp)
+{
+ auto r = m_propHooks.find(pProp);
+ if (!r.found() || r->value.expired())
+ return nullptr;
+
+ return r->value.lock();
+}
+
+bool SendPropHookManager::IsPropHooked(const SendProp *pProp) const
+{
+ auto r = m_propHooks.find(pProp);
+ return r.found() && !r->value.expired();
+}
+
+bool SendPropHookManager::IsEntityHooked(int entity) const
+{
+ auto r = m_entityInfos.find(entity);
+ return r.found();
+}
+
+bool SendPropHookManager::IsEntityHooked(int entity, const SendProp *pProp, int element, const IPluginFunction *pFunc) const
+{
+ auto r = m_entityInfos.find(entity);
+ if (!r.found())
+ return false;
+
+ return std::any_of(r->value.list.cbegin(), r->value.list.cend(), [&](const SendPropHook &hook) {
+ return hook.proxy->GetProp() == pProp
+ && hook.pCallback == (void *)pFunc
+ && ((hook.proxy->GetProp()->GetType() != DPT_Array && hook.proxy->GetProp()->GetType() != DPT_DataTable)
+ || hook.element == element);
+ });
+}
+
+bool SendPropHookManager::IsAnyEntityHooked() const
+{
+ return m_entityInfos.elements() > 0;
+}
+
+class TailInvoker
+{
+public:
+ explicit TailInvoker(std::function fn) : m_call(fn) {}
+ TailInvoker() = delete;
+ TailInvoker(const TailInvoker &other) = delete;
+ ~TailInvoker() { m_call(); }
+
+private:
+ std::function m_call;
+};
+
+// !! MUST BE CALLED IN MAIN THREAD
+void GlobalProxy(const SendProp *pProp, const void *pStructBase, const void * pData, DVariant *pOut, int iElement, int objectID)
+{
+ auto pHook = g_pSendPropHookManager->GetPropHook(pProp);
+ Assert(pHook != nullptr);
+ if (!pHook)
+ {
+ LogError("FATAL: Leftover entity proxy %s", pProp->GetName());
+ return;
+ }
+
+ ProxyVariant *pOverride = nullptr;
+ TailInvoker finally(
+ [&]() -> void
+ {
+ if (pOverride) {
+ const void *pNewData = nullptr;
+ std::visit(overloaded {
+ [&pNewData](const auto &arg) { pNewData = &arg; },
+ [&pNewData](const CBaseHandle &arg) { pNewData = &arg; },
+ [&pNewData](const std::string &arg) { pNewData = arg.c_str(); },
+ }, *pOverride);
+
+ pHook->CallOriginal(pStructBase, pNewData, pOut, iElement, objectID);
+ } else {
+ pHook->CallOriginal(pStructBase, pData, pOut, iElement, objectID);
+ }
+ }
+ );
+
+ SendPropEntityInfo *pEntHook = g_pSendPropHookManager->GetEntityHooks(objectID);
+ if (!pEntHook)
+ return;
+
+ int client = ClientPacksDetour::GetCurrentClientIndex();
+ if (client == -1)
+ return;
+
+ for (const SendPropHook& hook : pEntHook->list)
+ {
+ if (hook.proxy->GetProp() != pProp)
+ continue;
+
+ if (pProp->IsInsideArray() && hook.element != iElement)
+ continue;
+
+ if (hook.type == PropType::Prop_Int) {
+ pEntHook->data = *reinterpret_cast(pData);
+ } else if (hook.type == PropType::Prop_Float) {
+ pEntHook->data = *reinterpret_cast(pData);
+ } else if (hook.type == PropType::Prop_Vector) {
+ pEntHook->data = *reinterpret_cast(pData);
+ } else if (hook.type == PropType::Prop_String) {
+ pEntHook->data.emplace(reinterpret_cast(pData), DT_MAX_STRING_BUFFERSIZE);
+ } else if (hook.type == PropType::Prop_EHandle) {
+ pEntHook->data = *reinterpret_cast(pData);
+ } else {
+ LogError("%s: SendProxy report: Unknown prop type (%s).", __func__, pProp->GetName());
+ continue;
+ }
+
+ if (hook.fnProcess(hook.pCallback, pProp, pEntHook->data, hook.element, objectID, client))
+ {
+ pOverride = &pEntHook->data;
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/extension/sendprop_hookmanager.h b/extension/sendprop_hookmanager.h
new file mode 100644
index 0000000..61acfcf
--- /dev/null
+++ b/extension/sendprop_hookmanager.h
@@ -0,0 +1,100 @@
+#ifndef _SENDPROP_HOOKMANAGER_H
+#define _SENDPROP_HOOKMANAGER_H
+
+#include "extension.h"
+#include "am-hashmap.h"
+#include "sendproxy_callback.h"
+#include "sendproxy_variant.h"
+#include
+#include
+#include
+
+class SendProxyHook
+{
+public:
+ explicit SendProxyHook(SendProp *pProp, SendVarProxyFn pfnProxy);
+ ~SendProxyHook();
+ SendProxyHook(const SendProxyHook &other) = delete;
+
+ const SendProp* GetProp() const { return m_pProp; }
+ void CallOriginal(const void *pStructBase, const void *pData, DVariant *pOut, int iElement, int objectID)
+ {
+ m_fnRealProxy(m_pProp, pStructBase, pData, pOut, iElement, objectID);
+ }
+
+private:
+ SendProp *m_pProp;
+ SendVarProxyFn m_fnRealProxy;
+};
+
+struct SendPropHook
+{
+ std::shared_ptr proxy{nullptr};
+ std::function fnProcess{nullptr};
+ void *pCallback{nullptr};
+ void *pOwner{nullptr};
+ int element{-1};
+ PropType type{PropType::Prop_Max};
+};
+
+struct SendPropEntityInfo
+{
+ std::forward_list list;
+ ProxyVariant data;
+};
+
+class SendPropHookManager
+{
+protected:
+ struct IntHashMapPolicy
+ {
+ static inline bool matches(const int32_t lookup, const int32_t compare) {
+ return lookup == compare;
+ }
+ static inline uint32_t hash(const int32_t key) {
+ return ke::HashInt32(key);
+ }
+ };
+
+ using SendPropHookMap = ke::HashMap, ke::PointerPolicy>;
+ using SendPropEntityInfoMap = ke::HashMap;
+
+public:
+ SendPropHookManager();
+ SendPropHookManager(const SendPropHookManager &other) = delete;
+ SendPropHookManager(SendPropHookManager &&other) = delete;
+
+ bool HookEntity(int entity, SendProp *pProp, int element, PropType type, IPluginFunction *callback);
+ void UnhookEntity(int entity, const SendProp *pProp, int element, const void *callback);
+ void UnhookEntityAll(int entity);
+ SendPropEntityInfo* GetEntityHooks(int entity);
+
+ void OnPluginUnloaded(IPlugin *plugin);
+ void OnExtentionUnloaded(IExtension *ext);
+
+ std::shared_ptr GetPropHook(const SendProp *pProp);
+ bool IsPropHooked(const SendProp *pProp) const;
+ bool IsEntityHooked(int entity) const;
+ bool IsEntityHooked(int entity, const SendProp *pProp, int element, const IPluginFunction *pFunc) const;
+ bool IsAnyEntityHooked() const;
+
+ void Clear();
+
+protected:
+ friend class SendProxyHook;
+ void RemoveHook(const SendProp *pProp);
+
+ void RemoveEntity(int entity, std::function pred);
+ void RemoveEntity(SendPropEntityInfoMap::iterator it, std::function pred);
+
+ void OnEntityEnterHook(int entity);
+ void OnEntityLeaveHook(int entity);
+
+private:
+ SendPropHookMap m_propHooks;
+ SendPropEntityInfoMap m_entityInfos;
+};
+
+extern SendPropHookManager *g_pSendPropHookManager;
+
+#endif
\ No newline at end of file
diff --git a/extension/sendproxy_callback.cpp b/extension/sendproxy_callback.cpp
new file mode 100644
index 0000000..7a4e58f
--- /dev/null
+++ b/extension/sendproxy_callback.cpp
@@ -0,0 +1,61 @@
+#include "sendproxy_callback.h"
+#include "dt_send.h"
+
+bool SendProxyPluginCallback(void *callback, const SendProp *pProp, ProxyVariant &variant, int element, int entity, int client)
+{
+ auto func = static_cast(callback);
+
+ cell_t result = Pl_Continue;
+
+ if (gamehelpers->ReferenceToEntity(entity) != GetGameRulesProxyEnt())
+ func->PushCell(entity);
+
+ func->PushString(pProp->GetName());
+
+ ProxyVariant temp = variant;
+ cell_t iEntity = -1;
+
+ std::visit(overloaded {
+ [func](int &arg) { func->PushCellByRef(reinterpret_cast(&arg)); },
+ [func](float &arg) { func->PushFloatByRef(&arg); },
+ [func](std::string& arg) { func->PushStringEx(arg.data(), arg.capacity(), SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); func->PushCell(arg.capacity()); },
+ [func](Vector &arg) { func->PushArray(reinterpret_cast(&arg), 3, SM_PARAM_COPYBACK); },
+ [func, &iEntity](CBaseHandle &arg) {
+ if (edict_t *edict = gamehelpers->GetHandleEntity(arg))
+ iEntity = gamehelpers->IndexOfEdict(edict);
+ func->PushCellByRef(&iEntity);
+ },
+ }, temp);
+
+ func->PushCell(element);
+ func->PushCell(client);
+ func->Execute(&result);
+
+ if (result == Pl_Changed)
+ {
+ bool intercept = true;
+
+ std::visit(overloaded {
+ [&variant](auto &arg) {
+ variant = arg;
+ },
+
+ [func, iEntity, &intercept](CBaseHandle &arg) {
+ if (iEntity == -1) {
+ arg.Term();
+ } else if (edict_t *edict = gamehelpers->EdictOfIndex(iEntity)) {
+ gamehelpers->SetHandleEntity(arg, edict);
+ } else {
+ func->GetParentRuntime()->GetDefaultContext()->BlamePluginError(
+ func, "Unexpected invalid edict index (%d)", iEntity);
+
+ intercept = false;
+ }
+ },
+ }, temp);
+
+ return intercept;
+ }
+
+ return false;
+}
diff --git a/extension/sendproxy_callback.h b/extension/sendproxy_callback.h
new file mode 100644
index 0000000..6422b5c
--- /dev/null
+++ b/extension/sendproxy_callback.h
@@ -0,0 +1,12 @@
+#ifndef _SENDPROXY_CALLBACK_H
+#define _SENDPROXY_CALLBACK_H
+
+#include "extension.h"
+#include "sendproxy_variant.h"
+
+using SendProxyCallback = bool (void *callback, const SendProp *pProp, ProxyVariant &variant, int element, int entity, int client);
+
+bool SendProxyPluginCallback(void *callback, const SendProp *pProp, ProxyVariant &variant, int element, int entity, int client);
+// bool SendProxyExtCallback(void *callback, const SendProp *pProp, ProxyVariant &variant, int element, int entity, int client);
+
+#endif
\ No newline at end of file
diff --git a/extension/sendproxy_variant.h b/extension/sendproxy_variant.h
new file mode 100644
index 0000000..17262b2
--- /dev/null
+++ b/extension/sendproxy_variant.h
@@ -0,0 +1,17 @@
+#ifndef _SENDPROXY_VARIANT_H
+#define _SENDPROXY_VARIANT_H
+
+#include
+#include
+#include "mathlib/vector.h"
+#include "basehandle.h"
+
+using ProxyVariant = std::variant;
+
+// helper type for the visitor
+template
+struct overloaded : Ts... { using Ts::operator()...; };
+template
+overloaded(Ts...) -> overloaded;
+
+#endif
\ No newline at end of file
diff --git a/extension/smsdk_config.h b/extension/smsdk_config.h
index a9f97af..3a8906d 100644
--- a/extension/smsdk_config.h
+++ b/extension/smsdk_config.h
@@ -40,9 +40,9 @@
/* 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.3"
-#define SMEXT_CONF_AUTHOR "afronanny & AlliedModders community"
-#define SMEXT_CONF_URL "https://forums.alliedmods.net/showthread.php?t=169795"
+#define SMEXT_CONF_VERSION "2.0.0"
+#define SMEXT_CONF_AUTHOR "afronanny & AlliedModders community, Forgetest"
+#define SMEXT_CONF_URL "https://github.com/jensewe/Left4SendProxy"
#define SMEXT_CONF_LOGTAG "SENDPROXY"
#define SMEXT_CONF_LICENSE "GPL"
#define SMEXT_CONF_DATESTRING __DATE__
@@ -77,6 +77,6 @@
//#define SMEXT_ENABLE_USERMSGS
//#define SMEXT_ENABLE_TRANSLATOR
//#define SMEXT_ENABLE_NINVOKE
-#define SMEXT_ENABLE_ROOTCONSOLEMENU
+//#define SMEXT_ENABLE_ROOTCONSOLEMENU
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
diff --git a/extension/util.h b/extension/util.h
new file mode 100644
index 0000000..868bfda
--- /dev/null
+++ b/extension/util.h
@@ -0,0 +1,215 @@
+#pragma once
+
+#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; \
+ }
+
+#define DECL_DETOUR(name) \
+ CDetour *name##_Detour = nullptr;
+
+#define CREATE_DETOUR(name, signname, var) \
+ name##_Detour = DETOUR_CREATE_MEMBER(name, signname); \
+ if (name##_Detour != NULL) \
+ { \
+ name##_Detour->EnableDetour(); \
+ var &= true; \
+ } else { \
+ LogError("Failed to create " signname " detour, check error log.\n"); \
+ var = false; \
+ }
+
+#define CREATE_DETOUR_STATIC(name, signname, var) \
+ name##_Detour = DETOUR_CREATE_STATIC(name, signname); \
+ if (name##_Detour != NULL) \
+ { \
+ name##_Detour->EnableDetour(); \
+ var &= true; \
+ } else { \
+ LogError("Failed to create " signname " detour, check error log.\n"); \
+ var = false; \
+ }
+
+#define DESTROY_DETOUR(name) \
+ if (name##_Detour != nullptr) \
+{ \
+ name##_Detour->Destroy(); \
+ name##_Detour = nullptr; \
+}
+
+template
+inline void LogMessage(const char *format, Args... args)
+{
+ g_pSM->LogMessage(myself, format, args...);
+}
+
+template
+inline void LogError(const char *format, Args... args)
+{
+ g_pSM->LogError(myself, format, args...);
+}
+
+class ConVarScopedSet
+{
+public:
+ explicit ConVarScopedSet(ConVar *cvar, const char *value)
+ : m_cvar(cvar), m_savevalue(m_cvar->GetString())
+ {
+ m_cvar->SetValue(value);
+ }
+
+ ConVarScopedSet() = delete;
+ ConVarScopedSet(const ConVarScopedSet &other) = delete;
+
+ ~ConVarScopedSet()
+ {
+ m_cvar->SetValue(m_savevalue.data());
+ }
+
+private:
+ ConVar *m_cvar;
+ std::string m_savevalue;
+};
+
+class AutoGameConfig
+{
+public:
+ static std::optional Load(const char *name)
+ {
+ char buffer[256];
+ IGameConfig *gc;
+ if (!gameconfs->LoadGameConfigFile(name, &gc, buffer, sizeof(buffer)))
+ {
+ LogError("Could not read config file (%s) (%s)", name, buffer);
+ return {};
+ }
+ return AutoGameConfig(gc);
+ }
+
+protected:
+ AutoGameConfig(IGameConfig *gc) : gc_(gc) {}
+
+public:
+ AutoGameConfig() : gc_(nullptr) {}
+
+ AutoGameConfig(AutoGameConfig &&other) noexcept
+ {
+ gc_ = other.gc_;
+ other.gc_ = nullptr;
+ }
+ AutoGameConfig &operator=(AutoGameConfig &&other)
+ {
+ gc_ = other.gc_;
+ other.gc_ = nullptr;
+ return *this;
+ }
+
+ AutoGameConfig(const AutoGameConfig &other) = delete;
+ AutoGameConfig &operator=(const AutoGameConfig &other) = delete;
+
+ ~AutoGameConfig()
+ {
+ Destroy();
+ }
+
+ void Destroy() noexcept
+ {
+ if (gc_ != nullptr)
+ {
+ gameconfs->CloseGameConfigFile(gc_);
+ gc_ = nullptr;
+ }
+ }
+
+ operator IGameConfig *() const noexcept
+ {
+ return gc_;
+ }
+
+ IGameConfig *operator->() const noexcept
+ {
+ return gc_;
+ }
+
+private:
+ IGameConfig *gc_;
+};
+
+#define GAMECONF_GETADDRESS(conf, key, var) \
+ do \
+ { \
+ if (!conf->GetAddress(key, (void **)var)) \
+ { \
+ ke::SafeStrcpy(error, maxlen, "Unable to find address (" key ")\n"); \
+ return false; \
+ } \
+ } while (false)
+
+#define GAMECONF_GETOFFSET(conf, key, var) \
+ do \
+ { \
+ if (!conf->GetOffset(key, (void **)var)) \
+ { \
+ ke::SafeStrcpy(error, maxlen, "Unable to find offset (" key ")\n"); \
+ return false; \
+ } \
+ } while (false)
+
+#define GAMECONF_GETSIGNATURE(conf, key, var) \
+ do \
+ { \
+ if (!conf->GetMemSig(key, (void **)var)) \
+ { \
+ ke::SafeStrcpy(error, maxlen, "Unable to find signature (" key ")\n"); \
+ return false; \
+ } \
+ } while (false)
+
+inline edict_t *UTIL_EdictOfIndex(int index)
+{
+ if (index < 0 || index >= MAX_EDICTS)
+ return nullptr;
+
+ return gamehelpers->EdictOfIndex(index);
+}
+
+extern CGlobalVars *gpGlobals;
+inline CBaseEntity *FindEntityByNetClass(int start, const char *classname)
+{
+ if (gpGlobals == nullptr)
+ return nullptr;
+
+ int maxEntities = gpGlobals->maxEntities;
+ for (int i = start; i < maxEntities; i++)
+ {
+ CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i);
+ if (pEntity == nullptr)
+ {
+ continue;
+ }
+
+ IServerNetworkable* pNetwork = ((IServerUnknown *)pEntity)->GetNetworkable();
+ if (pNetwork == nullptr)
+ {
+ continue;
+ }
+
+ ServerClass *pServerClass = pNetwork->GetServerClass();
+ if (pServerClass == nullptr)
+ {
+ continue;
+ }
+
+ const char *name = pServerClass->GetName();
+ if (!strcmp(name, classname))
+ {
+ return pEntity;
+ }
+ }
+
+ return nullptr;
+}
diff --git a/extension/wrappers.h b/extension/wrappers.h
index 0e152f4..6c00934 100644
--- a/extension/wrappers.h
+++ b/extension/wrappers.h
@@ -1,22 +1,162 @@
#ifndef _WRAPPERS_H_INC_
#define _WRAPPERS_H_INC_
-#include "tier0/threadtools.h"
+#include
#include "tier1/utlvector.h"
#include "tier1/utllinkedlist.h"
#include "tier1/mempool.h"
+#include "iclient.h"
+#include
+
+#if defined( _LINUX ) || defined( _OSX )
+// linux implementation
+namespace Wrappers {
+
+inline int32 ThreadInterlockedIncrement( int32 volatile *p )
+{
+ Assert( (size_t)p % 4 == 0 );
+ return __sync_fetch_and_add( p, 1 ) + 1;
+}
+
+inline int32 ThreadInterlockedDecrement( int32 volatile *p )
+{
+ Assert( (size_t)p % 4 == 0 );
+ return __sync_fetch_and_add( p, -1 ) - 1;
+}
+
+inline int32 ThreadInterlockedExchange( int32 volatile *p, int32 value )
+{
+ Assert( (size_t)p % 4 == 0 );
+ int32 nRet;
+
+ // Note: The LOCK instruction prefix is assumed on the XCHG instruction and GCC gets very confused on the Mac when we use it.
+ __asm __volatile(
+ "xchgl %2,(%1)"
+ : "=r" (nRet)
+ : "r" (p), "0" (value)
+ : "memory");
+ return nRet;
+}
+
+inline int32 ThreadInterlockedExchangeAdd( int32 volatile *p, int32 value )
+{
+ Assert( (size_t)p % 4 == 0 );
+ return __sync_fetch_and_add( p, value );
+}
+inline int32 ThreadInterlockedCompareExchange( int32 volatile *p, int32 value, int32 comperand )
+{
+ Assert( (size_t)p % 4 == 0 );
+ return __sync_val_compare_and_swap( p, comperand, value );
+}
+
+inline bool ThreadInterlockedAssignIf( int32 volatile *p, int32 value, int32 comperand )
+{
+ Assert( (size_t)p % 4 == 0 );
+ return __sync_bool_compare_and_swap( p, comperand, value );
+}
+
+}
+
+template
+class CInterlockedIntTHack
+{
+public:
+ CInterlockedIntTHack() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T) == sizeof(int32) ); }
+ CInterlockedIntTHack( T value ) : m_value( value ) {}
+
+ operator T() const { return m_value; }
+
+ bool operator!() const { return ( m_value == 0 ); }
+ bool operator==( T rhs ) const { return ( m_value == rhs ); }
+ bool operator!=( T rhs ) const { return ( m_value != rhs ); }
+
+ T operator++() { return (T)Wrappers::ThreadInterlockedIncrement( (int32 *)&m_value ); }
+ T operator++(int) { return operator++() - 1; }
+
+ T operator--() { return (T)Wrappers::ThreadInterlockedDecrement( (int32 *)&m_value ); }
+ T operator--(int) { return operator--() + 1; }
+
+ bool AssignIf( T conditionValue, T newValue ) { return Wrappers::ThreadInterlockedAssignIf( (int32 *)&m_value, (int32)newValue, (int32)conditionValue ); }
+
+ T operator=( T newValue ) { Wrappers::ThreadInterlockedExchange((int32 *)&m_value, newValue); return m_value; }
+
+ void operator+=( T add ) { Wrappers::ThreadInterlockedExchangeAdd( (int32 *)&m_value, (int32)add ); }
+ void operator-=( T subtract ) { operator+=( -subtract ); }
+ void operator*=( T multiplier ) {
+ T original, result;
+ do
+ {
+ original = m_value;
+ result = original * multiplier;
+ } while ( !AssignIf( original, result ) );
+ }
+ void operator/=( T divisor ) {
+ T original, result;
+ do
+ {
+ original = m_value;
+ result = original / divisor;
+ } while ( !AssignIf( original, result ) );
+ }
+
+ T operator+( T rhs ) const { return m_value + rhs; }
+ T operator-( T rhs ) const { return m_value - rhs; }
+
+private:
+ volatile T m_value;
+};
+#endif
class ServerClass;
class ClientClass;
-class CFrameSnapshotEntry;
-class CHLTVEntityData;
class CEventInfo;
+class CChangeFrameList;
+
+#define INVALID_PACKED_ENTITY_HANDLE (0)
+typedef int PackedEntityHandle_t;
+
+struct UnpackedDataCache_t;
+
+//-----------------------------------------------------------------------------
+// Purpose: Individual entity data, did the entity exist and what was it's serial number
+//-----------------------------------------------------------------------------
+class CFrameSnapshotEntry
+{
+public:
+ ServerClass* m_pClass;
+ int m_nSerialNumber;
+ // Keeps track of the fullpack info for this frame for all entities in any pvs:
+ PackedEntityHandle_t m_pPackedData;
+};
+
+// HLTV needs some more data per entity
+class CHLTVEntityData
+{
+public:
+ vec_t origin[3]; // entity position
+ unsigned int m_nNodeCluster; // if (1<<31) is set it's a node, otherwise a cluster
+};
class CFrameSnapshot
{
public:
+ static void *s_pfnReleaseReference;
+ static ICallWrapper *s_callReleaseReference;
+ inline void ReleaseReference()
+ {
+ struct {
+ CFrameSnapshot *pThis;
+ } stack{ this };
+
+ s_callReleaseReference->Execute(&stack, NULL);
+ }
+
// Index info CFrameSnapshotManager::m_FrameSnapshots.
+#if defined( _LINUX ) || defined( _OSX )
+ CInterlockedIntTHack m_ListIndex;
+#else
CInterlockedInt m_ListIndex;
+#endif
// Associated frame.
int m_nTickCount; // = sv.tickcount
@@ -38,42 +178,95 @@ class CFrameSnapshot
CUtlVector m_iExplicitDeleteSlots;
// Snapshots auto-delete themselves when their refcount goes to zero.
+#if defined( _LINUX ) || defined( _OSX )
+ CInterlockedIntTHack m_nReferences;
+#else
CInterlockedInt m_nReferences;
+#endif
};
class PackedEntity
{
public:
+ int GetSnapshotCreationTick() const;
+
ServerClass *m_pServerClass; // Valid on the server
ClientClass *m_pClientClass; // Valid on the client
int m_nEntityIndex; // Entity index.
+#if defined( _LINUX ) || defined( _OSX )
+ CInterlockedIntTHack m_ReferenceCount;
+#else
CInterlockedInt m_ReferenceCount; // reference count;
-};
+#endif
-#define INVALID_PACKED_ENTITY_HANDLE (0)
-typedef int PackedEntityHandle_t;
+ CUtlVector m_Recipients;
-typedef struct
+ void *m_pData; // Packed data.
+ int m_nBits; // Number of bits used to encode.
+ CChangeFrameList *m_pChangeFrameList; // Only the most current
+
+ // This is the tick this PackedEntity was created on
+ unsigned int m_nSnapshotCreationTick : 31;
+ unsigned int m_nShouldCheckCreationTick : 1;
+};
+
+inline int PackedEntity::GetSnapshotCreationTick() const
{
- PackedEntity *pEntity; // original packed entity
- int counter; // increaseing counter to find LRU entries
- int bits; // uncompressed data length in bits
- char data[MAX_PACKEDENTITY_DATA]; // uncompressed data cache
-} UnpackedDataCache_t;
+ return (int)m_nSnapshotCreationTick;
+}
class CFrameSnapshotManager
{
public:
virtual ~CFrameSnapshotManager( void );
+ static void* s_pfnCreateEmptySnapshot;
+ static ICallWrapper* s_callCreateEmptySnapshot;
+ inline CFrameSnapshot* CreateEmptySnapshot(int tickcount, int maxEntities)
+ {
+ struct {
+ CFrameSnapshotManager *pThis;
+ int tickcount;
+ int maxEntities;
+ } stack{ this, tickcount, maxEntities };
+
+ CFrameSnapshot *ret;
+ s_callCreateEmptySnapshot->Execute(&stack, &ret);
+ return ret;
+ }
+
+ inline void AddEntityReference( PackedEntityHandle_t handle )
+ {
+ m_PackedEntities[ handle ]->m_ReferenceCount++;
+ }
+
+ static void* s_pfnRemoveEntityReference;
+ static ICallWrapper* s_callRemoveEntityReference;
+ inline void RemoveEntityReference( PackedEntityHandle_t handle )
+ {
+ struct {
+ CFrameSnapshotManager *pThis;
+ PackedEntityHandle_t handle;
+ } stack{ this, handle };
+
+ s_callRemoveEntityReference->Execute(&stack, nullptr);
+ }
+
public:
- int pad[21];
+ uint32_t pad[21];
- CUtlFixedLinkedList m_PackedEntities;
+ template
+ class CUtlFixedLinkedListHack : public CUtlFixedLinkedList
+ {
+ private:
+ int m_NumAlloced; // The number of allocated elements
+ };
- // FIXME: Update CUtlFixedLinkedList in hl2sdk-l4d2
- int pad2;
+ std::conditional_t) == 40u,
+ CUtlFixedLinkedListHack,
+ CUtlFixedLinkedList
+ > m_PackedEntities;
int m_nPackedEntityCacheCounter; // increase with every cache access
CUtlVector m_PackedEntityCache; // cache for uncompressed packed entities
@@ -87,4 +280,23 @@ class CFrameSnapshotManager
CUtlVector m_iExplicitDeleteSlots;
};
+class CCheckTransmitInfo;
+class CGameClient
+{
+public:
+ int GetPlayerSlot()
+ {
+ IClient *pClient = reinterpret_cast((uint8_t*)this + 4);
+ return pClient->GetPlayerSlot();
+ }
+};
+
+extern CFrameSnapshotManager* framesnapshotmanager;
+
+#if SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2
+constexpr int MAXPLAYERS = 32;
+#else
+constexpr int MAXPLAYERS = SM_MAXPLAYERS;
+#endif
+
#endif
\ No newline at end of file
diff --git a/gamedata/sendproxy.txt b/gamedata/sendproxy.txt
new file mode 100644
index 0000000..03f5b07
--- /dev/null
+++ b/gamedata/sendproxy.txt
@@ -0,0 +1,114 @@
+"Games"
+{
+ "left4dead2"
+ {
+ "Addresses"
+ {
+ "framesnapshotmanager"
+ {
+ "linux"
+ {
+ "signature" "framesnapshotmanager"
+ }
+ "windows"
+ {
+ "signature" "CFrameSnapshot::ReleaseReference"
+ "read" "7"
+ }
+ "read" "0"
+ }
+
+ "g_ppLocalNetworkBackdoor"
+ {
+ "linux"
+ {
+ "signature" "g_pLocalNetworkBackdoor"
+ }
+ "windows"
+ {
+ "signature" "SV_ComputeClientPacks"
+ "read" "331"
+ }
+ }
+ }
+
+ "Signatures"
+ {
+ "framesnapshotmanager"
+ {
+ "library" "engine"
+ "linux" "@framesnapshotmanager"
+ }
+
+ "g_pLocalNetworkBackdoor"
+ {
+ "library" "engine"
+ "linux" "@g_pLocalNetworkBackdoor"
+ }
+
+ "SV_ComputeClientPacks"
+ {
+ "library" "engine"
+ "linux" "@_Z21SV_ComputeClientPacksiPP11CGameClientP14CFrameSnapshot"
+ "windows" "\x55\x8B\xEC\x83\xEC\x44\xA1\x2A\x2A\x2A\x2A\x53"
+ // 55 8B EC 83 EC 44 A1 ? ? ? ? 53
+ }
+
+ "PackEntities_Normal"
+ {
+ "library" "engine"
+ "linux" "@_Z19PackEntities_NormaliPP11CGameClientP14CFrameSnapshot"
+ "windows" "\x55\x8B\xEC\xB8\x48\x60\x00\x00"
+ // 55 8B EC B8 48 60 00 00
+ }
+
+ "CFrameSnapshotManager::UsePreviouslySentPacket"
+ {
+ "library" "engine"
+ "linux" "@_ZN21CFrameSnapshotManager23UsePreviouslySentPacketEP14CFrameSnapshotii"
+ "windows" "\x55\x8B\xEC\x56\x8B\x75\x0C\x57\x8B\xBC\xB1\x9C\x00\x00\x00"
+ // 55 8B EC 56 8B 75 0C 57 8B BC B1 9C 00 00 00
+ }
+
+ "CFrameSnapshotManager::GetPreviouslySentPacket"
+ {
+ "library" "engine"
+ "linux" "@_ZN21CFrameSnapshotManager23GetPreviouslySentPacketEii"
+ "windows" "\x55\x8B\xEC\x8B\x55\x08\x8B\x84\x91\x9C\x00\x00\x00"
+ }
+
+ "CFrameSnapshotManager::CreatePackedEntity"
+ {
+ "library" "engine"
+ "linux" "@_ZN21CFrameSnapshotManager18CreatePackedEntityEP14CFrameSnapshoti"
+ "windows" "\x55\x8B\xEC\x83\xEC\x0C\x53\x8B\xD9\x56"
+ // 55 8B EC 83 EC 0C 53 8B D9 56
+ }
+
+ "CFrameSnapshotManager::CreateEmptySnapshot"
+ {
+ "library" "engine"
+ "linux" "@_ZN21CFrameSnapshotManager19CreateEmptySnapshotEii"
+ "windows" "\x55\x8B\xEC\x83\xEC\x0C\x53\x56\x57\x89\x4D\xF8"
+ // 55 8B EC 83 EC 0C 53 56 57 89 4D F8
+ // Search string "Sending full update to Client %s (%s)", the called function with arg2 is 2048
+ }
+
+ "CFrameSnapshotManager::RemoveEntityReference"
+ {
+ "library" "engine"
+ "linux" "@_ZN21CFrameSnapshotManager21RemoveEntityReferenceEi"
+ "windows" "\x55\x8B\xEC\x51\x8B\x45\x08\x53\x8B\x18"
+ // 55 8B EC 51 8B 45 08 53 8B 18
+ }
+
+ "CFrameSnapshot::ReleaseReference"
+ {
+ "library" "engine"
+ "linux" "@_ZN14CFrameSnapshot16ReleaseReferenceEv"
+ "windows" "\x55\x8B\xEC\x51\x56\x8B\x35\x2A\x2A\x2A\x2A\x57"
+ // 55 8B EC 51 56 8B 35 ? ? ? ? 57
+ }
+ }
+ }
+}
diff --git a/scripting/include/sendproxy.inc b/scripting/include/sendproxy.inc
new file mode 100644
index 0000000..3cfa126
--- /dev/null
+++ b/scripting/include/sendproxy.inc
@@ -0,0 +1,124 @@
+#if defined _SENDPROXYMANAGER_INC_
+ #endinput
+#endif
+#define _SENDPROXYMANAGER_INC_
+
+#define SENDPROXY_LIB "sendproxy2"
+
+enum SendPropType
+{
+ Prop_Int,
+ Prop_Float,
+ Prop_String,
+ Prop_Vector,
+ Prop_EHandle,
+ Prop_Max
+};
+
+/**
+ * Callback for send proxy hooks.
+ *
+ * @param entity Index of the hooked entity.
+ * @param prop Name of the hooked send prop.
+ * @param value Prop value.
+ * @param element 0 if the hooked prop is not an array,
+ * otherwise an index into the array (starting from 0).
+ * @param client Index of the current processing client.
+ *
+ * @return Action Plugin_Changed to override value, otherwise ignored.
+ */
+typeset SendProxyCallback
+{
+ function Action (int entity, const char[] prop, int &value, int element, int client); //Prop_Int, Prop_EHandle
+ function Action (int entity, const char[] prop, float &value, int element, int client); //Prop_Float
+ function Action (int entity, const char[] prop, char[] value, int maxlength, int element, int client); //Prop_String
+ function Action (int entity, const char[] prop, float value[3], int element, int client); //Prop_Vector
+};
+
+/**
+ * Callback for gamerules send proxy hooks.
+ *
+ * @param prop Name of the hooked send prop.
+ * @param value Prop value.
+ * @param element 0 if the hooked prop is NOT an array (InsideArray),
+ * otherwise an index into the array (starting from 0).
+ * @param client Index of the current processing client.
+ *
+ * @return Action Plugin_Changed to override value, ignored otherwise.
+ */
+typeset SendProxyCallbackGamerules
+{
+ function Action (const char[] prop, int &value, int element, int client); //Prop_Int, Prop_EHandle
+ function Action (const char[] prop, float &value, int element, int client); //Prop_Float
+ function Action (const char[] prop, char[] value, int maxlength, int element, int client); //Prop_String
+ function Action (const char[] prop, float value[3], int element, int client); //Prop_Vector
+};
+
+/**
+ * Hook an entity's prop to override its value in callback without actually changing the prop.
+ * @note Callback function cannot be checked so make sure it matches the prop type.
+ *
+ * @param entity Entity index to hook.
+ * @param prop Send prop name.
+ * @param type Prop type. Reports an error if type is mismatched.
+ * @param callback Callback function.
+ * @param element Element of the prop. Has no effect if the prop is NOT an array or a table.
+ *
+ * @return bool True if success, false if already hooked.
+ */
+native bool SendProxy_HookEntity(int entity, const char[] prop, SendPropType type, SendProxyCallback callback, int element = 0);
+native bool SendProxy_HookGameRules(const char[] prop, SendPropType type, SendProxyCallbackGamerules callback, int element = 0);
+
+/**
+ * Unhook an entity's prop.
+ *
+ * @param entity Entity index to unhook.
+ * @param prop Send prop name.
+ * @param callback Callback function.
+ * @param element Element of the prop. Has no effect if the prop is NOT an array or a table.
+ *
+ * @return bool True if found, false otherwise.
+ */
+native bool SendProxy_UnhookEntity(int entity, const char[] prop, SendProxyCallback callback, int element = 0);
+native bool SendProxy_UnhookGameRules(const char[] prop, SendProxyCallbackGamerules callback, int element = 0);
+
+/**
+ * Test if an entity's prop is hooked.
+ *
+ * @param entity Entity index to hook.
+ * @param prop Send prop name.
+ * @param callback Callback function.
+ * @param element Element of the prop. Has no effect if the prop is NOT an array or a table.
+ *
+ * @return bool True if hooked, false otherwise.
+ */
+native bool SendProxy_IsHookedEntity(int entity, const char[] prop, SendProxyCallback callback, int element = 0);
+native bool SendProxy_IsHookedGameRules(const char[] prop, SendProxyCallbackGamerules callback, int element = 0);
+
+public __ext_sendproxymanager_SetNTVOptional()
+{
+#if !defined REQUIRE_EXTENSIONS
+ MarkNativeAsOptional("SendProxy_HookEntity");
+ MarkNativeAsOptional("SendProxy_HookGameRules");
+ MarkNativeAsOptional("SendProxy_UnhookEntity");
+ MarkNativeAsOptional("SendProxy_UnhookGameRules");
+ MarkNativeAsOptional("SendProxy_IsHookedEntity");
+ MarkNativeAsOptional("SendProxy_IsHookedGameRules");
+#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
+};
\ No newline at end of file