commit d241bda1f8d3d8e5b6eb99f95ca389ab2fecc624 Author: BotoX Date: Wed Dec 18 12:04:10 2019 +0100 initial commit diff --git a/AMBuildScript b/AMBuildScript new file mode 100644 index 0000000..3e8cee8 --- /dev/null +++ b/AMBuildScript @@ -0,0 +1,448 @@ +# 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) + + # Optimizaiton + 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', + ] + cxx.cxxflags += [ + '-std=c++11', + '-fno-exceptions', + '-fno-threadsafe-statics', + '-Wno-non-virtual-dtor', + '-Wno-overloaded-virtual', + '-fvisibility-inlines-hidden', + ] + cxx.linkflags += ['-m32'] + + 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/AMBuilder b/AMBuilder new file mode 100644 index 0000000..06594c5 --- /dev/null +++ b/AMBuilder @@ -0,0 +1,24 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: +import os, sys + +projectName = 'PhysHooks' + +project = Extension.HL2Project(builder, projectName + '.ext') +project.sources += [ + 'extension.cpp', + '../../public/smsdk_ext.cpp', + '../../public/CDetour/detours.cpp', + '../../public/asm/asm.c', + '../../public/libudis86/decode.c', + '../../public/libudis86/itab.c', + '../../public/libudis86/syn-att.c', + '../../public/libudis86/syn-intel.c', + '../../public/libudis86/syn.c', + '../../public/libudis86/udis86.c', +] + +for sdk_name in Extension.sdks: + sdk = Extension.sdks[sdk_name] + binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk) + +Extension.extensions = builder.Add(project) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..eedafa6 --- /dev/null +++ b/Makefile @@ -0,0 +1,233 @@ +# (C)2004-2010 SourceMod Development Team +# Makefile written by David "BAILOPAN" Anderson + +########################################### +### EDIT THESE PATHS FOR YOUR OWN SETUP ### +########################################### + +SMSDK = ../.. +HL2SDK_ORIG = ../../../hl2sdk +HL2SDK_OB = ../../../hl2sdk-ob +HL2SDK_CSS = ../../../hl2sdk-css +HL2SDK_OB_VALVE = ../../../hl2sdk-ob-valve +HL2SDK_L4D = ../../../hl2sdk-l4d +HL2SDK_L4D2 = ../../../hl2sdk-l4d2 +HL2SDK_CSGO = ../../../hl2sdk-csgo +MMSOURCE19 = ../../../mmsource-1.9 + +##################################### +### EDIT BELOW FOR OTHER PROJECTS ### +##################################### + +PROJECT = sample + +#Uncomment for Metamod: Source enabled extension +#USEMETA = true + +OBJECTS = smsdk_ext.cpp extension.cpp + +############################################## +### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ### +############################################## + +C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing +C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3 +C_GCC4_FLAGS = -fvisibility=hidden +CPP_GCC4_FLAGS = -fvisibility-inlines-hidden +CPP = gcc +CPP_OSX = clang + +########################## +### SDK CONFIGURATIONS ### +########################## + +override ENGSET = false + +# Check for valid list of engines +ifneq (,$(filter original orangebox orangeboxvalve css left4dead left4dead2 csgo,$(ENGINE))) + override ENGSET = true +endif + +ifeq "$(ENGINE)" "original" + HL2SDK = $(HL2SDK_ORIG) + CFLAGS += -DSOURCE_ENGINE=1 +endif +ifeq "$(ENGINE)" "orangebox" + HL2SDK = $(HL2SDK_OB) + CFLAGS += -DSOURCE_ENGINE=3 +endif +ifeq "$(ENGINE)" "css" + HL2SDK = $(HL2SDK_CSS) + CFLAGS += -DSOURCE_ENGINE=6 +endif +ifeq "$(ENGINE)" "orangeboxvalve" + HL2SDK = $(HL2SDK_OB_VALVE) + CFLAGS += -DSOURCE_ENGINE=7 +endif +ifeq "$(ENGINE)" "left4dead" + HL2SDK = $(HL2SDK_L4D) + CFLAGS += -DSOURCE_ENGINE=8 +endif +ifeq "$(ENGINE)" "left4dead2" + HL2SDK = $(HL2SDK_L4D2) + CFLAGS += -DSOURCE_ENGINE=9 +endif +ifeq "$(ENGINE)" "csgo" + HL2SDK = $(HL2SDK_CSGO) + CFLAGS += -DSOURCE_ENGINE=12 +endif + +HL2PUB = $(HL2SDK)/public + +ifeq "$(ENGINE)" "original" + INCLUDE += -I$(HL2SDK)/public/dlls + METAMOD = $(MMSOURCE19)/core-legacy +else + INCLUDE += -I$(HL2SDK)/public/game/server + METAMOD = $(MMSOURCE19)/core +endif + +OS := $(shell uname -s) + +ifeq "$(OS)" "Darwin" + LIB_EXT = dylib + HL2LIB = $(HL2SDK)/lib/mac +else + LIB_EXT = so + ifeq "$(ENGINE)" "original" + HL2LIB = $(HL2SDK)/linux_sdk + else + HL2LIB = $(HL2SDK)/lib/linux + endif +endif + +# if ENGINE is original or OB +ifneq (,$(filter original orangebox,$(ENGINE))) + LIB_SUFFIX = _i486.$(LIB_EXT) +else + LIB_PREFIX = lib + LIB_SUFFIX = .$(LIB_EXT) +endif + +INCLUDE += -I. -I.. -Isdk -I$(SMSDK)/public -I$(SMSDK)/sourcepawn/include + +ifeq "$(USEMETA)" "true" + LINK_HL2 = $(HL2LIB)/tier1_i486.a $(LIB_PREFIX)vstdlib$(LIB_SUFFIX) $(LIB_PREFIX)tier0$(LIB_SUFFIX) + ifeq "$(ENGINE)" "csgo" + LINK_HL2 += $(HL2LIB)/interfaces_i486.a + endif + + LINK += $(LINK_HL2) + + INCLUDE += -I$(HL2PUB) -I$(HL2PUB)/engine -I$(HL2PUB)/tier0 -I$(HL2PUB)/tier1 -I$(METAMOD) \ + -I$(METAMOD)/sourcehook + CFLAGS += -DSE_EPISODEONE=1 -DSE_DARKMESSIAH=2 -DSE_ORANGEBOX=3 -DSE_BLOODYGOODTIME=4 -DSE_EYE=5 \ + -DSE_CSS=6 -DSE_ORANGEBOXVALVE=7 -DSE_LEFT4DEAD=8 -DSE_LEFT4DEAD2=9 -DSE_ALIENSWARM=10 \ + -DSE_PORTAL2=11 -DSE_CSGO=12 +endif + +LINK += -m32 -lm -ldl + +CFLAGS += -DPOSIX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \ + -D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -DCOMPILER_GCC -Wall -Werror \ + -Wno-overloaded-virtual -Wno-switch -Wno-unused -msse -DSOURCEMOD_BUILD -DHAVE_STDINT_H -m32 +CPPFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti -std=c++11 + +################################################ +### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ### +################################################ + +BINARY = $(PROJECT).ext.$(LIB_EXT) + +ifeq "$(DEBUG)" "true" + BIN_DIR = Debug + CFLAGS += $(C_DEBUG_FLAGS) +else + BIN_DIR = Release + CFLAGS += $(C_OPT_FLAGS) +endif + +ifeq "$(USEMETA)" "true" + BIN_DIR := $(BIN_DIR).$(ENGINE) +endif + +ifeq "$(OS)" "Darwin" + CPP = $(CPP_OSX) + LIB_EXT = dylib + CFLAGS += -DOSX -D_OSX + LINK += -dynamiclib -lstdc++ -mmacosx-version-min=10.5 +else + LIB_EXT = so + CFLAGS += -D_LINUX + LINK += -shared +endif + +IS_CLANG := $(shell $(CPP) --version | head -1 | grep clang > /dev/null && echo "1" || echo "0") + +ifeq "$(IS_CLANG)" "1" + CPP_MAJOR := $(shell $(CPP) --version | grep clang | sed "s/.*version \([0-9]\)*\.[0-9]*.*/\1/") + CPP_MINOR := $(shell $(CPP) --version | grep clang | sed "s/.*version [0-9]*\.\([0-9]\)*.*/\1/") +else + CPP_MAJOR := $(shell $(CPP) -dumpversion >&1 | cut -b1) + CPP_MINOR := $(shell $(CPP) -dumpversion >&1 | cut -b3) +endif + +# If not clang +ifeq "$(IS_CLANG)" "0" + CFLAGS += -mfpmath=sse +endif + +# Clang || GCC >= 4 +ifeq "$(shell expr $(IS_CLANG) \| $(CPP_MAJOR) \>= 4)" "1" + CFLAGS += $(C_GCC4_FLAGS) + CPPFLAGS += $(CPP_GCC4_FLAGS) +endif + +# Clang >= 3 || GCC >= 4.7 +ifeq "$(shell expr $(IS_CLANG) \& $(CPP_MAJOR) \>= 3 \| $(CPP_MAJOR) \>= 4 \& $(CPP_MINOR) \>= 7)" "1" + CFLAGS += -Wno-delete-non-virtual-dtor +endif + +# OS is Linux and not using clang +ifeq "$(shell expr $(OS) \= Linux \& $(IS_CLANG) \= 0)" "1" + LINK += -static-libgcc +endif + +OBJ_BIN := $(OBJECTS:%.cpp=$(BIN_DIR)/%.o) + +# This will break if we include other Makefiles, but is fine for now. It allows +# us to make a copy of this file that uses altered paths (ie. Makefile.mine) +# or other changes without mucking up the original. +MAKEFILE_NAME := $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) + +$(BIN_DIR)/%.o: %.cpp + $(CPP) $(INCLUDE) $(CFLAGS) $(CPPFLAGS) -o $@ -c $< + +all: check + mkdir -p $(BIN_DIR) + ln -sf ../smsdk_ext.cpp + if [ "$(USEMETA)" = "true" ]; then \ + ln -sf $(HL2LIB)/$(LIB_PREFIX)vstdlib$(LIB_SUFFIX); \ + ln -sf $(HL2LIB)/$(LIB_PREFIX)tier0$(LIB_SUFFIX); \ + fi + $(MAKE) -f $(MAKEFILE_NAME) extension + +check: + if [ "$(USEMETA)" = "true" ] && [ "$(ENGSET)" = "false" ]; then \ + echo "You must supply one of the following values for ENGINE:"; \ + echo "csgo, left4dead2, left4dead, css, orangeboxvalve, orangebox, or original"; \ + exit 1; \ + fi + +extension: check $(OBJ_BIN) + $(CPP) $(INCLUDE) $(OBJ_BIN) $(LINK) -o $(BIN_DIR)/$(BINARY) + +debug: + $(MAKE) -f $(MAKEFILE_NAME) all DEBUG=true + +default: all + +clean: check + rm -rf $(BIN_DIR)/*.o + rm -rf $(BIN_DIR)/$(BINARY) + diff --git a/PackageScript b/PackageScript new file mode 100644 index 0000000..303bd0d --- /dev/null +++ b/PackageScript @@ -0,0 +1,42 @@ +# 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/include', + 'addons/sourcemod/gamedata', +] + +# Create the distribution folder hierarchy. +folder_map = {} +for folder in folder_list: + norm_folder = os.path.normpath(folder) + folder_map[folder] = builder.AddFolder(norm_folder) + +# Do all straight-up file copies from the source tree. +def CopyFiles(src, dest, files): + if not dest: + dest = src + dest_entry = folder_map[dest] + for source_file in files: + source_path = os.path.join(builder.sourcePath, src, source_file) + builder.AddCopy(source_path, dest_entry) + +# Include files +CopyFiles('include', 'addons/sourcemod/include', + [ 'PhysHooks.inc', ] +) + +# GameData files +CopyFiles('gamedata', 'addons/sourcemod/gamedata', + [ 'PhysHooks.games.txt' ] +) + +# Copy binaries. +for cxx_task in Extension.extensions: + builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions']) diff --git a/configure.py b/configure.py new file mode 100644 index 0000000..57910e8 --- /dev/null +++ b/configure.py @@ -0,0 +1,23 @@ +# vim: set sts=2 ts=8 sw=2 tw=99 et: +import sys +from ambuild2 import run + +# Simple extensions do not need to modify this file. + +builder = run.PrepareBuild(sourcePath = sys.path[0]) + +builder.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, + help='Root search folder for HL2SDKs') +builder.options.add_option('--mms-path', type=str, dest='mms_path', default=None, + help='Path to Metamod:Source') +builder.options.add_option('--sm-path', type=str, dest='sm_path', default=None, + help='Path to SourceMod') +builder.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', + help='Enable debugging symbols') +builder.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', + help='Enable optimization') +builder.options.add_option('-s', '--sdks', default='all', dest='sdks', + help='Build against specified SDKs; valid args are "all", "present", or ' + 'comma-delimited list of engine names (default: %default)') + +builder.Configure() diff --git a/extension.cpp b/extension.cpp new file mode 100644 index 0000000..14259a9 --- /dev/null +++ b/extension.cpp @@ -0,0 +1,604 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod PhysHooks Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "extension.h" +#include "CDetour/detours.h" +#include +#include +#include +#include + +#define SetBit(A,I) ((A)[(I) >> 5] |= (1 << ((I) & 31))) +#define ClearBit(A,I) ((A)[(I) >> 5] &= ~(1 << ((I) & 31))) +#define CheckBit(A,I) !!((A)[(I) >> 5] & (1 << ((I) & 31))) + +class CTriggerMoved : public IPartitionEnumerator +{ +public: + virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) = 0; +}; + +class CTouchLinks : public IPartitionEnumerator +{ +public: + virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity ) = 0; +}; + +struct SrcdsPatch +{ + const char *pSignature; + const unsigned char *pPatchSignature; + const char *pPatchPattern; + const unsigned char *pPatch; + + unsigned char *pOriginal; + uintptr_t pAddress; + uintptr_t pPatchAddress; + bool engine; +} gs_Patches[] = { + // Hook: Replace call inside Physics_RunThinkFunctions + { + "_Z25Physics_RunThinkFunctionsb", + (unsigned char *)"\x8B\x04\x9E\x85\xC0\x74\x13\xA1\x00\x00\x00\x00\x89\x78\x0C\x8B\x04\x9E\x89\x04\x24\xE8\x00\x00\x00\x00", + "xxxxxxxx????xxxxxxxxxx????", + NULL, + 0, 0, 0, false + } +}; + +uintptr_t FindPattern(uintptr_t BaseAddr, const unsigned char *pData, const char *pPattern, size_t MaxSize); + +/** + * @file extension.cpp + * @brief Implement extension code here. + */ + +PhysHooks g_Interface; /**< Global singleton for extension's main interface */ + +SMEXT_LINK(&g_Interface); + +CGlobalVars *gpGlobals = NULL; +IGameConfig *g_pGameConf = NULL; + +CDetour *g_pDetour_RunThinkFunctions = NULL; + +IForward *g_pOnRunThinkFunctions = NULL; +IForward *g_pOnPrePlayerThinkFunctions = NULL; +IForward *g_pOnPostPlayerThinkFunctions = NULL; +IForward *g_pOnRunThinkFunctionsPost = NULL; + +int g_SH_TriggerMoved = 0; +int g_SH_TouchLinks = 0; + +CTriggerMoved *g_CTriggerMoved = NULL; +CTouchLinks *g_CTouchLinks = NULL; + + +DETOUR_DECL_STATIC1(DETOUR_RunThinkFunctions, void, bool, simulating) +{ + if(g_pOnRunThinkFunctions->GetFunctionCount()) + { + g_pOnRunThinkFunctions->PushCell(simulating); + g_pOnRunThinkFunctions->Execute(); + } + + if(g_pOnPrePlayerThinkFunctions->GetFunctionCount()) + { + g_pOnPrePlayerThinkFunctions->Execute(); + } + + DETOUR_STATIC_CALL(DETOUR_RunThinkFunctions)(simulating); + + if(g_pOnRunThinkFunctionsPost->GetFunctionCount()) + { + g_pOnRunThinkFunctionsPost->PushCell(simulating); + g_pOnRunThinkFunctionsPost->Execute(); + } +} + +void (*g_pPhysics_SimulateEntity)(CBaseEntity *pEntity) = NULL; +void Physics_SimulateEntity_CustomLoop(CBaseEntity **ppList, int Count, float Startime) +{ + CBaseEntity *apPlayers[SM_MAXPLAYERS]; + int iPlayers = 0; + + // Remove players from list and put into apPlayers + for(int i = 0; i < Count; i++) + { + CBaseEntity *pEntity = ppList[i]; + if(!pEntity) + continue; + + edict_t *pEdict = gamehelpers->EdictOfIndex(gamehelpers->EntityToBCompatRef(pEntity)); + if(!pEdict) + continue; + + int Entity = gamehelpers->IndexOfEdict(pEdict); + if(Entity >= 1 && Entity <= SM_MAXPLAYERS) + { + apPlayers[iPlayers++] = pEntity; + ppList[i] = NULL; + } + } + + // Shuffle players array + for(int i = iPlayers - 1; i > 0; i--) + { + int j = rand() % (i + 1); + CBaseEntity *pTmp = apPlayers[j]; + apPlayers[j] = apPlayers[i]; + apPlayers[i] = pTmp; + } + + // Simulate players first + for(int i = 0; i < iPlayers; i++) + { + gpGlobals->curtime = Startime; + g_pPhysics_SimulateEntity(apPlayers[i]); + } + + // Post Player simulation done + if(g_pOnPostPlayerThinkFunctions->GetFunctionCount()) + { + g_pOnPostPlayerThinkFunctions->Execute(); + } + + // Now simulate the rest + for(int i = 0; i < Count; i++) + { + CBaseEntity *pEntity = ppList[i]; + if(!pEntity) + continue; + + gpGlobals->curtime = Startime; + g_pPhysics_SimulateEntity(pEntity); + } +} + +int g_TriggerEntityMoved; +int *g_pBlockTriggerTouchPlayers = NULL; +int *g_pBlockTriggerMoved = NULL; +// void IVEngineServer::TriggerMoved( edict_t *pTriggerEnt, bool testSurroundingBoundsOnly ) = 0; +SH_DECL_HOOK2_void(IVEngineServer, TriggerMoved, SH_NOATTRIB, 0, edict_t *, bool); +void TriggerMoved(edict_t *pTriggerEnt, bool testSurroundingBoundsOnly) +{ + g_TriggerEntityMoved = gamehelpers->IndexOfEdict(pTriggerEnt); + + // Block if bit is set + if(g_pBlockTriggerMoved && CheckBit(g_pBlockTriggerMoved, g_TriggerEntityMoved)) + { + RETURN_META(MRES_SUPERCEDE); + } + + // Decide per entity in TriggerMoved_EnumElement + RETURN_META(MRES_IGNORED); +} + +// IterationRetval_t CTriggerMoved::EnumElement( IHandleEntity *pHandleEntity ) = 0; +SH_DECL_HOOK1(CTriggerMoved, EnumElement, SH_NOATTRIB, 0, IterationRetval_t, IHandleEntity *); +IterationRetval_t TriggerMoved_EnumElement(IHandleEntity *pHandleEntity) +{ + if(!g_pBlockTriggerTouchPlayers) + { + RETURN_META_VALUE(MRES_IGNORED, ITERATION_CONTINUE); + } + + IServerUnknown *pUnk = static_cast< IServerUnknown* >( pHandleEntity ); + CBaseHandle hndl = pUnk->GetRefEHandle(); + int index = hndl.GetEntryIndex(); + + // We only care about players + if(index > SM_MAXPLAYERS) + { + RETURN_META_VALUE(MRES_IGNORED, ITERATION_CONTINUE); + } + + // block touching any clients here if bit is set + if(CheckBit(g_pBlockTriggerTouchPlayers, g_TriggerEntityMoved)) + { + RETURN_META_VALUE(MRES_SUPERCEDE, ITERATION_CONTINUE); + } + + // allow touch + RETURN_META_VALUE(MRES_IGNORED, ITERATION_CONTINUE); +} + +cell_t BlockTriggerMoved(IPluginContext *pContext, const cell_t *params) +{ + if(params[2]) + pContext->LocalToPhysAddr(params[1], &g_pBlockTriggerMoved); + else + g_pBlockTriggerMoved = NULL; + + return 0; +} + +cell_t BlockTriggerTouchPlayers(IPluginContext *pContext, const cell_t *params) +{ + if(params[2]) + pContext->LocalToPhysAddr(params[1], &g_pBlockTriggerTouchPlayers); + else + g_pBlockTriggerTouchPlayers = NULL; + + return 0; +} + +int g_SolidEntityMoved; +int *g_pBlockSolidMoved = NULL; +int *g_pBlockSolidTouchPlayers = NULL; +int *g_pFilterClientSolidTouch = NULL; +// void IVEngineServer::SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool testSurroundingBoundsOnly ) = 0; +SH_DECL_HOOK4_void(IVEngineServer, SolidMoved, SH_NOATTRIB, 0, edict_t *, ICollideable *, const Vector *, bool); +void SolidMoved(edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector *pPrevAbsOrigin, bool testSurroundingBoundsOnly) +{ + g_SolidEntityMoved = gamehelpers->IndexOfEdict(pSolidEnt); + + // Block if bit is set + if(g_pBlockSolidMoved && CheckBit(g_pBlockSolidMoved, g_SolidEntityMoved)) + { + RETURN_META(MRES_SUPERCEDE); + } + + // Decide per entity in TouchLinks_EnumElement + RETURN_META(MRES_IGNORED); +} + +// IterationRetval_t CTouchLinks::EnumElement( IHandleEntity *pHandleEntity ) = 0; +SH_DECL_HOOK1(CTouchLinks, EnumElement, SH_NOATTRIB, 0, IterationRetval_t, IHandleEntity *); +IterationRetval_t TouchLinks_EnumElement(IHandleEntity *pHandleEntity) +{ + IServerUnknown *pUnk = static_cast< IServerUnknown* >( pHandleEntity ); + CBaseHandle hndl = pUnk->GetRefEHandle(); + int index = hndl.GetEntryIndex(); + + // Optimization: Players shouldn't touch other players + if(g_SolidEntityMoved <= SM_MAXPLAYERS && index <= SM_MAXPLAYERS) + { + RETURN_META_VALUE(MRES_SUPERCEDE, ITERATION_CONTINUE); + } + + // block solid from touching any clients here if bit is set + if(g_pBlockSolidTouchPlayers && index <= SM_MAXPLAYERS && CheckBit(g_pBlockSolidTouchPlayers, g_SolidEntityMoved)) + { + RETURN_META_VALUE(MRES_SUPERCEDE, ITERATION_CONTINUE); + } + + // Block player from touching any filtered entity here if bit is set + if(g_pFilterClientSolidTouch && index < 2048 && CheckBit(g_pFilterClientSolidTouch, g_SolidEntityMoved * 2048 + index)) + { + RETURN_META_VALUE(MRES_SUPERCEDE, ITERATION_CONTINUE); + } + + // Allow otherwise + RETURN_META_VALUE(MRES_IGNORED, ITERATION_CONTINUE); +} + +cell_t BlockSolidMoved(IPluginContext *pContext, const cell_t *params) +{ + if(params[2]) + pContext->LocalToPhysAddr(params[1], &g_pBlockSolidMoved); + else + g_pBlockSolidMoved = NULL; + + return 0; +} + +cell_t BlockSolidTouchPlayers(IPluginContext *pContext, const cell_t *params) +{ + if(params[2]) + pContext->LocalToPhysAddr(params[1], &g_pBlockSolidTouchPlayers); + else + g_pBlockSolidTouchPlayers = NULL; + + return 0; +} + +cell_t FilterClientSolidTouch(IPluginContext *pContext, const cell_t *params) +{ + if(params[2]) + pContext->LocalToPhysAddr(params[1], &g_pFilterClientSolidTouch); + else + g_pFilterClientSolidTouch = NULL; + + return 0; +} + +bool PhysHooks::SDK_OnLoad(char *error, size_t maxlength, bool late) +{ + srand((unsigned int)time(NULL)); + + char conf_error[255] = ""; + if(!gameconfs->LoadGameConfigFile("PhysHooks.games", &g_pGameConf, conf_error, sizeof(conf_error))) + { + if(conf_error[0]) + snprintf(error, maxlength, "Could not read PhysHooks.games.txt: %s", conf_error); + + return false; + } + + CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf); + + g_pDetour_RunThinkFunctions = DETOUR_CREATE_STATIC(DETOUR_RunThinkFunctions, "Physics_RunThinkFunctions"); + if(g_pDetour_RunThinkFunctions == NULL) + { + snprintf(error, maxlength, "Could not create detour for Physics_RunThinkFunctions"); + SDK_OnUnload(); + return false; + } + + g_pDetour_RunThinkFunctions->EnableDetour(); + + // Find VTable for CTriggerMoved + uintptr_t pCTriggerMoved; + if(!g_pGameConf->GetMemSig("CTriggerMoved", (void **)(&pCTriggerMoved)) || !pCTriggerMoved) + { + snprintf(error, maxlength, "Failed to find CTriggerMoved.\n"); + SDK_OnUnload(); + return false; + } + // First function in VTable + g_CTriggerMoved = (CTriggerMoved *)(pCTriggerMoved + 8); + + // Find VTable for CTouchLinks + uintptr_t pCTouchLinks; + if(!g_pGameConf->GetMemSig("CTouchLinks", (void **)(&pCTouchLinks)) || !pCTouchLinks) + { + snprintf(error, maxlength, "Failed to find CTouchLinks.\n"); + SDK_OnUnload(); + return false; + } + // First function in VTable + g_CTouchLinks = (CTouchLinks *)(pCTouchLinks + 8); + + g_SH_TriggerMoved = SH_ADD_DVPHOOK(CTriggerMoved, EnumElement, g_CTriggerMoved, SH_STATIC(TriggerMoved_EnumElement), false); + g_SH_TouchLinks = SH_ADD_DVPHOOK(CTouchLinks, EnumElement, g_CTouchLinks, SH_STATIC(TouchLinks_EnumElement), false); + + SH_ADD_HOOK(IVEngineServer, TriggerMoved, engine, SH_STATIC(TriggerMoved), false); + SH_ADD_HOOK(IVEngineServer, SolidMoved, engine, SH_STATIC(SolidMoved), false); + + if(!g_pGameConf->GetMemSig("Physics_SimulateEntity", (void **)(&g_pPhysics_SimulateEntity)) || !g_pPhysics_SimulateEntity) + { + snprintf(error, maxlength, "Failed to find Physics_SimulateEntity.\n"); + SDK_OnUnload(); + return false; + } + + void *pServerSo = dlopen("cstrike/bin/server_srv.so", RTLD_NOW); + if(!pServerSo) + { + snprintf(error, maxlength, "Could not dlopen server_srv.so"); + SDK_OnUnload(); + return false; + } + + void *pEngineSo = dlopen("bin/engine_srv.so", RTLD_NOW); + if(!pEngineSo) + { + snprintf(error, maxlength, "Could not dlopen engine_srv.so"); + dlclose(pServerSo); + SDK_OnUnload(); + return false; + } + + /* Hook: Replace call inside Physics_RunThinkFunctions */ + uintptr_t pAddress = (uintptr_t)memutils->ResolveSymbol(pServerSo, gs_Patches[0].pSignature); + if(!pAddress) + { + snprintf(error, maxlength, "Could not find symbol: %s", gs_Patches[0].pSignature); + dlclose(pServerSo); + dlclose(pEngineSo); + SDK_OnUnload(); + return false; + } + + uintptr_t pPatchAddress = FindPattern(pAddress, gs_Patches[0].pPatchSignature, gs_Patches[0].pPatchPattern, 1024); + if(!pPatchAddress) + { + snprintf(error, maxlength, "Could not find patch signature for symbol: %s", gs_Patches[0].pSignature); + dlclose(pServerSo); + dlclose(pEngineSo); + SDK_OnUnload(); + return false; + } + + // mov [esp+8], edi ; startime + // mov [esp+4], eax ; count + // mov [esp], esi ; **list + // call NULL ; <- our func here + // mov eax, ds:gpGlobals ; restore + // jmp +11 ; jump over useless instructions + static unsigned char aPatch[] = "\x89\x7C\x24\x08\x89\x44\x24\x04\x89\x34\x24\xE8\x00\x00\x00\x00\xA1\x00\x00\x00\x00\xEB\x0B\x90\x90\x90"; + gs_Patches[0].pPatch = aPatch; + + // put our function address into the relative call instruction + // relative call: new PC = PC + imm1 + // call is at + 11 after pPatchAddress + // PC will be past our call instruction so + 5 + *(uintptr_t *)&aPatch[12] = (uintptr_t)Physics_SimulateEntity_CustomLoop - (pPatchAddress + 11 + 5); + + // restore "mov eax, ds:gpGlobals" from original to our patch after the call + *(uintptr_t *)&aPatch[17] = *(uintptr_t *)(pPatchAddress + 8); + + // Apply all patches + for(size_t i = 0; i < sizeof(gs_Patches) / sizeof(*gs_Patches); i++) + { + struct SrcdsPatch *pPatch = &gs_Patches[i]; + int PatchLen = strlen(pPatch->pPatchPattern); + + void *pBinary = pPatch->engine ? pEngineSo : pServerSo; + pPatch->pAddress = (uintptr_t)memutils->ResolveSymbol(pBinary, pPatch->pSignature); + if(!pPatch->pAddress) + { + snprintf(error, maxlength, "Could not find symbol: %s", pPatch->pSignature); + dlclose(pServerSo); + dlclose(pEngineSo); + SDK_OnUnload(); + return false; + } + + pPatch->pPatchAddress = FindPattern(pPatch->pAddress, pPatch->pPatchSignature, pPatch->pPatchPattern, 1024); + if(!pPatch->pPatchAddress) + { + snprintf(error, maxlength, "Could not find patch signature for symbol: %s", pPatch->pSignature); + dlclose(pServerSo); + dlclose(pEngineSo); + SDK_OnUnload(); + return false; + } + + pPatch->pOriginal = (unsigned char *)malloc(PatchLen * sizeof(unsigned char)); + + SourceHook::SetMemAccess((void *)pPatch->pPatchAddress, PatchLen, SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + for(int j = 0; j < PatchLen; j++) + { + pPatch->pOriginal[j] = *(unsigned char *)(pPatch->pPatchAddress + j); + *(unsigned char *)(pPatch->pPatchAddress + j) = pPatch->pPatch[j]; + } + SourceHook::SetMemAccess((void *)pPatch->pPatchAddress, PatchLen, SH_MEM_READ|SH_MEM_EXEC); + } + + dlclose(pServerSo); + dlclose(pEngineSo); + + g_pOnRunThinkFunctions = forwards->CreateForward("OnRunThinkFunctions", ET_Ignore, 1, NULL, Param_Cell); + g_pOnPrePlayerThinkFunctions = forwards->CreateForward("OnPrePlayerThinkFunctions", ET_Ignore, 0, NULL); + g_pOnPostPlayerThinkFunctions = forwards->CreateForward("OnPostPlayerThinkFunctions", ET_Ignore, 0, NULL); + g_pOnRunThinkFunctionsPost = forwards->CreateForward("OnRunThinkFunctionsPost", ET_Ignore, 1, NULL, Param_Cell); + + return true; +} + +const sp_nativeinfo_t MyNatives[] = +{ + { "BlockTriggerMoved", BlockTriggerMoved }, + { "BlockTriggerTouchPlayers", BlockTriggerTouchPlayers }, + { "BlockSolidMoved", BlockSolidMoved }, + { "BlockSolidTouchPlayers", BlockSolidTouchPlayers }, + { "FilterClientSolidTouch", FilterClientSolidTouch }, + { NULL, NULL } +}; + +void PhysHooks::SDK_OnAllLoaded() +{ + sharesys->AddNatives(myself, MyNatives); + sharesys->RegisterLibrary(myself, "PhysHooks"); +} +void PhysHooks::SDK_OnUnload() +{ + if(g_pDetour_RunThinkFunctions != NULL) + { + g_pDetour_RunThinkFunctions->Destroy(); + g_pDetour_RunThinkFunctions = NULL; + } + + if(g_pOnRunThinkFunctions != NULL) + { + forwards->ReleaseForward(g_pOnRunThinkFunctions); + g_pOnRunThinkFunctions = NULL; + } + + if(g_pOnRunThinkFunctionsPost != NULL) + { + forwards->ReleaseForward(g_pOnRunThinkFunctionsPost); + g_pOnRunThinkFunctionsPost = NULL; + } + + if(g_pOnPrePlayerThinkFunctions != NULL) + { + forwards->ReleaseForward(g_pOnPrePlayerThinkFunctions); + g_pOnPrePlayerThinkFunctions = NULL; + } + + if(g_pOnPostPlayerThinkFunctions != NULL) + { + forwards->ReleaseForward(g_pOnPostPlayerThinkFunctions); + g_pOnPostPlayerThinkFunctions = NULL; + } + + if(g_SH_TriggerMoved) + SH_REMOVE_HOOK_ID(g_SH_TriggerMoved); + + if(g_SH_TouchLinks) + SH_REMOVE_HOOK_ID(g_SH_TouchLinks); + + SH_REMOVE_HOOK(IVEngineServer, TriggerMoved, engine, SH_STATIC(TriggerMoved), false); + SH_REMOVE_HOOK(IVEngineServer, SolidMoved, engine, SH_STATIC(SolidMoved), false); + + gameconfs->CloseGameConfigFile(g_pGameConf); + + // Revert all applied patches + for(size_t i = 0; i < sizeof(gs_Patches) / sizeof(*gs_Patches); i++) + { + struct SrcdsPatch *pPatch = &gs_Patches[i]; + int PatchLen = strlen(pPatch->pPatchPattern); + + if(!pPatch->pOriginal) + continue; + + SourceHook::SetMemAccess((void *)pPatch->pPatchAddress, PatchLen, SH_MEM_READ|SH_MEM_WRITE|SH_MEM_EXEC); + for(int j = 0; j < PatchLen; j++) + { + *(unsigned char *)(pPatch->pPatchAddress + j) = pPatch->pOriginal[j]; + } + SourceHook::SetMemAccess((void *)pPatch->pPatchAddress, PatchLen, SH_MEM_READ|SH_MEM_EXEC); + + free(pPatch->pOriginal); + pPatch->pOriginal = NULL; + } +} + +bool PhysHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) +{ + GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); + gpGlobals = ismm->GetCGlobals(); + return true; +} + +uintptr_t FindPattern(uintptr_t BaseAddr, const unsigned char *pData, const char *pPattern, size_t MaxSize) +{ + unsigned char *pMemory; + uintptr_t PatternLen = strlen(pPattern); + + pMemory = reinterpret_cast(BaseAddr); + + for(uintptr_t i = 0; i < MaxSize; i++) + { + uintptr_t Matches = 0; + while(*(pMemory + i + Matches) == pData[Matches] || pPattern[Matches] != 'x') + { + Matches++; + if(Matches == PatternLen) + return (uintptr_t)(pMemory + i); + } + } + + return 0x00; +} diff --git a/extension.h b/extension.h new file mode 100644 index 0000000..8f2e214 --- /dev/null +++ b/extension.h @@ -0,0 +1,119 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod PhysHooks Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ + +/** + * @file extension.h + * @brief PhysHooks extension code header. + */ + +#include "smsdk_ext.h" + + +/** + * @brief PhysHooks implementation of the SDK Extension. + * Note: Uncomment one of the pre-defined virtual functions in order to use it. + */ +class PhysHooks : public SDKExtension +{ +public: + /** + * @brief This is called after the initial loading sequence has been processed. + * + * @param error Error message buffer. + * @param maxlen Size of error message buffer. + * @param late Whether or not the module was loaded after map load. + * @return True to succeed loading, false to fail. + */ + virtual bool SDK_OnLoad(char *error, size_t maxlen, bool late); + + /** + * @brief This is called right before the extension is unloaded. + */ + virtual void SDK_OnUnload(); + + /** + * @brief This is called once all known extensions have been loaded. + * Note: It is is a good idea to add natives here, if any are provided. + */ + virtual void SDK_OnAllLoaded(); + + /** + * @brief Called when the pause state is changed. + */ + //virtual void SDK_OnPauseChange(bool paused); + + /** + * @brief this is called when Core wants to know if your extension is working. + * + * @param error Error message buffer. + * @param maxlen Size of error message buffer. + * @return True if working, false otherwise. + */ + //virtual bool QueryRunning(char *error, size_t maxlen); +public: +#if defined SMEXT_CONF_METAMOD + /** + * @brief Called when Metamod is attached, before the extension version is called. + * + * @param error Error buffer. + * @param maxlen Maximum size of error buffer. + * @param late Whether or not Metamod considers this a late load. + * @return True to succeed, false to fail. + */ + virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late); + + /** + * @brief Called when Metamod is detaching, after the extension version is called. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param error Error buffer. + * @param maxlen Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlen); + + /** + * @brief Called when Metamod's pause state is changing. + * NOTE: By default this is blocked unless sent from SourceMod. + * + * @param paused Pause state being set. + * @param error Error buffer. + * @param maxlen Maximum size of error buffer. + * @return True to succeed, false to fail. + */ + //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlen); +#endif +}; + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ diff --git a/gamedata/PhysHooks.games.txt b/gamedata/PhysHooks.games.txt new file mode 100644 index 0000000..5e64ffa --- /dev/null +++ b/gamedata/PhysHooks.games.txt @@ -0,0 +1,32 @@ +"Games" +{ + "#default" + { + "Signatures" + { + "Physics_RunThinkFunctions" + { + "library" "server" + "linux" "@_Z25Physics_RunThinkFunctionsb" + } + + "Physics_SimulateEntity" + { + "library" "server" + "linux" "@_Z22Physics_SimulateEntityP11CBaseEntity" + } + + "CTriggerMoved" + { + "library" "engine" + "linux" "@_ZTV13CTriggerMoved" + } + + "CTouchLinks" + { + "library" "engine" + "linux" "@_ZTV11CTouchLinks" + } + } + } +} diff --git a/include/PhysHooks.inc b/include/PhysHooks.inc new file mode 100644 index 0000000..8b27a77 --- /dev/null +++ b/include/PhysHooks.inc @@ -0,0 +1,47 @@ +#if defined _physhooks_included + #endinput +#endif +#define _physhooks_included + +forward void OnRunThinkFunctions(bool simulating); +forward void OnPrePlayerThinkFunctions(); +forward void OnPostPlayerThinkFunctions(); +forward void OnRunThinkFunctionsPost(bool simulating); + +// Block TriggerMoved from being called at all for an entity by setting the bit to 1. +native void BlockTriggerMoved(int map[2048 / 32], bool set); + +// Block triggers TriggerMoved from touching any client by setting the bit to 1 for the entity index. +native void BlockTriggerTouchPlayers(int map[2048 / 32], bool set); + +// Block SolidMoved from being called at all for an entity by setting the bit to 1. +native void BlockSolidMoved(int map[2048 / 32], bool set); + +// Block solids SolidMoved from touching any client by setting the bit to 1 for the entity index. +native void BlockSolidTouchPlayers(int map[2048 / 32], bool set); + +// Block clients SolidMoved from touching an entity by setting the bit to 1 in the clients map. +native void FilterClientSolidTouch(int map[((MAXPLAYERS + 1) * 2048) / 32], bool set); + + +public Extension __ext_PhysHooks = +{ + name = "PhysHooks", + file = "PhysHooks.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_PhysHooks_SetNTVOptional() +{ +} +#endif diff --git a/smsdk_config.h b/smsdk_config.h new file mode 100644 index 0000000..db4697a --- /dev/null +++ b/smsdk_config.h @@ -0,0 +1,81 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Sample Extension + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ + +/** + * @file smsdk_config.h + * @brief Contains macros for configuring basic extension information. + */ + +/* Basic information exposed publicly */ +#define SMEXT_CONF_NAME "PhysHooks" +#define SMEXT_CONF_DESCRIPTION "Hooks, forwards and natives for LagCompensation, etc." +#define SMEXT_CONF_VERSION "1.0" +#define SMEXT_CONF_AUTHOR "BotoX" +#define SMEXT_CONF_URL "" +#define SMEXT_CONF_LOGTAG "PhysHooks" +#define SMEXT_CONF_LICENSE "GPL" +#define SMEXT_CONF_DATESTRING __DATE__ + +/** + * @brief Exposes plugin's main interface. + */ +#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name; + +/** + * @brief Sets whether or not this plugin required Metamod. + * NOTE: Uncomment to enable, comment to disable. + */ +#define SMEXT_CONF_METAMOD + +/** Enable interfaces you want to use here by uncommenting lines */ +#define SMEXT_ENABLE_FORWARDSYS +//#define SMEXT_ENABLE_HANDLESYS +#define SMEXT_ENABLE_PLAYERHELPERS +//#define SMEXT_ENABLE_DBMANAGER +#define SMEXT_ENABLE_GAMECONF +#define SMEXT_ENABLE_MEMUTILS +#define SMEXT_ENABLE_GAMEHELPERS +//#define SMEXT_ENABLE_TIMERSYS +//#define SMEXT_ENABLE_THREADER +//#define SMEXT_ENABLE_LIBSYS +//#define SMEXT_ENABLE_MENUS +//#define SMEXT_ENABLE_ADTFACTORY +//#define SMEXT_ENABLE_PLUGINSYS +//#define SMEXT_ENABLE_ADMINSYS +//#define SMEXT_ENABLE_TEXTPARSERS +//#define SMEXT_ENABLE_USERMSGS +//#define SMEXT_ENABLE_TRANSLATOR +//#define SMEXT_ENABLE_ROOTCONSOLEMENU + +#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_