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