├── AMBuildScript ├── AMBuilder ├── PackageScript ├── README.md ├── configure.py ├── extension.cpp ├── extension.h ├── smsdk_config.h └── sourcemod ├── gamedata └── collisionhook.txt └── scripting └── include └── collisionhook.inc /AMBuildScript: -------------------------------------------------------------------------------- 1 | # vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: 2 | import os, sys 3 | 4 | # Simple extensions do not need to modify this file. 5 | 6 | class SDK(object): 7 | def __init__(self, sdk, ext, aDef, name, platform, dir): 8 | self.folder = 'hl2sdk-' + dir 9 | self.envvar = sdk 10 | self.ext = ext 11 | self.code = aDef 12 | self.define = name 13 | self.platform = platform 14 | self.name = dir 15 | self.path = None # Actual path 16 | 17 | WinOnly = ['windows'] 18 | WinLinux = ['windows', 'linux'] 19 | WinLinuxMac = ['windows', 'linux', 'mac'] 20 | 21 | PossibleSDKs = { 22 | 'episode1': SDK('HL2SDK', '1.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'), 23 | 'ep2': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'), 24 | 'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinuxMac, 'css'), 25 | 'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinuxMac, 'hl2dm'), 26 | 'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinuxMac, 'dods'), 27 | 'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'), 28 | 'tf2': SDK('HL2SDKTF2', '2.tf2', '11', 'TF2', WinLinuxMac, 'tf2'), 29 | 'l4d': SDK('HL2SDKL4D', '2.l4d', '12', 'LEFT4DEAD', WinLinuxMac, 'l4d'), 30 | 'nucleardawn': SDK('HL2SDKND', '2.nd', '13', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'), 31 | 'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '15', 'LEFT4DEAD2', WinLinuxMac, 'l4d2'), 32 | 'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'), 33 | 'swarm': SDK('HL2SDK-SWARM', '2.swarm', '16', 'ALIENSWARM', WinOnly, 'swarm'), 34 | 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'), 35 | 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'), 36 | 'csgo': SDK('HL2SDKCSGO', '2.csgo', '20', 'CSGO', WinLinuxMac, 'csgo'), 37 | 'dota': SDK('HL2SDKDOTA', '2.dota', '21', 'DOTA', [], 'dota'), 38 | 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'), 39 | 'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinLinux, 'blade'), 40 | 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'), 41 | 'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'), 42 | 'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'), 43 | } 44 | 45 | def ResolveEnvPath(env, folder): 46 | if env in os.environ: 47 | path = os.environ[env] 48 | if os.path.isdir(path): 49 | return path 50 | return None 51 | 52 | head = os.getcwd() 53 | oldhead = None 54 | while head != None and head != oldhead: 55 | path = os.path.join(head, folder) 56 | if os.path.isdir(path): 57 | return path 58 | oldhead = head 59 | head, tail = os.path.split(head) 60 | 61 | return None 62 | 63 | def Normalize(path): 64 | return os.path.abspath(os.path.normpath(path)) 65 | 66 | class ExtensionConfig(object): 67 | def __init__(self): 68 | self.sdks = {} 69 | self.binaries = [] 70 | self.extensions = [] 71 | self.generated_headers = None 72 | self.mms_root = None 73 | self.sm_root = None 74 | 75 | @property 76 | def tag(self): 77 | if builder.options.debug == '1': 78 | return 'Debug' 79 | return 'Release' 80 | 81 | def detectSDKs(self): 82 | sdk_list = builder.options.sdks.split(',') 83 | use_all = sdk_list[0] == 'all' 84 | use_present = sdk_list[0] == 'present' 85 | 86 | for sdk_name in PossibleSDKs: 87 | sdk = PossibleSDKs[sdk_name] 88 | if builder.target_platform in sdk.platform: 89 | if builder.options.hl2sdk_root: 90 | sdk_path = os.path.join(builder.options.hl2sdk_root, sdk.folder) 91 | else: 92 | sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder) 93 | if sdk_path is None or not os.path.isdir(sdk_path): 94 | if use_all or sdk_name in sdk_list: 95 | raise Exception('Could not find a valid path for {0}'.format(sdk.envvar)) 96 | continue 97 | if use_all or use_present or sdk_name in sdk_list: 98 | sdk.path = Normalize(sdk_path) 99 | self.sdks[sdk_name] = sdk 100 | 101 | if len(self.sdks) < 1: 102 | raise Exception('At least one SDK must be available.') 103 | 104 | if builder.options.sm_path: 105 | self.sm_root = builder.options.sm_path 106 | else: 107 | self.sm_root = ResolveEnvPath('SOURCEMOD18', 'sourcemod-1.8') 108 | if not self.sm_root: 109 | self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod') 110 | if not self.sm_root: 111 | self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central') 112 | 113 | if not self.sm_root or not os.path.isdir(self.sm_root): 114 | raise Exception('Could not find a source copy of SourceMod') 115 | self.sm_root = Normalize(self.sm_root) 116 | 117 | if builder.options.mms_path: 118 | self.mms_root = builder.options.mms_path 119 | else: 120 | self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10') 121 | if not self.mms_root: 122 | self.mms_root = ResolveEnvPath('MMSOURCE', 'metamod-source') 123 | if not self.mms_root: 124 | self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'mmsource-central') 125 | 126 | if not self.mms_root or not os.path.isdir(self.mms_root): 127 | raise Exception('Could not find a source copy of Metamod:Source') 128 | self.mms_root = Normalize(self.mms_root) 129 | 130 | def configure(self): 131 | cxx = builder.DetectCompilers() 132 | 133 | if cxx.like('gcc'): 134 | self.configure_gcc(cxx) 135 | elif cxx.vendor == 'msvc': 136 | self.configure_msvc(cxx) 137 | 138 | # Optimizaiton 139 | if builder.options.opt == '1': 140 | cxx.defines += ['NDEBUG'] 141 | 142 | # Debugging 143 | if builder.options.debug == '1': 144 | cxx.defines += ['DEBUG', '_DEBUG'] 145 | 146 | # Platform-specifics 147 | if builder.target_platform == 'linux': 148 | self.configure_linux(cxx) 149 | elif builder.target_platform == 'mac': 150 | self.configure_mac(cxx) 151 | elif builder.target_platform == 'windows': 152 | self.configure_windows(cxx) 153 | 154 | # Finish up. 155 | cxx.includes += [ 156 | os.path.join(self.sm_root, 'public'), 157 | ] 158 | 159 | def configure_gcc(self, cxx): 160 | cxx.defines += [ 161 | 'stricmp=strcasecmp', 162 | '_stricmp=strcasecmp', 163 | '_snprintf=snprintf', 164 | '_vsnprintf=vsnprintf', 165 | 'HAVE_STDINT_H', 166 | 'GNUC', 167 | ] 168 | cxx.cflags += [ 169 | '-pipe', 170 | '-fno-strict-aliasing', 171 | '-Wall', 172 | '-Werror', 173 | '-Wno-unused', 174 | '-Wno-switch', 175 | '-Wno-array-bounds', 176 | '-msse', 177 | '-m32', 178 | '-fvisibility=hidden', 179 | ] 180 | cxx.cxxflags += [ 181 | '-std=c++11', 182 | '-fno-exceptions', 183 | '-fno-threadsafe-statics', 184 | '-Wno-non-virtual-dtor', 185 | '-Wno-overloaded-virtual', 186 | '-fvisibility-inlines-hidden', 187 | ] 188 | cxx.linkflags += ['-m32'] 189 | 190 | have_gcc = cxx.vendor == 'gcc' 191 | have_clang = cxx.vendor == 'clang' 192 | if cxx.version >= 'clang-3.6': 193 | cxx.cxxflags += ['-Wno-inconsistent-missing-override'] 194 | if have_clang or (cxx.version >= 'gcc-4.6'): 195 | cxx.cflags += ['-Wno-narrowing'] 196 | if have_clang or (cxx.version >= 'gcc-4.7'): 197 | cxx.cxxflags += ['-Wno-delete-non-virtual-dtor'] 198 | if cxx.version >= 'gcc-4.8': 199 | cxx.cflags += ['-Wno-unused-result'] 200 | 201 | if have_clang: 202 | cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch'] 203 | if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4': 204 | cxx.cxxflags += ['-Wno-deprecated-register'] 205 | else: 206 | cxx.cxxflags += ['-Wno-deprecated'] 207 | cxx.cflags += ['-Wno-sometimes-uninitialized'] 208 | 209 | if have_gcc: 210 | cxx.cflags += ['-mfpmath=sse'] 211 | 212 | if builder.options.opt == '1': 213 | cxx.cflags += ['-O3'] 214 | 215 | def configure_msvc(self, cxx): 216 | if builder.options.debug == '1': 217 | cxx.cflags += ['/MTd'] 218 | cxx.linkflags += ['/NODEFAULTLIB:libcmt'] 219 | else: 220 | cxx.cflags += ['/MT'] 221 | cxx.defines += [ 222 | '_CRT_SECURE_NO_DEPRECATE', 223 | '_CRT_SECURE_NO_WARNINGS', 224 | '_CRT_NONSTDC_NO_DEPRECATE', 225 | '_ITERATOR_DEBUG_LEVEL=0', 226 | ] 227 | cxx.cflags += [ 228 | '/W3', 229 | ] 230 | cxx.cxxflags += [ 231 | '/EHsc', 232 | '/GR-', 233 | '/TP', 234 | ] 235 | cxx.linkflags += [ 236 | '/MACHINE:X86', 237 | 'kernel32.lib', 238 | 'user32.lib', 239 | 'gdi32.lib', 240 | 'winspool.lib', 241 | 'comdlg32.lib', 242 | 'advapi32.lib', 243 | 'shell32.lib', 244 | 'ole32.lib', 245 | 'oleaut32.lib', 246 | 'uuid.lib', 247 | 'odbc32.lib', 248 | 'odbccp32.lib', 249 | ] 250 | 251 | if builder.options.opt == '1': 252 | cxx.cflags += ['/Ox', '/Zo'] 253 | cxx.linkflags += ['/OPT:ICF', '/OPT:REF'] 254 | 255 | if builder.options.debug == '1': 256 | cxx.cflags += ['/Od', '/RTC1'] 257 | 258 | # This needs to be after our optimization flags which could otherwise disable it. 259 | # Don't omit the frame pointer. 260 | cxx.cflags += ['/Oy-'] 261 | 262 | def configure_linux(self, cxx): 263 | cxx.defines += ['_LINUX', 'POSIX'] 264 | cxx.linkflags += ['-Wl,--exclude-libs,ALL', '-lm'] 265 | if cxx.vendor == 'gcc': 266 | cxx.linkflags += ['-static-libgcc'] 267 | elif cxx.vendor == 'clang': 268 | cxx.linkflags += ['-lgcc_eh'] 269 | 270 | def configure_mac(self, cxx): 271 | cxx.defines += ['OSX', '_OSX', 'POSIX'] 272 | cxx.cflags += ['-mmacosx-version-min=10.5'] 273 | cxx.linkflags += [ 274 | '-mmacosx-version-min=10.5', 275 | '-arch', 'i386', 276 | '-lstdc++', 277 | '-stdlib=libstdc++', 278 | ] 279 | cxx.cxxflags += ['-stdlib=libstdc++'] 280 | 281 | def configure_windows(self, cxx): 282 | cxx.defines += ['WIN32', '_WINDOWS'] 283 | 284 | def ConfigureForExtension(self, context, compiler): 285 | compiler.cxxincludes += [ 286 | os.path.join(context.currentSourcePath), 287 | os.path.join(context.currentSourcePath, 'sdk'), 288 | os.path.join(self.sm_root, 'public'), 289 | os.path.join(self.sm_root, 'public', 'extensions'), 290 | os.path.join(self.sm_root, 'sourcepawn', 'include'), 291 | os.path.join(self.sm_root, 'public', 'amtl', 'amtl'), 292 | os.path.join(self.sm_root, 'public', 'amtl'), 293 | ] 294 | return compiler 295 | 296 | def ConfigureForHL2(self, binary, sdk): 297 | compiler = binary.compiler 298 | 299 | if sdk.name == 'episode1': 300 | mms_path = os.path.join(self.mms_root, 'core-legacy') 301 | else: 302 | mms_path = os.path.join(self.mms_root, 'core') 303 | 304 | compiler.cxxincludes += [ 305 | os.path.join(mms_path), 306 | os.path.join(mms_path, 'sourcehook'), 307 | ] 308 | 309 | defines = ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs] 310 | compiler.defines += defines 311 | 312 | paths = [ 313 | ['public'], 314 | ['public', 'engine'], 315 | ['public', 'mathlib'], 316 | ['public', 'vstdlib'], 317 | ['public', 'tier0'], 318 | ['public', 'tier1'] 319 | ] 320 | if sdk.name == 'episode1' or sdk.name == 'darkm': 321 | paths.append(['public', 'dlls']) 322 | paths.append(['game_shared']) 323 | else: 324 | paths.append(['public', 'game', 'server']) 325 | paths.append(['public', 'toolframework']) 326 | paths.append(['game', 'shared']) 327 | paths.append(['common']) 328 | 329 | compiler.defines += ['SOURCE_ENGINE=' + sdk.code] 330 | 331 | if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'): 332 | # The 2013 SDK already has these in public/tier0/basetypes.h 333 | compiler.defines.remove('stricmp=strcasecmp') 334 | compiler.defines.remove('_stricmp=strcasecmp') 335 | compiler.defines.remove('_snprintf=snprintf') 336 | compiler.defines.remove('_vsnprintf=vsnprintf') 337 | 338 | if compiler.like('msvc'): 339 | compiler.defines += ['COMPILER_MSVC', 'COMPILER_MSVC32'] 340 | if compiler.version >= 1900: 341 | compiler.linkflags += ['legacy_stdio_definitions.lib'] 342 | else: 343 | compiler.defines += ['COMPILER_GCC'] 344 | 345 | # For everything after Swarm, this needs to be defined for entity networking 346 | # to work properly with sendprop value changes. 347 | if sdk.name in ['blade', 'insurgency', 'csgo', 'dota']: 348 | compiler.defines += ['NETWORK_VARS_ENABLED'] 349 | 350 | if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota']: 351 | if builder.target_platform in ['linux', 'mac']: 352 | compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE'] 353 | 354 | if sdk.name == 'csgo' and builder.target_platform == 'linux': 355 | compiler.linkflags += ['-lstdc++'] 356 | 357 | for path in paths: 358 | compiler.cxxincludes += [os.path.join(sdk.path, *path)] 359 | 360 | if builder.target_platform == 'linux': 361 | if sdk.name == 'episode1': 362 | lib_folder = os.path.join(sdk.path, 'linux_sdk') 363 | elif sdk.name in ['sdk2013', 'bms']: 364 | lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32') 365 | else: 366 | lib_folder = os.path.join(sdk.path, 'lib', 'linux') 367 | elif builder.target_platform == 'mac': 368 | if sdk.name in ['sdk2013', 'bms']: 369 | lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32') 370 | else: 371 | lib_folder = os.path.join(sdk.path, 'lib', 'mac') 372 | 373 | if builder.target_platform in ['linux', 'mac']: 374 | if sdk.name in ['sdk2013', 'bms']: 375 | compiler.postlink += [ 376 | compiler.Dep(os.path.join(lib_folder, 'tier1.a')), 377 | compiler.Dep(os.path.join(lib_folder, 'mathlib.a')) 378 | ] 379 | else: 380 | compiler.postlink += [ 381 | compiler.Dep(os.path.join(lib_folder, 'tier1_i486.a')), 382 | compiler.Dep(os.path.join(lib_folder, 'mathlib_i486.a')) 383 | ] 384 | 385 | if sdk.name in ['blade', 'insurgency', 'csgo', 'dota']: 386 | compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))] 387 | 388 | dynamic_libs = [] 389 | if builder.target_platform == 'linux': 390 | if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency']: 391 | dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so'] 392 | elif sdk.name in ['l4d', 'blade', 'insurgency', 'csgo', 'dota']: 393 | dynamic_libs = ['libtier0.so', 'libvstdlib.so'] 394 | else: 395 | dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so'] 396 | elif builder.target_platform == 'mac': 397 | compiler.linkflags.append('-liconv') 398 | dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib'] 399 | elif builder.target_platform == 'windows': 400 | libs = ['tier0', 'tier1', 'vstdlib', 'mathlib'] 401 | if sdk.name in ['swarm', 'blade', 'insurgency', 'csgo', 'dota']: 402 | libs.append('interfaces') 403 | for lib in libs: 404 | lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib' 405 | compiler.linkflags.append(compiler.Dep(lib_path)) 406 | 407 | for library in dynamic_libs: 408 | source_path = os.path.join(lib_folder, library) 409 | output_path = os.path.join(binary.localFolder, library) 410 | 411 | def make_linker(source_path, output_path): 412 | def link(context, binary): 413 | cmd_node, (output,) = context.AddSymlink(source_path, output_path) 414 | return output 415 | return link 416 | 417 | linker = make_linker(source_path, output_path) 418 | compiler.linkflags[0:0] = [compiler.Dep(library, linker)] 419 | 420 | return binary 421 | 422 | def HL2Library(self, context, name, sdk): 423 | binary = context.compiler.Library(name) 424 | self.ConfigureForExtension(context, binary.compiler) 425 | return self.ConfigureForHL2(binary, sdk) 426 | 427 | def HL2Project(self, context, name): 428 | project = context.compiler.LibraryProject(name) 429 | self.ConfigureForExtension(context, project.compiler) 430 | return project 431 | 432 | def HL2Config(self, project, name, sdk): 433 | binary = project.Configure(name, '{0} - {1}'.format(self.tag, sdk.name)) 434 | return self.ConfigureForHL2(binary, sdk) 435 | 436 | Extension = ExtensionConfig() 437 | Extension.detectSDKs() 438 | Extension.configure() 439 | 440 | # Add additional buildscripts here 441 | BuildScripts = [ 442 | 'AMBuilder', 443 | ] 444 | 445 | if builder.backend == 'amb2': 446 | BuildScripts += [ 447 | 'PackageScript', 448 | ] 449 | 450 | builder.RunBuildScripts(BuildScripts, { 'Extension': Extension}) 451 | -------------------------------------------------------------------------------- /AMBuilder: -------------------------------------------------------------------------------- 1 | # vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: 2 | import os, sys 3 | 4 | projectName = 'collisionhook' 5 | 6 | # smsdk_ext.cpp will be automatically added later 7 | sourceFiles = [ 8 | 'extension.cpp', 9 | os.path.join(Extension.sm_root, 'public', 'CDetour', 'detours.cpp'), 10 | os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c') 11 | ] 12 | 13 | ############### 14 | # Make sure to edit PackageScript, which copies your files to their appropriate locations 15 | # Simple extensions do not need to modify past this point. 16 | 17 | project = Extension.HL2Project(builder, projectName + '.ext') 18 | 19 | if os.path.isfile(os.path.join(builder.currentSourcePath, 'sdk', 'smsdk_ext.cpp')): 20 | # Use the copy included in the project 21 | project.sources += [os.path.join('sdk', 'smsdk_ext.cpp')] 22 | else: 23 | # Use the copy included with SM 1.6 and newer 24 | project.sources += [os.path.join(Extension.sm_root, 'public', 'smsdk_ext.cpp')] 25 | 26 | project.sources += sourceFiles 27 | 28 | for sdk_name in Extension.sdks: 29 | sdk = Extension.sdks[sdk_name] 30 | 31 | binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk) 32 | 33 | Extension.extensions = builder.Add(project) 34 | -------------------------------------------------------------------------------- /PackageScript: -------------------------------------------------------------------------------- 1 | # vim: set ts=8 sts=2 sw=2 tw=99 et ft=python: 2 | import os 3 | 4 | # This is where the files will be output to 5 | # package is the default 6 | builder.SetBuildFolder('package') 7 | 8 | # Add any folders you need to this list 9 | folder_list = [ 10 | 'addons/sourcemod/extensions', 11 | 'addons/sourcemod/scripting/include', 12 | 'addons/sourcemod/gamedata', 13 | #'addons/sourcemod/configs', 14 | ] 15 | 16 | # Create the distribution folder hierarchy. 17 | folder_map = {} 18 | for folder in folder_list: 19 | norm_folder = os.path.normpath(folder) 20 | folder_map[folder] = builder.AddFolder(norm_folder) 21 | 22 | # Do all straight-up file copies from the source tree. 23 | def CopyFiles(src, dest, files): 24 | if not dest: 25 | dest = src 26 | dest_entry = folder_map[dest] 27 | for source_file in files: 28 | source_path = os.path.join(builder.sourcePath, src, source_file) 29 | builder.AddCopy(source_path, dest_entry) 30 | 31 | # Include files 32 | CopyFiles('sourcemod/scripting/include', 'addons/sourcemod/scripting/include', 33 | [ 'collisionhook.inc', ] 34 | ) 35 | 36 | # GameData files 37 | CopyFiles('sourcemod/gamedata', 'addons/sourcemod/gamedata', 38 | [ 'collisionhook.txt' ] 39 | ) 40 | 41 | # Config Files 42 | #CopyFiles('configs', 'addons/sourcemod/configs', 43 | # [ 'configfile.cfg', 44 | # 'otherconfig.cfg, 45 | # ] 46 | #) 47 | 48 | # Copy binaries. 49 | for cxx_task in Extension.extensions: 50 | builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions']) 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CollisionHook 2 | https://forums.alliedmods.net/showthread.php?t=197815 3 | 4 | CS:GO fix for the CollisionHook extension by VoiDeD 5 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | # vim: set sts=2 ts=8 sw=2 tw=99 et: 2 | import sys 3 | from ambuild2 import run 4 | 5 | # Simple extensions do not need to modify this file. 6 | 7 | builder = run.PrepareBuild(sourcePath = sys.path[0]) 8 | 9 | builder.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, 10 | help='Root search folder for HL2SDKs') 11 | builder.options.add_option('--mms-path', type=str, dest='mms_path', default=None, 12 | help='Path to Metamod:Source') 13 | builder.options.add_option('--sm-path', type=str, dest='sm_path', default=None, 14 | help='Path to SourceMod') 15 | builder.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', 16 | help='Enable debugging symbols') 17 | builder.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', 18 | help='Enable optimization') 19 | builder.options.add_option('-s', '--sdks', default='all', dest='sdks', 20 | help='Build against specified SDKs; valid args are "all", "present", or ' 21 | 'comma-delimited list of engine names (default: %default)') 22 | 23 | builder.Configure() 24 | -------------------------------------------------------------------------------- /extension.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "extension.h" 4 | 5 | #include "sourcehook.h" 6 | #include "CDetour/detours.h" 7 | 8 | #include "vphysics_interface.h" 9 | #include "ihandleentity.h" 10 | 11 | #include "tier1/strtools.h" 12 | 13 | 14 | // memdbgon must be the last include file in a .cpp file!!! 15 | #include "tier0/memdbgon.h" 16 | 17 | #define DETOUR_DECL_STATIC2_FASTCALL(name, ret, p1type, p1name, p2type, p2name) \ 18 | ret (__fastcall *name##_Actual)(p1type, p2type) = NULL; \ 19 | ret __fastcall name(p1type p1name, p2type p2name) 20 | 21 | #if SOURCE_ENGINE == SE_CSGO && defined PLATFORM_WINDOWS 22 | #define DETOUR_FUNC DETOUR_DECL_STATIC2_FASTCALL 23 | #else 24 | #define DETOUR_FUNC DETOUR_DECL_STATIC2 25 | #endif 26 | 27 | CollisionHook g_CollisionHook; 28 | SMEXT_LINK( &g_CollisionHook ); 29 | 30 | 31 | SH_DECL_HOOK0( IPhysics, CreateEnvironment, SH_NOATTRIB, 0 , IPhysicsEnvironment * ); 32 | SH_DECL_HOOK1_void( IPhysicsEnvironment, SetCollisionSolver, SH_NOATTRIB, 0, IPhysicsCollisionSolver * ); 33 | SH_DECL_HOOK4( IPhysicsCollisionSolver, ShouldCollide, SH_NOATTRIB, 0, int, IPhysicsObject *, IPhysicsObject *, void *, void * ); 34 | 35 | 36 | IGameConfig *g_pGameConf = NULL; 37 | CDetour *g_pFilterDetour = NULL; 38 | 39 | IPhysics *g_pPhysics = NULL; 40 | 41 | IForward *g_pCollisionFwd = NULL; 42 | IForward *g_pPassFwd = NULL; 43 | 44 | 45 | DETOUR_FUNC( PassServerEntityFilterFunc, bool, const IHandleEntity *, pTouch, const IHandleEntity *, pPass ) 46 | { 47 | if ( g_pPassFwd->GetFunctionCount() == 0 ) 48 | return DETOUR_STATIC_CALL( PassServerEntityFilterFunc )( pTouch, pPass ); 49 | 50 | if ( pTouch == pPass ) 51 | return DETOUR_STATIC_CALL( PassServerEntityFilterFunc )( pTouch, pPass ); // self checks aren't interesting 52 | 53 | if ( !pTouch || !pPass ) 54 | return DETOUR_STATIC_CALL( PassServerEntityFilterFunc )( pTouch, pPass ); // need two valid entities 55 | 56 | CBaseEntity *pEnt1 = const_cast( UTIL_EntityFromEntityHandle( pTouch ) ); 57 | CBaseEntity *pEnt2 = const_cast( UTIL_EntityFromEntityHandle( pPass ) ); 58 | 59 | if ( !pEnt1 || !pEnt2 ) 60 | return DETOUR_STATIC_CALL( PassServerEntityFilterFunc )( pTouch, pPass ); // we need both entities 61 | 62 | cell_t ent1 = gamehelpers->EntityToBCompatRef( pEnt1 ); 63 | cell_t ent2 = gamehelpers->EntityToBCompatRef( pEnt2 ); 64 | 65 | // todo: do we want to fill result with with the game's result? perhaps the forward path is more performant... 66 | cell_t result = 0; 67 | g_pPassFwd->PushCell( ent1 ); 68 | g_pPassFwd->PushCell( ent2 ); 69 | g_pPassFwd->PushCellByRef( &result ); 70 | 71 | cell_t retValue = 0; 72 | g_pPassFwd->Execute( &retValue ); 73 | 74 | if ( retValue > Pl_Continue ) 75 | { 76 | // plugin wants to change the result 77 | return result == 1; 78 | } 79 | 80 | // otherwise, game decides 81 | return DETOUR_STATIC_CALL( PassServerEntityFilterFunc )( pTouch, pPass ); 82 | } 83 | 84 | 85 | bool CollisionHook::SDK_OnLoad( char *error, size_t maxlength, bool late ) 86 | { 87 | sharesys->RegisterLibrary( myself, "collisionhook" ); 88 | 89 | char szConfError[ 256 ] = ""; 90 | if ( !gameconfs->LoadGameConfigFile( "collisionhook", &g_pGameConf, szConfError, sizeof( szConfError ) ) ) 91 | { 92 | V_snprintf( error, maxlength, "Could not read collisionhook gamedata: %s", szConfError ); 93 | return false; 94 | } 95 | 96 | CDetourManager::Init( g_pSM->GetScriptingEngine(), g_pGameConf ); 97 | 98 | g_pFilterDetour = DETOUR_CREATE_STATIC( PassServerEntityFilterFunc, "PassServerEntityFilter" ); 99 | if ( !g_pFilterDetour ) 100 | { 101 | V_snprintf( error, maxlength, "Unable to hook PassServerEntityFilter!" ); 102 | return false; 103 | } 104 | 105 | g_pFilterDetour->EnableDetour(); 106 | 107 | g_pCollisionFwd = forwards->CreateForward( "CH_ShouldCollide", ET_Hook, 3, NULL, Param_Cell, Param_Cell, Param_CellByRef ); 108 | g_pPassFwd = forwards->CreateForward( "CH_PassFilter", ET_Hook, 3, NULL, Param_Cell, Param_Cell, Param_CellByRef ); 109 | 110 | return true; 111 | } 112 | 113 | void CollisionHook::SDK_OnUnload() 114 | { 115 | forwards->ReleaseForward( g_pCollisionFwd ); 116 | forwards->ReleaseForward( g_pPassFwd ); 117 | 118 | gameconfs->CloseGameConfigFile( g_pGameConf ); 119 | 120 | if ( g_pFilterDetour ) 121 | { 122 | g_pFilterDetour->Destroy(); 123 | g_pFilterDetour = NULL; 124 | } 125 | } 126 | 127 | bool CollisionHook::SDK_OnMetamodLoad( ISmmAPI *ismm, char *error, size_t maxlen, bool late ) 128 | { 129 | GET_V_IFACE_CURRENT( GetPhysicsFactory, g_pPhysics, IPhysics, VPHYSICS_INTERFACE_VERSION ); 130 | 131 | SH_ADD_HOOK( IPhysics, CreateEnvironment, g_pPhysics, SH_MEMBER( this, &CollisionHook::CreateEnvironment ), false ); 132 | 133 | return true; 134 | } 135 | 136 | bool CollisionHook::SDK_OnMetamodUnload(char *error, size_t maxlength) 137 | { 138 | SH_REMOVE_HOOK( IPhysics, CreateEnvironment, g_pPhysics, SH_MEMBER( this, &CollisionHook::CreateEnvironment ), false ); 139 | 140 | g_pPhysics = NULL; 141 | 142 | return true; 143 | } 144 | 145 | 146 | IPhysicsEnvironment *CollisionHook::CreateEnvironment() 147 | { 148 | // in order to hook IPhysicsCollisionSolver::ShouldCollide, we need to know when a solver is installed 149 | // in order to hook any installed solvers, we need to hook any created physics environments 150 | 151 | IPhysicsEnvironment *pEnvironment = SH_CALL( g_pPhysics, &IPhysics::CreateEnvironment )(); 152 | 153 | if ( !pEnvironment ) 154 | RETURN_META_VALUE( MRES_SUPERCEDE, pEnvironment ); // just in case 155 | 156 | // hook so we know when a solver is installed 157 | SH_ADD_HOOK( IPhysicsEnvironment, SetCollisionSolver, pEnvironment, SH_MEMBER( this, &CollisionHook::SetCollisionSolver ), false ); 158 | 159 | RETURN_META_VALUE( MRES_SUPERCEDE, pEnvironment ); 160 | } 161 | 162 | void CollisionHook::SetCollisionSolver( IPhysicsCollisionSolver *pSolver ) 163 | { 164 | if ( !pSolver ) 165 | RETURN_META( MRES_IGNORED ); // this shouldn't happen, but knowing valve... 166 | 167 | // the game is installing a solver, hook the func we want 168 | SH_ADD_HOOK( IPhysicsCollisionSolver, ShouldCollide, pSolver, SH_MEMBER( this, &CollisionHook::VPhysics_ShouldCollide ), false ); 169 | 170 | RETURN_META( MRES_IGNORED ); 171 | } 172 | 173 | int CollisionHook::VPhysics_ShouldCollide( IPhysicsObject *pObj1, IPhysicsObject *pObj2, void *pGameData1, void *pGameData2 ) 174 | { 175 | if ( g_pCollisionFwd->GetFunctionCount() == 0 ) 176 | RETURN_META_VALUE( MRES_IGNORED, 1 ); // no plugins are interested, let the game decide 177 | 178 | if ( pObj1 == pObj2 ) 179 | RETURN_META_VALUE( MRES_IGNORED, 1 ); // self collisions aren't interesting 180 | 181 | CBaseEntity *pEnt1 = reinterpret_cast( pGameData1 ); 182 | CBaseEntity *pEnt2 = reinterpret_cast( pGameData2 ); 183 | 184 | if ( !pEnt1 || !pEnt2 ) 185 | RETURN_META_VALUE( MRES_IGNORED, 1 ); // we need two entities 186 | 187 | cell_t ent1 = gamehelpers->EntityToBCompatRef( pEnt1 ); 188 | cell_t ent2 = gamehelpers->EntityToBCompatRef( pEnt2 ); 189 | 190 | // todo: do we want to fill result with with the game's result? perhaps the forward path is more performant... 191 | cell_t result = 0; 192 | g_pCollisionFwd->PushCell( ent1 ); 193 | g_pCollisionFwd->PushCell( ent2 ); 194 | g_pCollisionFwd->PushCellByRef( &result ); 195 | 196 | cell_t retValue = 0; 197 | g_pCollisionFwd->Execute( &retValue ); 198 | 199 | if ( retValue > Pl_Continue ) 200 | { 201 | // plugin wants to change the result 202 | RETURN_META_VALUE( MRES_SUPERCEDE, result == 1 ); 203 | } 204 | 205 | // otherwise, game decides 206 | RETURN_META_VALUE( MRES_IGNORED, 0 ); 207 | } 208 | -------------------------------------------------------------------------------- /extension.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _INCLUDE_COLLISIONHOOK_EXTENSION_H_ 3 | #define _INCLUDE_COLLISIONHOOK_EXTENSION_H_ 4 | 5 | 6 | #include "smsdk_ext.h" 7 | 8 | 9 | class IPhysicsEnvironment; 10 | class IPhysicsCollisionSolver; 11 | class IPhysicsObject; 12 | 13 | 14 | class CollisionHook : 15 | public SDKExtension 16 | { 17 | 18 | public: 19 | /** 20 | * @brief This is called after the initial loading sequence has been processed. 21 | * 22 | * @param error Error message buffer. 23 | * @param maxlength Size of error message buffer. 24 | * @param late Whether or not the module was loaded after map load. 25 | * @return True to succeed loading, false to fail. 26 | */ 27 | virtual bool SDK_OnLoad( char *error, size_t maxlength, bool late ); 28 | 29 | /** 30 | * @brief This is called right before the extension is unloaded. 31 | */ 32 | virtual void SDK_OnUnload(); 33 | 34 | /** 35 | * @brief This is called once all known extensions have been loaded. 36 | * Note: It is is a good idea to add natives here, if any are provided. 37 | */ 38 | //virtual void SDK_OnAllLoaded(); 39 | 40 | /** 41 | * @brief Called when the pause state is changed. 42 | */ 43 | //virtual void SDK_OnPauseChange(bool paused); 44 | 45 | /** 46 | * @brief this is called when Core wants to know if your extension is working. 47 | * 48 | * @param error Error message buffer. 49 | * @param maxlength Size of error message buffer. 50 | * @return True if working, false otherwise. 51 | */ 52 | //virtual bool QueryRunning(char *error, size_t maxlength); 53 | public: 54 | #if defined SMEXT_CONF_METAMOD 55 | /** 56 | * @brief Called when Metamod is attached, before the extension version is called. 57 | * 58 | * @param error Error buffer. 59 | * @param maxlength Maximum size of error buffer. 60 | * @param late Whether or not Metamod considers this a late load. 61 | * @return True to succeed, false to fail. 62 | */ 63 | virtual bool SDK_OnMetamodLoad( ISmmAPI *ismm, char *error, size_t maxlength, bool late ); 64 | 65 | /** 66 | * @brief Called when Metamod is detaching, after the extension version is called. 67 | * NOTE: By default this is blocked unless sent from SourceMod. 68 | * 69 | * @param error Error buffer. 70 | * @param maxlength Maximum size of error buffer. 71 | * @return True to succeed, false to fail. 72 | */ 73 | virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); 74 | 75 | /** 76 | * @brief Called when Metamod's pause state is changing. 77 | * NOTE: By default this is blocked unless sent from SourceMod. 78 | * 79 | * @param paused Pause state being set. 80 | * @param error Error buffer. 81 | * @param maxlength Maximum size of error buffer. 82 | * @return True to succeed, false to fail. 83 | */ 84 | //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); 85 | #endif 86 | 87 | public: // hooks 88 | IPhysicsEnvironment *CreateEnvironment(); 89 | void SetCollisionSolver( IPhysicsCollisionSolver *pSolver ); 90 | int VPhysics_ShouldCollide( IPhysicsObject *pObj1, IPhysicsObject *pObj2, void *pGameData1, void *pGameData2 ); 91 | 92 | }; 93 | 94 | // adapted from util_shared.h 95 | inline const CBaseEntity *UTIL_EntityFromEntityHandle( const IHandleEntity *pConstHandleEntity ) 96 | { 97 | IHandleEntity *pHandleEntity = const_cast( pConstHandleEntity ); 98 | IServerUnknown *pUnk = static_cast( pHandleEntity ); 99 | 100 | return pUnk->GetBaseEntity(); 101 | } 102 | 103 | 104 | #endif // _INCLUDE_COLLISIONHOOK_EXTENSION_H_ 105 | -------------------------------------------------------------------------------- /smsdk_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod Sample Extension 5 | * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. 6 | * ============================================================================= 7 | * 8 | * This program is free software; you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License, version 3.0, as published by the 10 | * Free Software Foundation. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | * 20 | * As a special exception, AlliedModders LLC gives you permission to link the 21 | * code of this program (as well as its derivative works) to "Half-Life 2," the 22 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 23 | * by the Valve Corporation. You must obey the GNU General Public License in 24 | * all respects for all other code used. Additionally, AlliedModders LLC grants 25 | * this exception to all derivative works. AlliedModders LLC defines further 26 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 27 | * or . 28 | * 29 | * Version: $Id$ 30 | */ 31 | 32 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 33 | #define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 34 | 35 | /** 36 | * @file smsdk_config.h 37 | * @brief Contains macros for configuring basic extension information. 38 | */ 39 | 40 | /* Basic information exposed publicly */ 41 | #define SMEXT_CONF_NAME "CollisionHooks" 42 | #define SMEXT_CONF_DESCRIPTION "Hook on entity collision" 43 | #define SMEXT_CONF_VERSION "0.2" 44 | #define SMEXT_CONF_AUTHOR "VoiDeD" 45 | #define SMEXT_CONF_URL "http://saxtonhell.com" 46 | #define SMEXT_CONF_LOGTAG "CLHOOK" 47 | #define SMEXT_CONF_LICENSE "GPL" 48 | #define SMEXT_CONF_DATESTRING __DATE__ 49 | 50 | /** 51 | * @brief Exposes plugin's main interface. 52 | */ 53 | #define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name; 54 | 55 | /** 56 | * @brief Sets whether or not this plugin required Metamod. 57 | * NOTE: Uncomment to enable, comment to disable. 58 | */ 59 | #define SMEXT_CONF_METAMOD 60 | 61 | /** Enable interfaces you want to use here by uncommenting lines */ 62 | #define SMEXT_ENABLE_FORWARDSYS 63 | //#define SMEXT_ENABLE_HANDLESYS 64 | //#define SMEXT_ENABLE_PLAYERHELPERS 65 | //#define SMEXT_ENABLE_DBMANAGER 66 | #define SMEXT_ENABLE_GAMECONF 67 | //#define SMEXT_ENABLE_MEMUTILS 68 | #define SMEXT_ENABLE_GAMEHELPERS 69 | //#define SMEXT_ENABLE_TIMERSYS 70 | //#define SMEXT_ENABLE_THREADER 71 | //#define SMEXT_ENABLE_LIBSYS 72 | //#define SMEXT_ENABLE_MENUS 73 | //#define SMEXT_ENABLE_ADTFACTORY 74 | //#define SMEXT_ENABLE_PLUGINSYS 75 | //#define SMEXT_ENABLE_ADMINSYS 76 | //#define SMEXT_ENABLE_TEXTPARSERS 77 | //#define SMEXT_ENABLE_USERMSGS 78 | //#define SMEXT_ENABLE_TRANSLATOR 79 | //#define SMEXT_ENABLE_NINVOKE 80 | //#define SMEXT_ENABLE_ROOTCONSOLEMENU 81 | 82 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 83 | -------------------------------------------------------------------------------- /sourcemod/gamedata/collisionhook.txt: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * quack 4 | */ 5 | 6 | "Games" 7 | { 8 | "#default" 9 | { 10 | "Signatures" 11 | { 12 | "PassServerEntityFilter" 13 | { 14 | "library" "server" 15 | "windows" "\x55\x8B\xEC\x56\x8B\x2A\x2A\x85\x2A\x75\x2A\xB0\x2A\x5E" 16 | "linux" "@_Z22PassServerEntityFilterPK13IHandleEntityS1_" 17 | } 18 | } 19 | } 20 | 21 | "csgo" 22 | { 23 | "Signatures" 24 | { 25 | "PassServerEntityFilter" 26 | { 27 | "library" "server" 28 | "windows" "\x56\x8B\xF2\x57\x8B\xF9\x85\xF6\x74\x2A\x3B\xFE" 29 | "linux" "\x55\xB8\x01\x00\x00\x00\x89\xE5\x83\xEC\x38\x89\x5D\xF4" 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /sourcemod/scripting/include/collisionhook.inc: -------------------------------------------------------------------------------- 1 | 2 | #if defined _collisionhook_included 3 | #endinput 4 | #endif 5 | #define _collisionhook_included 6 | 7 | 8 | // called when the game is performing vphysics collision checks between entities 9 | // return something other than Plugin_Continue to have the game use the result parameter 10 | forward Action:CH_ShouldCollide( ent1, ent2, &bool:result ); 11 | 12 | // called when the game performs collision checks through traces (only for traces using filters) 13 | // return something other than Plugin_Continue to have the game use the result parameter 14 | // note: any code in this forward should be very performant, the game will use many filtered traces per player per game frame 15 | forward Action:CH_PassFilter( ent1, ent2, &bool:result ); 16 | 17 | 18 | public Extension:__ext_collisionhook = 19 | { 20 | name = "CollisionHook", 21 | file = "collisionhook.ext", 22 | 23 | #if defined AUTOLOAD_EXTENSIONS 24 | autoload = 1, 25 | #else 26 | autoload = 0, 27 | #endif 28 | #if defined REQUIRE_EXTENSIONS 29 | required = 1, 30 | #else 31 | required = 0, 32 | #endif 33 | 34 | }; 35 | --------------------------------------------------------------------------------