├── .arcconfig ├── .gitmodules ├── AMBuildScript ├── build.bat ├── buildbot ├── BreakpadSymbols ├── PackageScript ├── Versioning └── pushbuild.txt ├── configure.py ├── extension ├── AMBuilder ├── CDetour │ ├── detourhelpers.h │ ├── detours.cpp │ └── detours.h ├── asm │ ├── asm.c │ └── asm.h ├── blob.h ├── extension.cpp ├── extension.h ├── sdk │ ├── smsdk_config.h │ ├── smsdk_ext.cpp │ └── smsdk_ext.h ├── tickets.h ├── version.h └── version.rc ├── plugin ├── steamtools.inc └── steamtools.sp ├── product.version └── upload.py /.arcconfig: -------------------------------------------------------------------------------- 1 | { 2 | "conduit_uri" : "https://code.limetech.org/" 3 | } 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "open-steamworks"] 2 | path = open-steamworks 3 | url=https://github.com/SteamRE/open-steamworks 4 | -------------------------------------------------------------------------------- /AMBuildScript: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | import sys 4 | from ambuild.command import Command 5 | from ambuild.command import ShellCommand 6 | from ambuild.command import SymlinkCommand 7 | 8 | class ExtractDebugInfoCommand(Command): 9 | def __init__(self, binary, outfile): 10 | Command.__init__(self) 11 | self.binary = binary 12 | self.outfile = outfile 13 | 14 | def run(self, runner, job): 15 | if not self.binary.NeedsRelink(self.outfile): 16 | return 17 | 18 | if AMBuild.target['platform'] == 'linux': 19 | job.AddCommand(ShellCommand('objcopy --only-keep-debug ' + self.outfile + ' ' + self.outfile + '.dbg')) 20 | job.AddCommand(ShellCommand('objcopy --strip-debug ' + self.outfile)) 21 | job.AddCommand(ShellCommand('objcopy --add-gnu-debuglink=' + os.path.basename(self.outfile) + '.dbg ' + self.outfile)) 22 | elif AMBuild.target['platform'] == 'darwin': 23 | job.AddCommand(ShellCommand('dsymutil ' + self.outfile)) 24 | job.AddCommand(ShellCommand('strip -S ' + self.outfile)) 25 | 26 | class SM: 27 | def __init__(self): 28 | self.compiler = Cpp.Compiler() 29 | 30 | #Build SDK info 31 | self.possibleSdks = { } 32 | self.possibleSdks['ep1'] = {'sdk': 'HL2SDK', 'ext': '1.ep1', 'def': '1', 33 | 'name': 'EPISODEONE', 'platform': []} 34 | self.possibleSdks['ep2'] = {'sdk': 'HL2SDKOB', 'ext': '2.ep2', 'def': '3', 35 | 'name': 'ORANGEBOX', 'platform': []} 36 | self.possibleSdks['css'] = {'sdk': 'HL2SDKCSS', 'ext': '2.css', 'def': '6', 37 | 'name': 'CSS', 'platform': []} 38 | self.possibleSdks['ep2v'] = {'sdk': 'HL2SDKOBVALVE', 'ext': '2.ep2v', 'def': '7', 39 | 'name': 'ORANGEBOXVALVE', 'platform': ['windows', 'linux', 'darwin']} 40 | self.possibleSdks['l4d'] = {'sdk': 'HL2SDKL4D', 'ext': '2.l4d', 'def': '8', 41 | 'name': 'LEFT4DEAD', 'platform': []} 42 | self.possibleSdks['l4d2'] = {'sdk': 'HL2SDKL4D2', 'ext': '2.l4d2', 'def': '9', 43 | 'name': 'LEFT4DEAD2', 'platform': []} 44 | self.possibleSdks['darkm'] = {'sdk': 'HL2SDK-DARKM', 'ext': '2.darkm', 'def': '2', 45 | 'name': 'DARKMESSIAH', 'platform': []} 46 | self.possibleSdks['swarm'] = {'sdk': 'HL2SDK-SWARM', 'ext': '2.swarm', 'def': '10', 47 | 'name': 'ALIENSWARM', 'platform': []} 48 | self.possibleSdks['bgt'] = {'sdk': 'HL2SDK-BGT', 'ext': '2.bgt', 'def': '4', 49 | 'name': 'BLOODYGOODTIME', 'platform': []} 50 | self.possibleSdks['eye'] = {'sdk': 'HL2SDK-EYE', 'ext': '2.eye', 'def': '5', 51 | 'name': 'EYE', 'platform': []} 52 | self.possibleSdks['csgo'] = {'sdk': 'HL2SDKCSGO', 'ext': '2.csgo', 'def': '12', 53 | 'name': 'CSGO', 'platform': []} 54 | 55 | self.sdkInfo = { } 56 | 57 | if AMBuild.mode == 'config': 58 | #Detect compilers 59 | self.compiler.DetectAll(AMBuild) 60 | 61 | #Detect variables 62 | envvars = { 'MMSOURCE19': 'mmsource-1.9', 63 | 'SOURCEMOD16': 'sourcemod-1.7', 64 | 'HL2SDKCSS': 'hl2sdk-css', 65 | 'HL2SDKOBVALVE': 'hl2sdk-tf2', 66 | 'HL2SDKL4D': 'hl2sdk-l4d', 67 | 'HL2SDKL4D2': 'hl2sdk-l4d2', 68 | 'HL2SDKCSGO': 'hl2sdk-csgo' 69 | } 70 | 71 | if AMBuild.target['platform'] != 'darwin': 72 | envvars['HL2SDK'] = 'hl2sdk' 73 | envvars['HL2SDKOB'] = 'hl2sdk-ob' 74 | 75 | if AMBuild.target['platform'] == 'windows': 76 | envvars['HL2SDK-DARKM'] = 'hl2sdk-darkm' 77 | envvars['HL2SDK-SWARM'] = 'hl2sdk-swarm' 78 | envvars['HL2SDK-BGT'] = 'hl2sdk-bgt' 79 | envvars['HL2SDK-EYE'] = 'hl2sdk-eye' 80 | 81 | # Finds if a dict with `key` set to `value` is present on the dict of dicts `dictionary` 82 | def findDictByKey(dictionary, key, value): 83 | for index in dictionary: 84 | elem = dictionary[index] 85 | if elem[key] == value: 86 | return (elem, index) 87 | return None 88 | 89 | for i in envvars: 90 | if i in os.environ: 91 | path = os.environ[i] 92 | if not os.path.isdir(path): 93 | raise Exception('Path for {0} was not found: {1}'.format(i, path)) 94 | elif i.startswith('HL2SDK'): 95 | (info, sdk) = findDictByKey(self.possibleSdks, 'sdk', i) 96 | self.sdkInfo[sdk] = info 97 | else: 98 | head = os.getcwd() 99 | oldhead = None 100 | while head != None and head != oldhead: 101 | path = os.path.join(head, envvars[i]) 102 | if os.path.isdir(path): 103 | break 104 | oldhead = head 105 | head, tail = os.path.split(head) 106 | if i.startswith('HL2SDK'): 107 | if head != None and head != oldhead: 108 | (info, sdk) = findDictByKey(self.possibleSdks, 'sdk', i) 109 | self.sdkInfo[sdk] = info 110 | elif head == None or head == oldhead: 111 | raise Exception('Could not find a valid path for {0}'.format(i)) 112 | AMBuild.cache.CacheVariable(i, path) 113 | 114 | if len(self.sdkInfo) < 1: 115 | raise Exception('At least one SDK must be available.') 116 | 117 | AMBuild.cache.CacheVariable('sdkInfo', self.sdkInfo) 118 | 119 | #Set up defines 120 | cxx = self.compiler.cxx 121 | if isinstance(cxx, Cpp.CompatGCC): 122 | if isinstance(cxx, Cpp.GCC): 123 | self.vendor = 'gcc' 124 | elif isinstance(cxx, Cpp.Clang): 125 | self.vendor = 'clang' 126 | self.compiler.AddToListVar('CDEFINES', 'stricmp=strcasecmp') 127 | self.compiler.AddToListVar('CDEFINES', '_stricmp=strcasecmp') 128 | self.compiler.AddToListVar('CDEFINES', '_snprintf=snprintf') 129 | self.compiler.AddToListVar('CDEFINES', '_vsnprintf=vsnprintf') 130 | self.compiler.AddToListVar('CFLAGS', '-pipe') 131 | self.compiler.AddToListVar('CFLAGS', '-fno-strict-aliasing') 132 | if (self.vendor == 'gcc' and cxx.majorVersion >= 4) or self.vendor == 'clang': 133 | self.compiler.AddToListVar('CFLAGS', '-fvisibility=hidden') 134 | self.compiler.AddToListVar('CXXFLAGS', '-fvisibility-inlines-hidden') 135 | self.compiler.AddToListVar('CFLAGS', '-Wall') 136 | self.compiler.AddToListVar('CFLAGS', '-Wno-uninitialized') 137 | self.compiler.AddToListVar('CFLAGS', '-Wno-unused') 138 | self.compiler.AddToListVar('CFLAGS', '-Wno-switch') 139 | self.compiler.AddToListVar('CFLAGS', '-msse') 140 | self.compiler.AddToListVar('CFLAGS', '-g3') 141 | self.compiler.AddToListVar('CFLAGS', '-m32') 142 | self.compiler.AddToListVar('POSTLINKFLAGS', '-m32') 143 | self.compiler.AddToListVar('CXXFLAGS', '-fpermissive') 144 | self.compiler.AddToListVar('CXXFLAGS', '-fno-exceptions') 145 | self.compiler.AddToListVar('CXXFLAGS', '-fno-threadsafe-statics') 146 | self.compiler.AddToListVar('CXXFLAGS', '-Wno-non-virtual-dtor') 147 | self.compiler.AddToListVar('CXXFLAGS', '-Wno-overloaded-virtual') 148 | if (self.vendor == 'gcc' and cxx.majorVersion >= 4 and cxx.minorVersion >= 3) or \ 149 | (self.vendor == 'clang' and cxx.majorVersion >= 3): 150 | self.compiler.AddToListVar('CXXFLAGS', '-Wno-delete-non-virtual-dtor') 151 | self.compiler.AddToListVar('CDEFINES', 'HAVE_STDINT_H') 152 | self.compiler.AddToListVar('CDEFINES', 'GNUC') 153 | if self.vendor == 'gcc': 154 | self.compiler.AddToListVar('CFLAGS', '-mfpmath=sse') 155 | elif isinstance(cxx, Cpp.MSVC): 156 | self.vendor = 'msvc' 157 | if AMBuild.options.debug == '1': 158 | self.compiler.AddToListVar('CFLAGS', '/MTd') 159 | self.compiler.AddToListVar('POSTLINKFLAGS', '/NODEFAULTLIB:libcmt') 160 | else: 161 | self.compiler.AddToListVar('CFLAGS', '/MT') 162 | self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_DEPRECATE') 163 | self.compiler.AddToListVar('CDEFINES', '_CRT_SECURE_NO_WARNINGS') 164 | self.compiler.AddToListVar('CDEFINES', '_CRT_NONSTDC_NO_DEPRECATE') 165 | self.compiler.AddToListVar('CXXFLAGS', '/EHsc') 166 | self.compiler.AddToListVar('CFLAGS', '/W3') 167 | self.compiler.AddToListVar('CFLAGS', '/nologo') 168 | self.compiler.AddToListVar('CFLAGS', '/Zi') 169 | self.compiler.AddToListVar('CXXFLAGS', '/TP') 170 | self.compiler.AddToListVar('POSTLINKFLAGS', '/DEBUG') 171 | self.compiler.AddToListVar('POSTLINKFLAGS', '/MACHINE:X86') 172 | self.compiler.AddToListVar('POSTLINKFLAGS', '/SUBSYSTEM:WINDOWS') 173 | self.compiler.AddToListVar('POSTLINKFLAGS', 'kernel32.lib') 174 | self.compiler.AddToListVar('POSTLINKFLAGS', 'user32.lib') 175 | self.compiler.AddToListVar('POSTLINKFLAGS', 'gdi32.lib') 176 | self.compiler.AddToListVar('POSTLINKFLAGS', 'winspool.lib') 177 | self.compiler.AddToListVar('POSTLINKFLAGS', 'comdlg32.lib') 178 | self.compiler.AddToListVar('POSTLINKFLAGS', 'advapi32.lib') 179 | self.compiler.AddToListVar('POSTLINKFLAGS', 'shell32.lib') 180 | self.compiler.AddToListVar('POSTLINKFLAGS', 'ole32.lib') 181 | self.compiler.AddToListVar('POSTLINKFLAGS', 'oleaut32.lib') 182 | self.compiler.AddToListVar('POSTLINKFLAGS', 'uuid.lib') 183 | self.compiler.AddToListVar('POSTLINKFLAGS', 'odbc32.lib') 184 | self.compiler.AddToListVar('POSTLINKFLAGS', 'odbccp32.lib') 185 | #self.compiler.AddToListVar('POSTLINKFLAGS', 'legacy_stdio_definitions.lib') 186 | 187 | #Optimization 188 | if AMBuild.options.opt == '1': 189 | self.compiler.AddToListVar('CDEFINES', 'NDEBUG') 190 | if self.vendor == 'gcc' or self.vendor == 'clang': 191 | self.compiler.AddToListVar('CFLAGS', '-O3') 192 | elif self.vendor == 'msvc': 193 | self.compiler.AddToListVar('CFLAGS', '/Ox') 194 | self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:ICF') 195 | self.compiler.AddToListVar('POSTLINKFLAGS', '/OPT:REF') 196 | 197 | #Debugging 198 | if AMBuild.options.debug == '1': 199 | self.compiler.AddToListVar('CDEFINES', 'DEBUG') 200 | self.compiler.AddToListVar('CDEFINES', '_DEBUG') 201 | if self.vendor == 'msvc': 202 | self.compiler.AddToListVar('CFLAGS', '/Od') 203 | self.compiler.AddToListVar('CFLAGS', '/RTC1') 204 | 205 | #Platform-specifics 206 | if AMBuild.target['platform'] == 'linux': 207 | self.compiler.AddToListVar('CDEFINES', '_LINUX') 208 | self.compiler.AddToListVar('CDEFINES', 'POSIX') 209 | if self.vendor == 'gcc': 210 | self.compiler.AddToListVar('POSTLINKFLAGS', '-static-libgcc') 211 | if self.vendor == 'clang': 212 | self.compiler.AddToListVar('POSTLINKFLAGS', '-lgcc_eh') 213 | elif AMBuild.target['platform'] == 'darwin': 214 | self.compiler.AddToListVar('CDEFINES', 'OSX') 215 | self.compiler.AddToListVar('CDEFINES', '_OSX') 216 | self.compiler.AddToListVar('CDEFINES', 'POSIX') 217 | self.compiler.AddToListVar('POSTLINKFLAGS', '-mmacosx-version-min=10.5') 218 | self.compiler.AddToListVar('POSTLINKFLAGS', ['-arch', 'i386']) 219 | self.compiler.AddToListVar('POSTLINKFLAGS', '-lstdc++') 220 | 221 | # For OS X dylib versioning 222 | import re 223 | productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r') 224 | productContents = productFile.read() 225 | productFile.close() 226 | m = re.match('(\d+)\.(\d+)\.(\d+).*', productContents) 227 | if m == None: 228 | self.version = '1.0.0' 229 | else: 230 | major, minor, release = m.groups() 231 | self.version = '{0}.{1}.{2}'.format(major, minor, release) 232 | AMBuild.cache.CacheVariable('version', self.version) 233 | elif AMBuild.target['platform'] == 'windows': 234 | self.compiler.AddToListVar('CDEFINES', 'WIN32') 235 | self.compiler.AddToListVar('CDEFINES', '_WINDOWS') 236 | 237 | #Finish up 238 | self.compiler.AddToListVar('CDEFINES', 'SOURCEMOD_BUILD') 239 | self.compiler.AddToListVar('CDEFINES', 'SM_GENERATED_BUILD') 240 | self.compiler.AddToListVar('CINCLUDES', 241 | os.path.join(AMBuild.outputFolder, 'includes')) 242 | self.compiler.ToConfig(AMBuild, 'compiler') 243 | AMBuild.cache.CacheVariable('vendor', self.vendor) 244 | self.targetMap = { } 245 | AMBuild.cache.CacheVariable('targetMap', self.targetMap) 246 | else: 247 | self.sdkInfo = AMBuild.cache['sdkInfo'] 248 | self.compiler.FromConfig(AMBuild, 'compiler') 249 | self.targetMap = AMBuild.cache['targetMap'] 250 | 251 | if AMBuild.target['platform'] == 'windows': 252 | self.compiler.AddToListVar('RCINCLUDES', os.path.join(AMBuild.sourceFolder, 'extension')) 253 | self.mmsPath = AMBuild.cache['MMSOURCE19'] 254 | 255 | def DefaultCompiler(self): 256 | return self.compiler.Clone() 257 | 258 | def JobMatters(self, jobname): 259 | file = sys._getframe().f_code.co_filename 260 | if AMBuild.mode == 'config': 261 | self.targetMap[jobname] = file 262 | return True 263 | if len(AMBuild.args) == 0: 264 | return True 265 | if not jobname in AMBuild.args: 266 | return False 267 | 268 | def AutoVersion(self, folder, binary): 269 | if AMBuild.target['platform'] == 'windows': 270 | env = {'RCDEFINES': ['BINARY_NAME="' + binary.binaryFile + '"', 'SM_GENERATED_BUILD']} 271 | binary.AddResourceFile(os.path.join(folder, 'version.rc' ), env) 272 | elif AMBuild.target['platform'] == 'darwin' and isinstance(binary, Cpp.LibraryBuilder): 273 | binary.compiler['POSTLINKFLAGS'].extend(['-compatibility_version', '1.0.0']) 274 | binary.compiler['POSTLINKFLAGS'].extend(['-current_version', AMBuild.cache['version']]) 275 | else: 276 | return 277 | 278 | def ExtractDebugInfo(self, job, binary): 279 | src = os.path.join('..', AMBuild.outputFolder, job.workFolder, binary.binaryFile) 280 | job.AddCommand(ExtractDebugInfoCommand(binary, src)) 281 | 282 | def PreSetupHL2Job(self, job, builder, sdk): 283 | info = self.sdkInfo[sdk] 284 | sdkPath = AMBuild.cache[info['sdk']] 285 | if AMBuild.target['platform'] == 'linux': 286 | if sdk == 'ep1': 287 | staticLibs = os.path.join(sdkPath, 'linux_sdk') 288 | else: 289 | staticLibs = os.path.join(sdkPath, 'lib', 'linux') 290 | workFolder = os.path.join(AMBuild.outputFolder, job.workFolder) 291 | if sdk == 'ep2v': 292 | libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib_srv.so', 'libtier0_srv.so'] 293 | for lib in libs: 294 | link = os.path.join(workFolder, lib) 295 | target = os.path.join(staticLibs, lib) 296 | try: 297 | os.lstat(link) 298 | except: 299 | job.AddCommand(SymlinkCommand(link, target)) 300 | elif sdk in ['css', 'l4d', 'l4d2', 'csgo']: 301 | libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.so', 'libtier0.so'] 302 | if sdk == 'csgo': 303 | libs.append('interfaces_i486.a') 304 | for lib in libs: 305 | link = os.path.join(workFolder, lib) 306 | target = os.path.join(staticLibs, lib) 307 | try: 308 | os.lstat(link) 309 | except: 310 | job.AddCommand(SymlinkCommand(link, target)) 311 | else: 312 | libs = ['tier1_i486.a', 'mathlib_i486.a', 'vstdlib_i486.so', 'tier0_i486.so'] 313 | for lib in libs: 314 | link = os.path.join(workFolder, lib) 315 | target = os.path.join(staticLibs, lib) 316 | try: 317 | os.lstat(link) 318 | except: 319 | job.AddCommand(SymlinkCommand(link, target)) 320 | elif AMBuild.target['platform'] == 'darwin': 321 | staticLibs = os.path.join(sdkPath, 'lib', 'mac') 322 | workFolder = os.path.join(AMBuild.outputFolder, job.workFolder) 323 | libs = ['tier1_i486.a', 'mathlib_i486.a', 'libvstdlib.dylib', 'libtier0.dylib'] 324 | if sdk == 'csgo': 325 | libs.append('interfaces_i486.a') 326 | for lib in libs: 327 | link = os.path.join(workFolder, lib) 328 | target = os.path.join(staticLibs, lib) 329 | try: 330 | os.lstat(link) 331 | except: 332 | job.AddCommand(SymlinkCommand(link, target)) 333 | elif AMBuild.target['platform'] == 'windows': 334 | libs = ['tier0', 'tier1', 'vstdlib', 'mathlib'] 335 | if sdk in ['swarm', 'csgo']: 336 | libs.append('interfaces') 337 | for lib in libs: 338 | libPath = os.path.join(sdkPath, 'lib', 'public', lib) + '.lib' 339 | builder.RebuildIfNewer(libPath) 340 | builder['POSTLINKFLAGS'].append(libPath) 341 | 342 | def PostSetupHL2Job(self, job, builder, sdk): 343 | if AMBuild.target['platform'] in ['linux', 'darwin']: 344 | builder.AddObjectFiles(['tier1_i486.a', 'mathlib_i486.a']) 345 | if sdk == 'csgo': 346 | builder.AddObjectFiles(['interfaces_i486.a']) 347 | 348 | def DefaultHL2Compiler(self, path, sdk, noLink = False, oldMms = '-legacy'): 349 | compiler = self.DefaultCompiler() 350 | 351 | mms = 'core' 352 | if sdk == 'ep1': 353 | mms += oldMms 354 | 355 | compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms)) 356 | compiler['CXXINCLUDES'].append(os.path.join(self.mmsPath, mms, 'sourcehook')) 357 | 358 | info = self.possibleSdks 359 | compiler['CDEFINES'].extend(['SE_' + info[i]['name'] + '=' + info[i]['def'] for i in info]) 360 | 361 | paths = [['public'], ['public', 'engine'], ['public', 'mathlib'], ['public', 'vstdlib'], ['public', 'tier0'], ['public', 'tier1']] 362 | if sdk == 'ep1' or sdk == 'darkm': 363 | paths.append(['public', 'dlls']) 364 | paths.append(['game_shared']) 365 | else: 366 | paths.append(['public', 'game', 'server']) 367 | paths.append(['public', 'toolframework']) 368 | paths.append(['game', 'shared']) 369 | paths.append(['common']) 370 | 371 | info = self.sdkInfo[sdk] 372 | sdkPath = AMBuild.cache[info['sdk']] 373 | 374 | compiler['CDEFINES'].append('SOURCE_ENGINE=' + info['def']) 375 | 376 | if sdk in ['swarm','csgo']: 377 | if AMBuild.target['platform'] == 'windows': 378 | compiler['CDEFINES'].extend(['COMPILER_MSVC', 'COMPILER_MSVC32']) 379 | else: 380 | compiler['CDEFINES'].extend(['COMPILER_GCC', 'POSIX']) 381 | 382 | if sdk == 'ep1': 383 | if AMBuild.target['platform'] == 'linux': 384 | staticLibs = os.path.join(sdkPath, 'linux_sdk') 385 | else: 386 | if AMBuild.target['platform'] == 'linux': 387 | staticLibs = os.path.join(sdkPath, 'lib', 'linux') 388 | elif AMBuild.target['platform'] == 'darwin': 389 | staticLibs = os.path.join(sdkPath, 'lib', 'mac') 390 | 391 | for i in paths: 392 | compiler['CXXINCLUDES'].append(os.path.join(sdkPath, *i)) 393 | 394 | if not noLink: 395 | if AMBuild.target['platform'] == 'linux': 396 | compiler['POSTLINKFLAGS'][0:0] = ['-lm'] 397 | if sdk == 'ep2v': 398 | compiler['POSTLINKFLAGS'][0:0] = ['libtier0_srv.so'] 399 | compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib_srv.so'] 400 | elif sdk in ['css', 'l4d', 'l4d2', 'csgo']: 401 | compiler['POSTLINKFLAGS'][0:0] = ['libtier0.so'] 402 | compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.so'] 403 | else: 404 | compiler['POSTLINKFLAGS'][0:0] = ['tier0_i486.so'] 405 | compiler['POSTLINKFLAGS'][0:0] = ['vstdlib_i486.so'] 406 | elif AMBuild.target['platform'] == 'darwin': 407 | compiler['POSTLINKFLAGS'][0:0] = ['libtier0.dylib'] 408 | compiler['POSTLINKFLAGS'][0:0] = ['libvstdlib.dylib'] 409 | 410 | return compiler 411 | 412 | sm = SM() 413 | globals = { 414 | 'SM': sm 415 | } 416 | 417 | AMBuild.Include(os.path.join('buildbot', 'Versioning'), globals) 418 | 419 | FileList = [ 420 | ['extension', 'AMBuilder'], 421 | ['buildbot', 'PackageScript'], 422 | ['buildbot', 'BreakpadSymbols'] 423 | ] 424 | 425 | for parts in FileList: 426 | AMBuild.Include(os.path.join(*parts), globals) 427 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | mkdir build 2 | cd build 3 | call "%VS100COMNTOOLS%\vsvars32.bat" 4 | ..\configure.py 5 | build.py 6 | pause 7 | cd .. -------------------------------------------------------------------------------- /buildbot/BreakpadSymbols: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | from ambuild.command import Command 4 | from ambuild.command import ShellCommand 5 | 6 | try: 7 | import urllib.request as urllib 8 | except ImportError: 9 | import urllib2 as urllib 10 | 11 | class IterateDebugInfoCommand(Command): 12 | def run(self, master, job): 13 | pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'rt') 14 | for debug_info in pdblog: 15 | debug_info = os.path.join(AMBuild.outputFolder, debug_info.strip()) 16 | job.AddCommand(SymbolCommand(debug_info, symbolServer)) 17 | pdblog.close() 18 | 19 | class SymbolCommand(ShellCommand): 20 | def __init__(self, debugFile, symbolServer): 21 | self.serverResponse = None 22 | self.symbolServer = symbolServer 23 | if AMBuild.target['platform'] == 'linux': 24 | cmdstring = "dump_syms {0} {1}".format(debugFile, os.path.dirname(debugFile)) 25 | elif AMBuild.target['platform'] == 'darwin': 26 | cmdstring = "dump_syms {0}".format(debugFile) 27 | elif AMBuild.target['platform'] == 'windows': 28 | cmdstring = "dump_syms.exe {0}".format(debugFile) 29 | ShellCommand.__init__(self, cmdstring) 30 | def run(self, master, job): 31 | ShellCommand.run(self, master, job) 32 | if self.stdout != None and len(self.stdout) > 0: 33 | request = urllib.Request(symbolServer, self.stdout.encode('utf-8')) 34 | request.add_header("Content-Type", "text/plain") 35 | self.serverResponse = urllib.urlopen(request).read().decode('utf-8') 36 | def spew(self, runner): 37 | if self.stderr != None and len(self.stderr) > 0: 38 | runner.PrintOut(self.stderr) 39 | if self.serverResponse != None and len(self.serverResponse) > 0: 40 | runner.PrintOut(self.serverResponse) 41 | 42 | if 'BREAKPAD_SYMBOL_SERVER' in os.environ: 43 | symbolServer = os.environ['BREAKPAD_SYMBOL_SERVER'] 44 | job = AMBuild.AddJob('breakpad-symbols') 45 | job.AddCommand(IterateDebugInfoCommand()) 46 | -------------------------------------------------------------------------------- /buildbot/PackageScript: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | import shutil 4 | import ambuild.osutil as osutil 5 | from ambuild.command import Command 6 | 7 | job = AMBuild.AddJob('package') 8 | 9 | class DestroyPath(Command): 10 | def __init__(self, folder): 11 | Command.__init__(self) 12 | self.folder = folder 13 | 14 | def destroy(self, path): 15 | entries = os.listdir(path) 16 | for entry in entries: 17 | newpath = os.path.join(path, entry) 18 | if os.path.isdir(newpath): 19 | self.destroy(newpath) 20 | os.rmdir(newpath) 21 | elif os.path.isfile(newpath): 22 | os.remove(newpath) 23 | 24 | def run(self, runner, job): 25 | runner.PrintOut('rm -rf {0}/*'.format(self.folder)) 26 | self.destroy(self.folder) 27 | 28 | class CreateFolders(Command): 29 | def __init__(self, folders): 30 | Command.__init__(self) 31 | self.folders = folders 32 | 33 | def run(self, runner, job): 34 | for folder in self.folders: 35 | path = os.path.join(*folder) 36 | runner.PrintOut('mkdir {0}'.format(path)) 37 | os.makedirs(path) 38 | 39 | #Shallow folder copy 40 | class CopyFolder(Command): 41 | def __init__(self, fromList, toList, excludes = []): 42 | Command.__init__(self) 43 | self.fromPath = os.path.join(AMBuild.sourceFolder, *fromList) 44 | self.toPath = os.path.join(*toList) 45 | self.excludes = excludes 46 | 47 | def run(self, runner, job): 48 | entries = os.listdir(self.fromPath) 49 | for entry in entries: 50 | if entry in self.excludes: 51 | continue 52 | path = os.path.join(self.fromPath, entry) 53 | if not os.path.isfile(path): 54 | continue 55 | runner.PrintOut('copy {0} to {1}'.format(path, self.toPath)) 56 | shutil.copy(path, self.toPath) 57 | 58 | #Single file copy 59 | class CopyFile(Command): 60 | def __init__(self, fromFile, toPath): 61 | Command.__init__(self) 62 | self.fromFile = fromFile 63 | self.toPath = toPath 64 | 65 | def run(self, runner, job): 66 | runner.PrintOut('copy {0} to {1}'.format(self.fromFile, self.toPath)) 67 | shutil.copy(self.fromFile, self.toPath) 68 | 69 | 70 | folders = [ 71 | ['addons', 'sourcemod', 'extensions'], 72 | ['addons', 'sourcemod', 'scripting', 'include'] 73 | ] 74 | 75 | 76 | #Setup 77 | job.AddCommand(DestroyPath(os.path.join(AMBuild.outputFolder, 'package'))) 78 | job.AddCommand(CreateFolders(folders)) 79 | 80 | #Copy Files 81 | job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'plugin', 'steamtools.inc'), 82 | os.path.join('addons', 'sourcemod', 'scripting', 'include'))) 83 | job.AddCommand(CopyFile(os.path.join(AMBuild.sourceFolder, 'plugin', 'steamtools.sp'), 84 | os.path.join('addons', 'sourcemod', 'scripting'))) 85 | 86 | bincopies = [] 87 | 88 | def AddNormalLibrary(name, dest): 89 | dest = os.path.join('addons', 'sourcemod', dest) 90 | bincopies.append(CopyFile(os.path.join('..', name, name + osutil.SharedLibSuffix()), dest)) 91 | 92 | # Each platform's version of dump_syms needs the path in a different format. 93 | if AMBuild.target['platform'] == 'linux': 94 | debug_info.append(name + '/' + name + '.so') 95 | elif AMBuild.target['platform'] == 'darwin': 96 | debug_info.append(name + '/' + name + '.dylib.dSYM') 97 | elif AMBuild.target['platform'] == 'windows': 98 | debug_info.append(name + '\\' + name + '.pdb') 99 | 100 | def AddHL2Library(name, dest): 101 | for i in SM.sdkInfo: 102 | sdk = SM.sdkInfo[i] 103 | if AMBuild.target['platform'] not in sdk['platform']: 104 | continue 105 | AddNormalLibrary(name + '.ext.' + sdk['ext'], dest) 106 | 107 | debug_info = [] 108 | 109 | AddHL2Library('steamtools', 'extensions') 110 | 111 | job.AddCommandGroup(bincopies) 112 | 113 | pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'wt') 114 | for pdb in debug_info: 115 | pdblog.write(pdb + '\n') 116 | pdblog.close() 117 | 118 | -------------------------------------------------------------------------------- /buildbot/Versioning: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | import re 4 | import subprocess 5 | from ambuild.cache import Cache 6 | import ambuild.command as command 7 | 8 | #Quickly try to ascertain the current repository revision 9 | def GetVersion(): 10 | p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) 11 | (stdout, stderr) = p.communicate() 12 | stdout = stdout.decode('UTF-8') 13 | return stdout.rstrip('\r\n') 14 | 15 | def PerformReversioning(): 16 | cset = GetVersion() 17 | cacheFile = os.path.join(AMBuild.outputFolder, '.ambuild', 'hgcache') 18 | cache = Cache(cacheFile) 19 | if os.path.isfile(cacheFile): 20 | cache.LoadCache() 21 | if cache.HasVariable('cset') and cache['cset'] == cset: 22 | return False 23 | cache.CacheVariable('cset', cset) 24 | 25 | productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r') 26 | productContents = productFile.read() 27 | productFile.close() 28 | m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents) 29 | if m == None: 30 | raise Exception('Could not detremine product version') 31 | major, minor, release, tag = m.groups() 32 | 33 | incFolder = os.path.join(AMBuild.sourceFolder, 'extension') 34 | incFile = open(os.path.join(incFolder, 'version_auto.h'), 'w') 35 | incFile.write(""" 36 | #ifndef _AUTO_VERSION_INFORMATION_H_ 37 | #define _AUTO_VERSION_INFORMATION_H_ 38 | 39 | #define SM_BUILD_TAG \"{0}\" 40 | #define SM_BUILD_UNIQUEID \"{1}\" 41 | #define SM_VERSION \"{2}.{3}.{4}\" 42 | #define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG "+" SM_BUILD_UNIQUEID 43 | #define SM_FILE_VERSION {5},{6},{7},0 44 | 45 | #endif /* _AUTO_VERSION_INFORMATION_H_ */ 46 | 47 | """.format(tag, cset, major, minor, release, major, minor, release)) 48 | incFile.close() 49 | cache.WriteCache() 50 | 51 | PerformReversioning() 52 | 53 | 54 | -------------------------------------------------------------------------------- /buildbot/pushbuild.txt: -------------------------------------------------------------------------------- 1 | Oh look, text! 2 | -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet: 2 | import sys 3 | import ambuild.runner as runner 4 | 5 | run = runner.Runner() 6 | run.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', 7 | help='Enable debugging symbols') 8 | run.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', 9 | help='Enable optimization') 10 | run.Configure(sys.path[0]) 11 | -------------------------------------------------------------------------------- /extension/AMBuilder: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 tw=99 noet ft=python: 2 | import os 3 | 4 | for i in SM.sdkInfo: 5 | sdk = SM.sdkInfo[i] 6 | if AMBuild.target['platform'] not in sdk['platform']: 7 | continue 8 | 9 | compiler = SM.DefaultHL2Compiler('extension', i) 10 | 11 | compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'extension')) 12 | compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'extension', 'sdk')) 13 | 14 | compiler['CXXINCLUDES'].append(os.path.join(AMBuild.cache['SOURCEMOD16'], 'public')) 15 | compiler['CXXINCLUDES'].append(os.path.join(AMBuild.cache['SOURCEMOD16'], 'public', 'sourcepawn')) 16 | 17 | compiler['CXXINCLUDES'].append(os.path.join(AMBuild.sourceFolder, 'open-steamworks', 'Open Steamworks')) 18 | 19 | if compiler.cc.name == 'gcc': 20 | compiler['CFLAGS'].append('-Wno-parentheses') 21 | 22 | if i != 'ep1': 23 | compiler['CDEFINES'].append('HOOKING_ENABLED') 24 | 25 | name = 'steamtools.ext.' + sdk['ext'] 26 | extension = AMBuild.AddJob(name) 27 | binary = Cpp.LibraryBuilder(name, AMBuild, extension, compiler) 28 | SM.PreSetupHL2Job(extension, binary, i) 29 | 30 | binary.AddSourceFiles('extension', [ 31 | 'extension.cpp', 32 | 'sdk/smsdk_ext.cpp', 33 | 'asm/asm.c', 34 | 'CDetour/detours.cpp', 35 | ]) 36 | 37 | SM.PostSetupHL2Job(extension, binary, i) 38 | SM.AutoVersion('extension', binary) 39 | SM.ExtractDebugInfo(extension, binary) 40 | binary.SendToJob() 41 | -------------------------------------------------------------------------------- /extension/CDetour/detourhelpers.h: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod 5 | * Copyright (C) 2004-2010 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: detourhelpers.h 248 2008-08-27 00:56:22Z pred $ 30 | */ 31 | 32 | #ifndef _INCLUDE_SOURCEMOD_DETOURHELPERS_H_ 33 | #define _INCLUDE_SOURCEMOD_DETOURHELPERS_H_ 34 | 35 | #if defined PLATFORM_POSIX 36 | #include 37 | #ifndef PAGE_SIZE 38 | #define PAGE_SIZE 4096 39 | #endif 40 | #define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1)) 41 | #define PAGE_EXECUTE_READWRITE PROT_READ|PROT_WRITE|PROT_EXEC 42 | #endif 43 | 44 | struct patch_t 45 | { 46 | patch_t() 47 | { 48 | patch[0] = 0; 49 | bytes = 0; 50 | } 51 | unsigned char patch[20]; 52 | size_t bytes; 53 | }; 54 | 55 | inline void ProtectMemory(void *addr, int length, int prot) 56 | { 57 | #if defined PLATFORM_POSIX 58 | void *addr2 = (void *)ALIGN(addr); 59 | mprotect(addr2, sysconf(_SC_PAGESIZE), prot); 60 | #elif defined PLATFORM_WINDOWS 61 | DWORD old_prot; 62 | VirtualProtect(addr, length, prot, &old_prot); 63 | #endif 64 | } 65 | 66 | inline void SetMemPatchable(void *address, size_t size) 67 | { 68 | ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE); 69 | } 70 | 71 | inline void DoGatePatch(unsigned char *target, void *callback) 72 | { 73 | SetMemPatchable(target, 20); 74 | 75 | target[0] = 0xFF; /* JMP */ 76 | target[1] = 0x25; /* MEM32 */ 77 | *(void **)(&target[2]) = callback; 78 | } 79 | 80 | inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore) 81 | { 82 | ProtectMemory(address, 20, PAGE_EXECUTE_READWRITE); 83 | 84 | unsigned char *addr = (unsigned char *)address + offset; 85 | if (restore) 86 | { 87 | for (size_t i=0; ibytes; i++) 88 | { 89 | restore->patch[i] = addr[i]; 90 | } 91 | restore->bytes = patch->bytes; 92 | } 93 | 94 | for (size_t i=0; ibytes; i++) 95 | { 96 | addr[i] = patch->patch[i]; 97 | } 98 | } 99 | 100 | #endif //_INCLUDE_SOURCEMOD_DETOURHELPERS_H_ 101 | -------------------------------------------------------------------------------- /extension/CDetour/detours.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod 5 | * Copyright (C) 2004-2010 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: detours.cpp 248 2008-08-27 00:56:22Z pred $ 30 | */ 31 | 32 | #include "detours.h" 33 | #include 34 | 35 | ISourcePawnEngine *CDetourManager::spengine = NULL; 36 | IGameConfig *CDetourManager::gameconf = NULL; 37 | 38 | void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf) 39 | { 40 | CDetourManager::spengine = spengine; 41 | CDetourManager::gameconf = gameconf; 42 | } 43 | 44 | CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, const char *signame) 45 | { 46 | CDetour *detour = new CDetour(callbackfunction, trampoline, signame); 47 | if (detour) 48 | { 49 | if (!detour->Init(spengine, gameconf)) 50 | { 51 | delete detour; 52 | return NULL; 53 | } 54 | 55 | return detour; 56 | } 57 | 58 | return NULL; 59 | } 60 | 61 | CDetour *CDetourManager::CreateDetour(void *callbackfunction, void **trampoline, void *pAddress) 62 | { 63 | CDetour *detour = new CDetour(callbackfunction, trampoline, pAddress); 64 | if (detour) 65 | { 66 | if (!detour->Init(spengine, gameconf)) 67 | { 68 | delete detour; 69 | return NULL; 70 | } 71 | 72 | return detour; 73 | } 74 | 75 | return NULL; 76 | } 77 | 78 | CDetour::CDetour(void *callbackfunction, void **trampoline, const char *signame) 79 | { 80 | enabled = false; 81 | detoured = false; 82 | detour_address = NULL; 83 | detour_trampoline = NULL; 84 | this->signame = signame; 85 | this->detour_callback = callbackfunction; 86 | spengine = NULL; 87 | gameconf = NULL; 88 | this->trampoline = trampoline; 89 | } 90 | 91 | CDetour::CDetour(void*callbackfunction, void **trampoline, void *pAddress) 92 | { 93 | enabled = false; 94 | detoured = false; 95 | detour_address = pAddress; 96 | detour_trampoline = NULL; 97 | this->signame = NULL; 98 | this->detour_callback = callbackfunction; 99 | spengine = NULL; 100 | gameconf = NULL; 101 | this->trampoline = trampoline; 102 | } 103 | 104 | bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf) 105 | { 106 | this->spengine = spengine; 107 | this->gameconf = gameconf; 108 | 109 | if (!CreateDetour()) 110 | { 111 | enabled = false; 112 | return enabled; 113 | } 114 | 115 | enabled = true; 116 | 117 | return enabled; 118 | } 119 | 120 | void CDetour::Destroy() 121 | { 122 | DeleteDetour(); 123 | delete this; 124 | } 125 | 126 | bool CDetour::IsEnabled() 127 | { 128 | return enabled; 129 | } 130 | 131 | bool CDetour::CreateDetour() 132 | { 133 | if (signame && !gameconf->GetMemSig(signame, &detour_address)) 134 | { 135 | g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame); 136 | return false; 137 | } 138 | else if(!detour_address) 139 | { 140 | g_pSM->LogError(myself, "Invalid detour address passed - Disabling detour to prevent crashes"); 141 | return false; 142 | } 143 | 144 | if (!detour_address) 145 | { 146 | g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour to prevent crashes", signame); 147 | return false; 148 | } 149 | 150 | detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1); 151 | 152 | /* First, save restore bits */ 153 | for (size_t i=0; iAllocatePageMemory(CodeSize); 182 | spengine->SetReadWrite(wr.outbase); 183 | wr.outptr = wr.outbase; 184 | detour_trampoline = wr.outbase; 185 | goto jit_rewind; 186 | } 187 | 188 | spengine->SetReadExecute(wr.outbase); 189 | 190 | *trampoline = detour_trampoline; 191 | 192 | return true; 193 | } 194 | 195 | void CDetour::DeleteDetour() 196 | { 197 | if (detoured) 198 | { 199 | DisableDetour(); 200 | } 201 | 202 | if (detour_trampoline) 203 | { 204 | /* Free the allocated trampoline memory */ 205 | spengine->FreePageMemory(detour_trampoline); 206 | detour_trampoline = NULL; 207 | } 208 | } 209 | 210 | void CDetour::EnableDetour() 211 | { 212 | if (!detoured) 213 | { 214 | DoGatePatch((unsigned char *)detour_address, &detour_callback); 215 | detoured = true; 216 | } 217 | } 218 | 219 | void CDetour::DisableDetour() 220 | { 221 | if (detoured) 222 | { 223 | /* Remove the patch */ 224 | ApplyPatch(detour_address, 0, &detour_restore, NULL); 225 | detoured = false; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /extension/CDetour/detours.h: -------------------------------------------------------------------------------- 1 | /** 2 | * vim: set ts=4 : 3 | * ============================================================================= 4 | * SourceMod 5 | * Copyright (C) 2004-2010 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: detours.h 257 2008-09-23 03:12:13Z pred $ 30 | */ 31 | 32 | #ifndef _INCLUDE_SOURCEMOD_DETOURS_H_ 33 | #define _INCLUDE_SOURCEMOD_DETOURS_H_ 34 | 35 | #include "extension.h" 36 | #include 37 | #include 38 | #include "detourhelpers.h" 39 | 40 | /** 41 | * CDetours class for SourceMod Extensions by pRED* 42 | * detourhelpers.h entirely stolen from CSS:DM and were written by BAILOPAN (I assume). 43 | * asm.h/c from devmaster.net (thanks cybermind) edited by pRED* to handle gcc -fPIC thunks correctly 44 | * Concept by Nephyrin Zey (http://www.doublezen.net/) and Windows Detour Library (http://research.microsoft.com/sn/detours/) 45 | * Member function pointer ideas by Don Clugston (http://www.codeproject.com/cpp/FastDelegate.asp) 46 | */ 47 | 48 | #define DETOUR_MEMBER_CALL(name) (this->*name##_Actual) 49 | #define DETOUR_STATIC_CALL(name) (name##_Actual) 50 | 51 | #define DETOUR_DECL_STATIC0(name, ret) \ 52 | ret (*name##_Actual)(void) = NULL; \ 53 | ret name(void) 54 | 55 | #define DETOUR_DECL_STATIC1(name, ret, p1type, p1name) \ 56 | ret (*name##_Actual)(p1type) = NULL; \ 57 | ret name(p1type p1name) 58 | 59 | #define DETOUR_DECL_STATIC2(name, ret, p1type, p1name, p2type, p2name) \ 60 | ret (*name##_Actual)(p1type, p2type) = NULL; \ 61 | ret name(p1type p1name, p2type p2name) 62 | 63 | #define DETOUR_DECL_STATIC4(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \ 64 | ret (*name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \ 65 | ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name) 66 | 67 | #define DETOUR_DECL_MEMBER0(name, ret) \ 68 | class name##Class \ 69 | { \ 70 | public: \ 71 | ret name(); \ 72 | static ret (name##Class::* name##_Actual)(void); \ 73 | }; \ 74 | ret (name##Class::* name##Class::name##_Actual)(void) = NULL; \ 75 | ret name##Class::name() 76 | 77 | #define DETOUR_DECL_MEMBER1(name, ret, p1type, p1name) \ 78 | class name##Class \ 79 | { \ 80 | public: \ 81 | ret name(p1type p1name); \ 82 | static ret (name##Class::* name##_Actual)(p1type); \ 83 | }; \ 84 | ret (name##Class::* name##Class::name##_Actual)(p1type) = NULL; \ 85 | ret name##Class::name(p1type p1name) 86 | 87 | #define DETOUR_DECL_MEMBER2(name, ret, p1type, p1name, p2type, p2name) \ 88 | class name##Class \ 89 | { \ 90 | public: \ 91 | ret name(p1type p1name, p2type p2name); \ 92 | static ret (name##Class::* name##_Actual)(p1type, p2type); \ 93 | }; \ 94 | ret (name##Class::* name##Class::name##_Actual)(p1type, p2type) = NULL; \ 95 | ret name##Class::name(p1type p1name, p2type p2name) 96 | 97 | #define DETOUR_DECL_MEMBER3(name, ret, p1type, p1name, p2type, p2name, p3type, p3name) \ 98 | class name##Class \ 99 | { \ 100 | public: \ 101 | ret name(p1type p1name, p2type p2name, p3type p3name); \ 102 | static ret (name##Class::* name##_Actual)(p1type, p2type, p3type); \ 103 | }; \ 104 | ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type) = NULL; \ 105 | ret name##Class::name(p1type p1name, p2type p2name, p3type p3name) 106 | 107 | #define DETOUR_DECL_MEMBER4(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name) \ 108 | class name##Class \ 109 | { \ 110 | public: \ 111 | ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name); \ 112 | static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type); \ 113 | }; \ 114 | ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type) = NULL; \ 115 | ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name) 116 | 117 | #define DETOUR_DECL_MEMBER8(name, ret, p1type, p1name, p2type, p2name, p3type, p3name, p4type, p4name, p5type, p5name, p6type, p6name, p7type, p7name, p8type, p8name) \ 118 | class name##Class \ 119 | { \ 120 | public: \ 121 | ret name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name); \ 122 | static ret (name##Class::* name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type); \ 123 | }; \ 124 | ret (name##Class::* name##Class::name##_Actual)(p1type, p2type, p3type, p4type, p5type, p6type, p7type, p8type) = NULL; \ 125 | ret name##Class::name(p1type p1name, p2type p2name, p3type p3name, p4type p4name, p5type p5name, p6type p6name, p7type p7name, p8type p8name) 126 | 127 | 128 | #define GET_MEMBER_CALLBACK(name) (void *)GetCodeAddress(&name##Class::name) 129 | #define GET_MEMBER_TRAMPOLINE(name) (void **)(&name##Class::name##_Actual) 130 | 131 | #define GET_STATIC_CALLBACK(name) (void *)&name 132 | #define GET_STATIC_TRAMPOLINE(name) (void **)&name##_Actual 133 | 134 | #define DETOUR_CREATE_MEMBER(name, gamedata) CDetourManager::CreateDetour(GET_MEMBER_CALLBACK(name), GET_MEMBER_TRAMPOLINE(name), gamedata); 135 | #define DETOUR_CREATE_STATIC(name, gamedata) CDetourManager::CreateDetour(GET_STATIC_CALLBACK(name), GET_STATIC_TRAMPOLINE(name), gamedata); 136 | 137 | 138 | class GenericClass {}; 139 | typedef void (GenericClass::*VoidFunc)(); 140 | 141 | inline void *GetCodeAddr(VoidFunc mfp) 142 | { 143 | return *(void **)&mfp; 144 | } 145 | 146 | /** 147 | * Converts a member function pointer to a void pointer. 148 | * This relies on the assumption that the code address lies at mfp+0 149 | * This is the case for both g++ and later MSVC versions on non virtual functions but may be different for other compilers 150 | * Based on research by Don Clugston : http://www.codeproject.com/cpp/FastDelegate.asp 151 | */ 152 | #define GetCodeAddress(mfp) GetCodeAddr(reinterpret_cast(mfp)) 153 | 154 | class CDetourManager; 155 | 156 | class CDetour 157 | { 158 | public: 159 | 160 | bool IsEnabled(); 161 | 162 | /** 163 | * These would be somewhat self-explanatory I hope 164 | */ 165 | void EnableDetour(); 166 | void DisableDetour(); 167 | 168 | void Destroy(); 169 | 170 | friend class CDetourManager; 171 | 172 | protected: 173 | CDetour(void *callbackfunction, void **trampoline, const char *signame); 174 | CDetour(void*callbackfunction, void **trampoline, void *pAddress); 175 | 176 | bool Init(ISourcePawnEngine *spengine, IGameConfig *gameconf); 177 | private: 178 | 179 | /* These create/delete the allocated memory */ 180 | bool CreateDetour(); 181 | void DeleteDetour(); 182 | 183 | bool enabled; 184 | bool detoured; 185 | 186 | patch_t detour_restore; 187 | /* Address of the detoured function */ 188 | void *detour_address; 189 | /* Address of the allocated trampoline function */ 190 | void *detour_trampoline; 191 | /* Address of the callback handler */ 192 | void *detour_callback; 193 | /* The function pointer used to call our trampoline */ 194 | void **trampoline; 195 | 196 | const char *signame; 197 | ISourcePawnEngine *spengine; 198 | IGameConfig *gameconf; 199 | }; 200 | 201 | class CDetourManager 202 | { 203 | public: 204 | 205 | static void Init(ISourcePawnEngine *spengine, IGameConfig *gameconf); 206 | 207 | /** 208 | * Creates a new detour 209 | * 210 | * @param callbackfunction Void pointer to your detour callback function. 211 | * @param trampoline Address of the trampoline pointer 212 | * @param signame Section name containing a signature to fetch from the gamedata file. 213 | * @return A new CDetour pointer to control your detour. 214 | * 215 | * Example: 216 | * 217 | * CBaseServer::ConnectClient(netadr_s &, int, int, int, char const*, char const*, char const*, int) 218 | * 219 | * Define a new class with the required function and a member function pointer to the same type: 220 | * 221 | * class CBaseServerDetour 222 | * { 223 | * public: 224 | * bool ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int); 225 | * static bool (CBaseServerDetour::* ConnectClient_Actual)(void *netaddr_s, int, int, int, char const*, char const*, char const*, int); 226 | * } 227 | * 228 | * void *callbackfunc = GetCodeAddress(&CBaseServerDetour::ConnectClient); 229 | * void **trampoline = (void **)(&CBaseServerDetour::ConnectClient_Actual); 230 | * 231 | * Creation: 232 | * CDetourManager::CreateDetour(callbackfunc, trampoline, "ConnectClient"); 233 | * 234 | * Usage: 235 | * 236 | * CBaseServerDetour::ConnectClient(void *netaddr_s, int, int, int, char const*, char const*, char const*, int) 237 | * { 238 | * //pre hook code 239 | * bool result = (this->*ConnectClient_Actual)(netaddr_s, rest of params); 240 | * //post hook code 241 | * return result; 242 | * } 243 | * 244 | * Note we changed the netadr_s reference into a void* to avoid needing to define the type 245 | */ 246 | static CDetour *CreateDetour(void *callbackfunction, void **trampoline, const char *signame); 247 | static CDetour *CreateDetour(void *callbackfunction, void **trampoline, void *pAddress); 248 | 249 | friend class CBlocker; 250 | friend class CDetour; 251 | 252 | private: 253 | static ISourcePawnEngine *spengine; 254 | static IGameConfig *gameconf; 255 | }; 256 | 257 | #endif // _INCLUDE_SOURCEMOD_DETOURS_H_ 258 | -------------------------------------------------------------------------------- /extension/asm/asm.c: -------------------------------------------------------------------------------- 1 | #include "asm.h" 2 | 3 | #ifndef WIN32 4 | #define _GNU_SOURCE 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define REG_EAX 0 11 | #define REG_ECX 1 12 | #define REG_EDX 2 13 | #define REG_EBX 3 14 | 15 | #define IA32_MOV_REG_IMM 0xB8 // encoding is +r 16 | #endif 17 | 18 | /** 19 | * Checks if a call to a fpic thunk has just been written into dest. 20 | * If found replaces it with a direct mov that sets the required register to the value of pc. 21 | * 22 | * @param dest Destination buffer where a call opcode + addr (5 bytes) has just been written. 23 | * @param pc The program counter value that needs to be set (usually the next address from the source). 24 | * @noreturn 25 | */ 26 | void check_thunks(unsigned char *dest, unsigned char *pc) 27 | { 28 | #ifndef WIN32 29 | /* Step write address back 4 to the start of the function address */ 30 | unsigned char *writeaddr = dest - 4; 31 | unsigned char *calloffset = *(unsigned char **)writeaddr; 32 | unsigned char *calladdr = (unsigned char *)(dest + (unsigned int)calloffset); 33 | 34 | /* Lookup name of function being called */ 35 | if ((*calladdr == 0x8B) && (*(calladdr+2) == 0x24) && (*(calladdr+3) == 0xC3)) 36 | { 37 | //a thunk maybe? 38 | char movByte = IA32_MOV_REG_IMM; 39 | 40 | /* Calculate the correct mov opcode */ 41 | switch (*(calladdr+1)) 42 | { 43 | case 0x04: 44 | { 45 | movByte += REG_EAX; 46 | break; 47 | } 48 | case 0x1C: 49 | { 50 | movByte += REG_EBX; 51 | break; 52 | } 53 | case 0x0C: 54 | { 55 | movByte += REG_ECX; 56 | break; 57 | } 58 | case 0x14: 59 | { 60 | movByte += REG_EDX; 61 | break; 62 | } 63 | default: 64 | { 65 | printf("Unknown thunk: %c\n", *(calladdr+1)); 66 | #ifndef NDEBUG 67 | abort(); 68 | #endif 69 | break; 70 | } 71 | } 72 | 73 | /* Move our write address back one to where the call opcode was */ 74 | writeaddr--; 75 | 76 | 77 | /* Write our mov */ 78 | *writeaddr = movByte; 79 | writeaddr++; 80 | 81 | /* Write the value - The provided program counter value */ 82 | *(void **)writeaddr = (void *)pc; 83 | writeaddr += 4; 84 | } 85 | #endif 86 | } 87 | 88 | //if dest is NULL, returns minimum number of bytes needed to be copied 89 | //if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs 90 | //http://www.devmaster.net/forums/showthread.php?t=2311 91 | int copy_bytes(unsigned char *func, unsigned char* dest, int required_len) { 92 | int bytecount = 0; 93 | 94 | while(bytecount < required_len && *func != 0xCC) 95 | { 96 | // prefixes F0h, F2h, F3h, 66h, 67h, D8h-DFh, 2Eh, 36h, 3Eh, 26h, 64h and 65h 97 | int operandSize = 4; 98 | int FPU = 0; 99 | int twoByte = 0; 100 | unsigned char opcode = 0x90; 101 | unsigned char modRM = 0xFF; 102 | while(*func == 0xF0 || 103 | *func == 0xF2 || 104 | *func == 0xF3 || 105 | (*func & 0xFC) == 0x64 || 106 | (*func & 0xF8) == 0xD8 || 107 | (*func & 0x7E) == 0x62) 108 | { 109 | if(*func == 0x66) 110 | { 111 | operandSize = 2; 112 | } 113 | else if((*func & 0xF8) == 0xD8) 114 | { 115 | FPU = *func; 116 | if (dest) 117 | *dest++ = *func++; 118 | else 119 | func++; 120 | bytecount++; 121 | break; 122 | } 123 | 124 | if (dest) 125 | *dest++ = *func++; 126 | else 127 | func++; 128 | bytecount++; 129 | } 130 | 131 | // two-byte opcode byte 132 | if(*func == 0x0F) 133 | { 134 | twoByte = 1; 135 | if (dest) 136 | *dest++ = *func++; 137 | else 138 | func++; 139 | bytecount++; 140 | } 141 | 142 | // opcode byte 143 | opcode = *func++; 144 | if (dest) *dest++ = opcode; 145 | bytecount++; 146 | 147 | // mod R/M byte 148 | modRM = 0xFF; 149 | if(FPU) 150 | { 151 | if((opcode & 0xC0) != 0xC0) 152 | { 153 | modRM = opcode; 154 | } 155 | } 156 | else if(!twoByte) 157 | { 158 | if((opcode & 0xC4) == 0x00 || 159 | ((opcode & 0xF4) == 0x60 && ((opcode & 0x0A) == 0x02 || (opcode & 0x09) == 0x09)) || 160 | (opcode & 0xF0) == 0x80 || 161 | ((opcode & 0xF8) == 0xC0 && (opcode & 0x0E) != 0x02) || 162 | (opcode & 0xFC) == 0xD0 || 163 | (opcode & 0xF6) == 0xF6) 164 | { 165 | modRM = *func++; 166 | if (dest) *dest++ = modRM; 167 | bytecount++; 168 | } 169 | } 170 | else 171 | { 172 | if(((opcode & 0xF0) == 0x00 && (opcode & 0x0F) >= 0x04 && (opcode & 0x0D) != 0x0D) || 173 | (opcode & 0xF0) == 0x30 || 174 | opcode == 0x77 || 175 | (opcode & 0xF0) == 0x80 || 176 | ((opcode & 0xF0) == 0xA0 && (opcode & 0x07) <= 0x02) || 177 | (opcode & 0xF8) == 0xC8) 178 | { 179 | // No mod R/M byte 180 | } 181 | else 182 | { 183 | modRM = *func++; 184 | if (dest) *dest++ = modRM; 185 | bytecount++; 186 | } 187 | } 188 | 189 | // SIB 190 | if((modRM & 0x07) == 0x04 && 191 | (modRM & 0xC0) != 0xC0) 192 | { 193 | if (dest) 194 | *dest++ = *func++; //SIB 195 | else 196 | func++; 197 | bytecount++; 198 | } 199 | 200 | // mod R/M displacement 201 | 202 | // Dword displacement, no base 203 | if((modRM & 0xC5) == 0x05) { 204 | if (dest) { 205 | *(unsigned int*)dest = *(unsigned int*)func; 206 | dest += 4; 207 | } 208 | func += 4; 209 | bytecount += 4; 210 | } 211 | 212 | // Byte displacement 213 | if((modRM & 0xC0) == 0x40) { 214 | if (dest) 215 | *dest++ = *func++; 216 | else 217 | func++; 218 | bytecount++; 219 | } 220 | 221 | // Dword displacement 222 | if((modRM & 0xC0) == 0x80) { 223 | if (dest) { 224 | *(unsigned int*)dest = *(unsigned int*)func; 225 | dest += 4; 226 | } 227 | func += 4; 228 | bytecount += 4; 229 | } 230 | 231 | // immediate 232 | if(FPU) 233 | { 234 | // Can't have immediate operand 235 | } 236 | else if(!twoByte) 237 | { 238 | if((opcode & 0xC7) == 0x04 || 239 | (opcode & 0xFE) == 0x6A || // PUSH/POP/IMUL 240 | (opcode & 0xF0) == 0x70 || // Jcc 241 | opcode == 0x80 || 242 | opcode == 0x83 || 243 | (opcode & 0xFD) == 0xA0 || // MOV 244 | opcode == 0xA8 || // TEST 245 | (opcode & 0xF8) == 0xB0 || // MOV 246 | (opcode & 0xFE) == 0xC0 || // RCL 247 | opcode == 0xC6 || // MOV 248 | opcode == 0xCD || // INT 249 | (opcode & 0xFE) == 0xD4 || // AAD/AAM 250 | (opcode & 0xF8) == 0xE0 || // LOOP/JCXZ 251 | opcode == 0xEB || 252 | (opcode == 0xF6 && (modRM & 0x30) == 0x00)) // TEST 253 | { 254 | if (dest) 255 | *dest++ = *func++; 256 | else 257 | func++; 258 | bytecount++; 259 | } 260 | else if((opcode & 0xF7) == 0xC2) // RET 261 | { 262 | if (dest) { 263 | *(unsigned short*)dest = *(unsigned short*)func; 264 | dest += 2; 265 | } 266 | func += 2; 267 | bytecount += 2; 268 | } 269 | else if((opcode & 0xFC) == 0x80 || 270 | (opcode & 0xC7) == 0x05 || 271 | (opcode & 0xF8) == 0xB8 || 272 | (opcode & 0xFE) == 0xE8 || // CALL/Jcc 273 | (opcode & 0xFE) == 0x68 || 274 | (opcode & 0xFC) == 0xA0 || 275 | (opcode & 0xEE) == 0xA8 || 276 | opcode == 0xC7 || 277 | (opcode == 0xF7 && (modRM & 0x30) == 0x00)) 278 | { 279 | if (dest) { 280 | //Fix CALL/JMP offset 281 | if ((opcode & 0xFE) == 0xE8) { 282 | if (operandSize == 4) 283 | { 284 | *(long*)dest = ((func + *(long*)func) - dest); 285 | 286 | //pRED* edit. func is the current address of the call address, +4 is the next instruction, so the value of $pc 287 | check_thunks(dest+4, func+4); 288 | } 289 | else 290 | *(short*)dest = ((func + *(short*)func) - dest); 291 | 292 | } else { 293 | if (operandSize == 4) 294 | *(unsigned long*)dest = *(unsigned long*)func; 295 | else 296 | *(unsigned short*)dest = *(unsigned short*)func; 297 | } 298 | dest += operandSize; 299 | } 300 | func += operandSize; 301 | bytecount += operandSize; 302 | 303 | } 304 | } 305 | else 306 | { 307 | if(opcode == 0xBA || // BT 308 | opcode == 0x0F || // 3DNow! 309 | (opcode & 0xFC) == 0x70 || // PSLLW 310 | (opcode & 0xF7) == 0xA4 || // SHLD 311 | opcode == 0xC2 || 312 | opcode == 0xC4 || 313 | opcode == 0xC5 || 314 | opcode == 0xC6) 315 | { 316 | if (dest) 317 | *dest++ = *func++; 318 | else 319 | func++; 320 | } 321 | else if((opcode & 0xF0) == 0x80) // Jcc -i 322 | { 323 | if (dest) { 324 | if (operandSize == 4) 325 | *(unsigned long*)dest = *(unsigned long*)func; 326 | else 327 | *(unsigned short*)dest = *(unsigned short*)func; 328 | 329 | dest += operandSize; 330 | } 331 | func += operandSize; 332 | bytecount += operandSize; 333 | } 334 | } 335 | } 336 | 337 | return bytecount; 338 | } 339 | 340 | //insert a specific JMP instruction at the given location 341 | void inject_jmp(void* src, void* dest) { 342 | *(unsigned char*)src = OP_JMP; 343 | *(long*)((unsigned char*)src+1) = (long)((unsigned char*)dest - ((unsigned char*)src + OP_JMP_SIZE)); 344 | } 345 | 346 | //fill a given block with NOPs 347 | void fill_nop(void* src, unsigned int len) { 348 | unsigned char* src2 = (unsigned char*)src; 349 | while (len) { 350 | *src2++ = OP_NOP; 351 | --len; 352 | } 353 | } 354 | 355 | void* eval_jump(void* src) { 356 | unsigned char* addr = (unsigned char*)src; 357 | 358 | if (!addr) return 0; 359 | 360 | //import table jump 361 | if (addr[0] == OP_PREFIX && addr[1] == OP_JMP_SEG) { 362 | addr += 2; 363 | addr = *(unsigned char**)addr; 364 | //TODO: if addr points into the IAT 365 | return *(void**)addr; 366 | } 367 | 368 | //8bit offset 369 | else if (addr[0] == OP_JMP_BYTE) { 370 | addr = &addr[OP_JMP_BYTE_SIZE] + *(char*)&addr[1]; 371 | //mangled 32bit jump? 372 | if (addr[0] == OP_JMP) { 373 | addr = addr + *(int*)&addr[1]; 374 | } 375 | return addr; 376 | } 377 | /* 378 | //32bit offset 379 | else if (addr[0] == OP_JMP) { 380 | addr = &addr[OP_JMP_SIZE] + *(int*)&addr[1]; 381 | } 382 | */ 383 | 384 | return addr; 385 | } 386 | /* 387 | from ms detours package 388 | static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress) 389 | { 390 | MEMORY_BASIC_INFORMATION mbi; 391 | VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi)); 392 | __try { 393 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; 394 | if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { 395 | return false; 396 | } 397 | 398 | PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + 399 | pDosHeader->e_lfanew); 400 | if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { 401 | return false; 402 | } 403 | 404 | if (pbAddress >= ((PBYTE)pDosHeader + 405 | pNtHeader->OptionalHeader 406 | .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && 407 | pbAddress < ((PBYTE)pDosHeader + 408 | pNtHeader->OptionalHeader 409 | .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + 410 | pNtHeader->OptionalHeader 411 | .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) { 412 | return true; 413 | } 414 | return false; 415 | } 416 | __except(EXCEPTION_EXECUTE_HANDLER) { 417 | return false; 418 | } 419 | } 420 | */ 421 | -------------------------------------------------------------------------------- /extension/asm/asm.h: -------------------------------------------------------------------------------- 1 | #ifndef __ASM_H__ 2 | #define __ASM_H__ 3 | 4 | #define OP_JMP 0xE9 5 | #define OP_JMP_SIZE 5 6 | 7 | #define OP_NOP 0x90 8 | #define OP_NOP_SIZE 1 9 | 10 | #define OP_PREFIX 0xFF 11 | #define OP_JMP_SEG 0x25 12 | 13 | #define OP_JMP_BYTE 0xEB 14 | #define OP_JMP_BYTE_SIZE 2 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | void check_thunks(unsigned char *dest, unsigned char *pc); 21 | 22 | //if dest is NULL, returns minimum number of bytes needed to be copied 23 | //if dest is not NULL, it will copy the bytes to dest as well as fix CALLs and JMPs 24 | //http://www.devmaster.net/forums/showthread.php?t=2311 25 | int copy_bytes(unsigned char *func, unsigned char* dest, int required_len); 26 | 27 | //insert a specific JMP instruction at the given location 28 | void inject_jmp(void* src, void* dest); 29 | 30 | //fill a given block with NOPs 31 | void fill_nop(void* src, unsigned int len); 32 | 33 | //evaluate a JMP at the target 34 | void* eval_jump(void* src); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif //__ASM_H__ 41 | -------------------------------------------------------------------------------- /extension/blob.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ====================================================== 3 | * OpenCoordinator 4 | * Copyright (C) 2011 The OpenCoordinator Team. 5 | * All rights reserved. 6 | * ====================================================== 7 | * 8 | * This software is provided 'as-is', without any express or implied warranty. 9 | * In no event will the authors be held liable for any damages arising from 10 | * the use of this software. 11 | * 12 | * Permission is granted to anyone to use this software for any purpose, 13 | * including commercial applications, and to alter it and redistribute it 14 | * freely, subject to the following restrictions: 15 | * 16 | * 1. The origin of this software must not be misrepresented; you must not 17 | * claim that you wrote the original software. If you use this software in a 18 | * product, an acknowledgment in the product documentation would be 19 | * appreciated but is not required. 20 | * 2. Altered source versions must be plainly marked as such, and must not be 21 | * misrepresented as being the original software. 22 | * 3. This notice may not be removed or altered from any source distribution. 23 | */ 24 | 25 | #pragma once 26 | 27 | class CBlob 28 | { 29 | public: 30 | CBlob(const void *pMessage, size_t iSize) 31 | : m_pMessage(pMessage) 32 | , m_iSize(iSize) 33 | , m_iCurrentOffset(0) 34 | {} 35 | 36 | private: 37 | const void *m_pMessage; 38 | size_t m_iSize; 39 | size_t m_iCurrentOffset; 40 | 41 | public: 42 | inline size_t GetPosition() 43 | { 44 | return m_iCurrentOffset; 45 | } 46 | 47 | inline size_t AdvancePosition(size_t iAdditionalOffset) 48 | { 49 | m_iCurrentOffset += iAdditionalOffset; 50 | return m_iCurrentOffset; 51 | } 52 | 53 | inline size_t RewindPosition(size_t iAdditionalNegativeOffset) 54 | { 55 | m_iCurrentOffset -= iAdditionalNegativeOffset; 56 | return m_iCurrentOffset; 57 | } 58 | 59 | inline void SetPosition(size_t iNewOffset) 60 | { 61 | m_iCurrentOffset = iNewOffset; 62 | } 63 | 64 | inline void ResetPosition() 65 | { 66 | m_iCurrentOffset = 0; 67 | } 68 | 69 | public: 70 | template 71 | inline T Read(bool *bError = NULL) 72 | { 73 | if ((m_iCurrentOffset + sizeof(T)) > m_iSize) 74 | { 75 | if (bError) 76 | *bError = true; 77 | return (T)0; 78 | } 79 | 80 | T tempBuffer = *((T *)((intptr_t)m_pMessage + m_iCurrentOffset)); 81 | m_iCurrentOffset += sizeof(T); 82 | 83 | if (bError) 84 | *bError = false; 85 | 86 | return tempBuffer; 87 | } 88 | 89 | template 90 | inline bool Read(T *pOut) 91 | { 92 | if ((m_iCurrentOffset + sizeof(T)) > m_iSize) 93 | return false; 94 | 95 | T *pTempBuffer = (T *)((intptr_t)m_pMessage + m_iCurrentOffset); 96 | m_iCurrentOffset += sizeof(T); 97 | memcpy(pOut, pTempBuffer, sizeof(T)); 98 | 99 | return true; 100 | } 101 | 102 | inline bool Read(void *pOut, size_t iLength) 103 | { 104 | if ((m_iCurrentOffset + iLength) > m_iSize) 105 | return false; 106 | 107 | void *pTempBuffer = (void *)((intptr_t)m_pMessage + m_iCurrentOffset); 108 | m_iCurrentOffset += iLength; 109 | memcpy(pOut, pTempBuffer, iLength); 110 | 111 | return true; 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /extension/extension.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * SteamTools - Exposes some SteamClient functions to SourceMod plugins. 4 | * Copyright (C) 2010 Asher Baker (asherkin). All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | * As a special exception, AlliedModders LLC gives you permission to link the 20 | * code of this program (as well as its derivative works) to "Half-Life 2," the 21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 22 | * by the Valve Corporation. You must obey the GNU General Public License in 23 | * all respects for all other code used. Additionally, AlliedModders LLC grants 24 | * this exception to all derivative works. AlliedModders LLC defines further 25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 26 | * or . 27 | * ============================================================================= 28 | */ 29 | 30 | /** 31 | * ============================================================================= 32 | * Attributions & Thanks: 33 | * ============================================================================= 34 | * AzuiSleet - Wrote the original example code to acquire the SteamClient 35 | * factory, information about GameServer auth tickets. 36 | * VoiDeD & AzuiSleet - The OpenSteamworks project. 37 | * Didrole - Linux autoloading. 38 | * ============================================================================= 39 | */ 40 | 41 | #ifdef WIN32 42 | #ifdef _MSC_VER 43 | #define atoui64(str) _strtoui64(str, 0, 10) 44 | #else 45 | #define atoui64(str) strtoul(str, 0, 10) 46 | #endif 47 | #else 48 | #define atoui64(str) strtoull(str, 0, 10) 49 | #endif 50 | 51 | #include "extension.h" 52 | 53 | #include "CDetour/detours.h" 54 | 55 | #include "filesystem.h" 56 | #include "tickets.h" 57 | #include "utlmap.h" 58 | 59 | /** 60 | * @file extension.cpp 61 | * @brief SteamTools extension code. 62 | */ 63 | 64 | SteamTools g_SteamTools; 65 | SMEXT_LINK(&g_SteamTools); 66 | 67 | SH_DECL_HOOK0_void(IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0); 68 | SH_DECL_HOOK0_void(IServerGameDLL, GameServerSteamAPIShutdown, SH_NOATTRIB, 0); 69 | 70 | SH_DECL_HOOK0(ISteamGameServer, WasRestartRequested, SH_NOATTRIB, 0, bool); 71 | 72 | SH_DECL_HOOK3(ISteamGameServer, BeginAuthSession, SH_NOATTRIB, 0, EBeginAuthSessionResult, const void *, int, CSteamID); 73 | SH_DECL_HOOK1_void(ISteamGameServer, EndAuthSession, SH_NOATTRIB, 0, CSteamID); 74 | 75 | ConVar SteamToolsVersion("steamtools_version", SMEXT_CONF_VERSION, FCVAR_NOTIFY|FCVAR_REPLICATED, SMEXT_CONF_DESCRIPTION); 76 | 77 | CDetour *g_pGetCallbackDetour; 78 | 79 | IServerGameDLL *g_pServerGameDLL = NULL; 80 | ICvar *g_pLocalCVar = NULL; 81 | IFileSystem *g_pFullFileSystem = NULL; 82 | 83 | ISteamGameServer *g_pSteamGameServer = NULL; 84 | ISteamUtils *g_pSteamUtils = NULL; 85 | ISteamGameServerStats *g_pSteamGameServerStats = NULL; 86 | ISteamHTTP *g_pSteamHTTP = NULL; 87 | 88 | CSteamID g_CustomSteamID = k_steamIDNil; 89 | CUtlVector g_RequestUserStatsSteamAPICalls; 90 | CUtlVector g_HTTPRequestSteamAPICalls; 91 | 92 | struct HTTPRequestCompletedContextFunction { 93 | IPluginContext *pContext; 94 | funcid_t uPluginFunction; 95 | bool bHasContext; 96 | }; 97 | 98 | union HTTPRequestCompletedContextPack { 99 | uint64 ulContextValue; 100 | struct { 101 | HTTPRequestCompletedContextFunction *pCallbackFunction; 102 | cell_t iPluginContextValue; 103 | }; 104 | }; 105 | 106 | bool MapLessFunc(const uint32 &in1, const uint32 &in2) 107 | { 108 | return (in1 < in2); 109 | }; 110 | 111 | typedef CUtlMap > SubIDMap; 112 | SubIDMap g_subIDs(MapLessFunc); 113 | 114 | #if 0 // Need to rework the API before exposing this. 115 | typedef CUtlVector SubIDVector; 116 | 117 | struct DLCInfo { 118 | uint32 uAppID; 119 | SubIDVector SubIDs; 120 | }; 121 | 122 | typedef CUtlMap > DLCMap; 123 | #else 124 | typedef CUtlMap > DLCMap; 125 | #endif 126 | DLCMap g_DLCs(MapLessFunc); 127 | 128 | typedef HSteamPipe (*GetPipeFn)(); 129 | typedef HSteamUser (*GetUserFn)(); 130 | 131 | typedef bool (*GetCallbackFn)(HSteamPipe hSteamPipe, CallbackMsg_t *pCallbackMsg); 132 | typedef void (*FreeLastCallbackFn)(HSteamPipe hSteamPipe); 133 | 134 | GetPipeFn g_GameServerSteamPipe; 135 | GetUserFn g_GameServerSteamUser; 136 | 137 | GetCallbackFn GetCallback; 138 | FreeLastCallbackFn FreeLastCallback; 139 | 140 | int g_GameServerSteamAPIActivatedHookID = 0; 141 | int g_GameServerSteamAPIShutdownHookID = 0; 142 | 143 | int g_WasRestartRequestedHookID = 0; 144 | 145 | int g_BeginAuthSessionHookID = 0; 146 | int g_EndAuthSessionHookID = 0; 147 | 148 | bool g_SteamServersConnected = false; 149 | bool g_SteamLoadFailed = false; 150 | 151 | IForward *g_pForwardGroupStatusResult = NULL; 152 | IForward *g_pForwardReputation = NULL; 153 | IForward *g_pForwardRestartRequested = NULL; 154 | 155 | IForward *g_pForwardSteamServersConnected = NULL; 156 | IForward *g_pForwardSteamServersDisconnected = NULL; 157 | 158 | IForward *g_pForwardClientReceivedStats = NULL; 159 | IForward *g_pForwardClientUnloadedStats = NULL; 160 | 161 | IForward *g_pForwardLoaded = NULL; 162 | IForward *g_pForwardShutdown = NULL; 163 | 164 | extern "C" void SteamAPIWarningMessageHook(int hpipe, const char *message) 165 | { 166 | g_pSM->LogError(myself, "SteamAPIWarning: %s", message); 167 | } 168 | 169 | void Hook_GameServerSteamAPIActivated(void) 170 | { 171 | #if defined _WIN32 172 | CSysModule *pModSteamApi = g_pFullFileSystem->LoadModule("../bin/steam_api.dll", "MOD", false); 173 | #elif defined _LINUX 174 | CSysModule *pModSteamApi = g_pFullFileSystem->LoadModule("../bin/libsteam_api.so", "MOD", false); 175 | #endif 176 | 177 | if ( !pModSteamApi ) 178 | { 179 | g_pSM->LogError(myself, "Unable to get steam_api handle."); 180 | return; 181 | } 182 | 183 | HMODULE steam_api_library = reinterpret_cast(pModSteamApi); 184 | 185 | g_GameServerSteamPipe = (GetPipeFn)GetProcAddress(steam_api_library, "SteamGameServer_GetHSteamPipe"); 186 | g_GameServerSteamUser = (GetUserFn)GetProcAddress(steam_api_library, "SteamGameServer_GetHSteamUser"); 187 | 188 | ISteamClient *client = NULL; 189 | 190 | if (!LoadSteamclient(&client)) 191 | return; 192 | 193 | g_pSteamGameServer = (ISteamGameServer *)client->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamPipe(), STEAMGAMESERVER_INTERFACE_VERSION); 194 | g_pSteamUtils = (ISteamUtils *)client->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamPipe(), STEAMUTILS_INTERFACE_VERSION); 195 | g_pSteamGameServerStats = (ISteamGameServerStats *)client->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamUser(), STEAMGAMESERVERSTATS_INTERFACE_VERSION); 196 | g_pSteamHTTP = (ISteamHTTP *)client->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamPipe(), STEAMHTTP_INTERFACE_VERSION); 197 | 198 | if (!CheckInterfaces()) 199 | return; 200 | 201 | g_WasRestartRequestedHookID = SH_ADD_HOOK(ISteamGameServer, WasRestartRequested, g_pSteamGameServer, SH_STATIC(Hook_WasRestartRequested), false); 202 | 203 | g_BeginAuthSessionHookID = SH_ADD_HOOK(ISteamGameServer, BeginAuthSession, g_pSteamGameServer, SH_STATIC(Hook_BeginAuthSession), true); 204 | g_EndAuthSessionHookID = SH_ADD_HOOK(ISteamGameServer, EndAuthSession, g_pSteamGameServer, SH_STATIC(Hook_EndAuthSession), true); 205 | 206 | g_pSteamUtils->SetWarningMessageHook(SteamAPIWarningMessageHook); 207 | 208 | g_SMAPI->ConPrintf("[STEAMTOOLS] Loading complete.\n"); 209 | 210 | g_SteamServersConnected = g_pSteamGameServer->BLoggedOn(); 211 | 212 | g_pForwardLoaded->Execute(NULL); 213 | 214 | if (g_SteamServersConnected) 215 | { 216 | g_pForwardSteamServersConnected->Execute(NULL); 217 | } else { 218 | g_pForwardSteamServersDisconnected->Execute(NULL); 219 | } 220 | 221 | if (g_GameServerSteamAPIActivatedHookID != 0) 222 | { 223 | SH_REMOVE_HOOK_ID(g_GameServerSteamAPIActivatedHookID); 224 | g_GameServerSteamAPIActivatedHookID = 0; 225 | } 226 | g_GameServerSteamAPIShutdownHookID = SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIShutdown, g_pServerGameDLL, SH_STATIC(Hook_GameServerSteamAPIShutdown), true); 227 | } 228 | 229 | void Hook_GameServerSteamAPIShutdown(void) 230 | { 231 | g_GameServerSteamPipe = NULL; 232 | g_GameServerSteamUser = NULL; 233 | 234 | g_pSteamGameServer = NULL; 235 | g_pSteamUtils = NULL; 236 | g_pSteamGameServerStats = NULL; 237 | g_pSteamHTTP = NULL; 238 | 239 | if (g_WasRestartRequestedHookID != 0) 240 | { 241 | SH_REMOVE_HOOK_ID(g_WasRestartRequestedHookID); 242 | g_WasRestartRequestedHookID = 0; 243 | } 244 | if (g_BeginAuthSessionHookID != 0) 245 | { 246 | SH_REMOVE_HOOK_ID(g_BeginAuthSessionHookID); 247 | g_BeginAuthSessionHookID = 0; 248 | } 249 | if (g_EndAuthSessionHookID != 0) 250 | { 251 | SH_REMOVE_HOOK_ID(g_EndAuthSessionHookID); 252 | g_EndAuthSessionHookID = 0; 253 | } 254 | 255 | g_SteamServersConnected = false; 256 | 257 | g_pForwardShutdown->Execute(NULL); 258 | 259 | if (g_GameServerSteamAPIShutdownHookID != 0) 260 | { 261 | SH_REMOVE_HOOK_ID(g_GameServerSteamAPIShutdownHookID); 262 | g_GameServerSteamAPIShutdownHookID = 0; 263 | } 264 | g_GameServerSteamAPIActivatedHookID = SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pServerGameDLL, SH_STATIC(Hook_GameServerSteamAPIActivated), true); 265 | } 266 | 267 | // This is O(n), but it's safe. 268 | // Switch over to a CUtlMap in the future. 269 | IPlugin *FindPluginByContext(IPluginContext *pContext) { 270 | IPlugin *pFoundPlugin = NULL; 271 | 272 | IPluginIterator *pPluginIterator = plsys->GetPluginIterator(); 273 | while (pPluginIterator->MorePlugins()) 274 | { 275 | IPlugin *pPlugin = pPluginIterator->GetPlugin(); 276 | 277 | if (pPlugin->GetBaseContext() == pContext) 278 | { 279 | pFoundPlugin = pPlugin; 280 | break; 281 | } 282 | 283 | pPluginIterator->NextPlugin(); 284 | } 285 | pPluginIterator->Release(); 286 | 287 | return pFoundPlugin; 288 | } 289 | 290 | DETOUR_DECL_STATIC2(DetourGetCallback, bool, HSteamPipe, hSteamPipe, CallbackMsg_t *, pCallbackMsg) 291 | { 292 | bool ret = DETOUR_STATIC_CALL(DetourGetCallback)(hSteamPipe, pCallbackMsg); 293 | if (!ret) { 294 | return false; 295 | } 296 | 297 | //g_SMAPI->ConPrintf("[STEAMTOOLS] Callback %d ...\n", pCallbackMsg->m_iCallback); 298 | 299 | switch (pCallbackMsg->m_iCallback) 300 | { 301 | case GSClientGroupStatus_t::k_iCallback: 302 | { 303 | GSClientGroupStatus_t *GroupStatus = (GSClientGroupStatus_t *)pCallbackMsg->m_pubParam; 304 | 305 | int i; 306 | for (i = 1; i <= playerhelpers->GetMaxClients(); ++i) 307 | { 308 | IGamePlayer *player = playerhelpers->GetGamePlayer(i); 309 | if (!player) 310 | continue; 311 | 312 | if (player->IsFakeClient()) 313 | continue; 314 | 315 | if (!player->IsAuthorized()) 316 | continue; 317 | 318 | edict_t *playerEdict = player->GetEdict(); 319 | if (!playerEdict || playerEdict->IsFree()) 320 | continue; 321 | 322 | if (*engine->GetClientSteamID(playerEdict) == GroupStatus->m_SteamIDUser) 323 | break; 324 | } 325 | 326 | if (i > playerhelpers->GetMaxClients()) 327 | { 328 | i = -1; 329 | g_CustomSteamID = GroupStatus->m_SteamIDUser; 330 | } 331 | 332 | g_pForwardGroupStatusResult->PushCell(i); 333 | g_pForwardGroupStatusResult->PushCell(GroupStatus->m_SteamIDGroup.GetAccountID()); 334 | g_pForwardGroupStatusResult->PushCell(GroupStatus->m_bMember); 335 | g_pForwardGroupStatusResult->PushCell(GroupStatus->m_bOfficer); 336 | g_pForwardGroupStatusResult->Execute(NULL); 337 | 338 | g_CustomSteamID = k_steamIDNil; 339 | 340 | FreeLastCallback(g_GameServerSteamPipe()); 341 | return false; 342 | } 343 | case SteamServersConnected_t::k_iCallback: 344 | { 345 | if (!g_SteamServersConnected) 346 | { 347 | g_pForwardSteamServersConnected->Execute(NULL); 348 | g_SteamServersConnected = true; 349 | } 350 | break; 351 | } 352 | case SteamServersDisconnected_t::k_iCallback: 353 | { 354 | if (g_SteamServersConnected) 355 | { 356 | g_pForwardSteamServersDisconnected->Execute(NULL); 357 | g_SteamServersConnected = false; 358 | } 359 | break; 360 | } 361 | case GSStatsReceived_t::k_iCallback: 362 | { 363 | // The handler above dealt with this anyway, stop this getting to the engine. 364 | FreeLastCallback(g_GameServerSteamPipe()); 365 | return false; 366 | } 367 | case GSStatsUnloaded_t::k_iCallback: 368 | { 369 | GSStatsUnloaded_t *StatsUnloaded = (GSStatsUnloaded_t *)pCallbackMsg->m_pubParam; 370 | 371 | int i; 372 | for (i = 1; i <= playerhelpers->GetMaxClients(); ++i) 373 | { 374 | IGamePlayer *player = playerhelpers->GetGamePlayer(i); 375 | if (!player) 376 | continue; 377 | 378 | if (player->IsFakeClient()) 379 | continue; 380 | 381 | if (!player->IsAuthorized()) 382 | continue; 383 | 384 | edict_t *playerEdict = player->GetEdict(); 385 | if (!playerEdict || playerEdict->IsFree()) 386 | continue; 387 | 388 | if (*engine->GetClientSteamID(playerEdict) == StatsUnloaded->m_steamIDUser) 389 | break; 390 | } 391 | 392 | if (i > playerhelpers->GetMaxClients()) 393 | { 394 | i = -1; 395 | g_CustomSteamID = StatsUnloaded->m_steamIDUser; 396 | } 397 | 398 | g_pForwardClientUnloadedStats->PushCell(i); 399 | g_pForwardClientUnloadedStats->Execute(NULL); 400 | 401 | g_CustomSteamID = k_steamIDNil; 402 | 403 | FreeLastCallback(g_GameServerSteamPipe()); 404 | return false; 405 | } 406 | case SteamAPICallCompleted_t::k_iCallback: 407 | { 408 | if (!g_pSteamUtils) { 409 | break; 410 | } 411 | 412 | SteamAPICallCompleted_t *APICallCompleted = (SteamAPICallCompleted_t *)pCallbackMsg->m_pubParam; 413 | SteamAPICall_t hSteamAPICall = APICallCompleted->m_hAsyncCall; 414 | 415 | if (g_RequestUserStatsSteamAPICalls.FindAndRemove(hSteamAPICall)) { 416 | do { 417 | bool bFailed = false; 418 | bool bComplete = g_pSteamUtils->IsAPICallCompleted(hSteamAPICall, &bFailed); 419 | //META_CONPRINTF("[STEAMTOOLS] (Stats) %llu: Completed: %s (Failed: %s)\n", hSteamAPICall, bComplete?"true":"false", bFailed?"true":"false"); 420 | 421 | if (!bComplete) { 422 | g_pSM->LogError(myself, "Stats request was not complete!"); 423 | g_RequestUserStatsSteamAPICalls.AddToTail(hSteamAPICall); 424 | break; 425 | } 426 | 427 | GSStatsReceived_t GSStatsReceived; 428 | g_pSteamUtils->GetAPICallResult(hSteamAPICall, &GSStatsReceived, sizeof(GSStatsReceived), GSStatsReceived.k_iCallback, &bFailed); 429 | 430 | if (bFailed) 431 | { 432 | ESteamAPICallFailure failureReason = g_pSteamUtils->GetAPICallFailureReason(hSteamAPICall); 433 | g_pSM->LogError(myself, "Getting stats failed. (ESteamAPICallFailure = %d)", failureReason); 434 | break; 435 | } 436 | 437 | if (GSStatsReceived.m_eResult != k_EResultOK) 438 | { 439 | if (GSStatsReceived.m_eResult == k_EResultFail) 440 | g_pSM->LogError(myself, "Getting stats for user %s failed, backend reported that the user has no stats.", GSStatsReceived.m_steamIDUser.Render()); 441 | else 442 | g_pSM->LogError(myself, "Stats for user %s received with an unexpected eResult. (eResult = %d)", GSStatsReceived.m_steamIDUser.Render(), GSStatsReceived.m_eResult); 443 | break; 444 | } 445 | 446 | int x; 447 | for (x = 1; x <= playerhelpers->GetMaxClients(); ++x) 448 | { 449 | IGamePlayer *player = playerhelpers->GetGamePlayer(x); 450 | if (!player) 451 | continue; 452 | 453 | if (player->IsFakeClient()) 454 | continue; 455 | 456 | if (!player->IsAuthorized()) 457 | continue; 458 | 459 | edict_t *playerEdict = player->GetEdict(); 460 | if (!playerEdict || playerEdict->IsFree()) 461 | continue; 462 | 463 | if (*engine->GetClientSteamID(playerEdict) == GSStatsReceived.m_steamIDUser) 464 | break; 465 | } 466 | 467 | if (x > playerhelpers->GetMaxClients()) 468 | { 469 | x = -1; 470 | g_CustomSteamID = GSStatsReceived.m_steamIDUser; 471 | } 472 | 473 | g_pForwardClientReceivedStats->PushCell(x); 474 | g_pForwardClientReceivedStats->Execute(NULL); 475 | 476 | g_CustomSteamID = k_steamIDNil; 477 | } while (0); 478 | } else if (g_HTTPRequestSteamAPICalls.FindAndRemove(hSteamAPICall)) { 479 | do { 480 | bool bFailed = false; 481 | bool bComplete = g_pSteamUtils->IsAPICallCompleted(hSteamAPICall, &bFailed); 482 | //META_CONPRINTF("[STEAMTOOLS] (HTTP) %llu: Completed: %s (Failed: %s)\n", hSteamAPICall, bComplete?"true":"false", bFailed?"true":"false"); 483 | 484 | if (!bComplete) { 485 | g_pSM->LogError(myself, "HTTP request was not complete!"); 486 | g_HTTPRequestSteamAPICalls.AddToTail(hSteamAPICall); 487 | break; 488 | } 489 | 490 | HTTPRequestCompleted_t HTTPRequestCompleted; 491 | g_pSteamUtils->GetAPICallResult(hSteamAPICall, &HTTPRequestCompleted, sizeof(HTTPRequestCompleted), HTTPRequestCompleted.k_iCallback, &bFailed); 492 | 493 | if (bFailed) 494 | { 495 | ESteamAPICallFailure failureReason = g_pSteamUtils->GetAPICallFailureReason(hSteamAPICall); 496 | g_pSM->LogError(myself, "HTTP request failed. (ESteamAPICallFailure = %d)", failureReason); 497 | break; 498 | } 499 | 500 | if (HTTPRequestCompleted.m_ulContextValue == 0) 501 | { 502 | g_pSM->LogError(myself, "Unable to find plugin in HTTPRequestCompleted handler. (No context value set)"); 503 | break; 504 | } 505 | 506 | HTTPRequestCompletedContextPack contextPack; 507 | contextPack.ulContextValue = HTTPRequestCompleted.m_ulContextValue; 508 | 509 | IPlugin *pPlugin = FindPluginByContext(contextPack.pCallbackFunction->pContext); 510 | 511 | if (!pPlugin) 512 | { 513 | g_pSM->LogError(myself, "Unable to find plugin in HTTPRequestCompleted handler. (No plugin found matching context)"); 514 | break; 515 | } 516 | 517 | IPluginFunction *pFunction = pPlugin->GetBaseContext()->GetFunctionById(contextPack.pCallbackFunction->uPluginFunction); 518 | 519 | if (!pFunction || !pFunction->IsRunnable()) 520 | { 521 | if (!pFunction) 522 | g_pSM->LogError(myself, "Unable to find plugin in HTTPRequestCompleted handler. (Function not found in plugin)"); 523 | 524 | break; 525 | } 526 | 527 | pFunction->PushCell(HTTPRequestCompleted.m_hRequest); 528 | pFunction->PushCell(HTTPRequestCompleted.m_bRequestSuccessful); 529 | pFunction->PushCell(HTTPRequestCompleted.m_eStatusCode); 530 | 531 | if (contextPack.pCallbackFunction->bHasContext) 532 | pFunction->PushCell(contextPack.iPluginContextValue); 533 | 534 | pFunction->Execute(NULL); 535 | 536 | delete contextPack.pCallbackFunction; 537 | } while (0); 538 | } else { 539 | break; 540 | } 541 | 542 | FreeLastCallback(g_GameServerSteamPipe()); 543 | return false; 544 | } 545 | } 546 | 547 | return true; 548 | } 549 | 550 | bool CheckInterfaces() 551 | { 552 | g_SteamLoadFailed = false; 553 | 554 | if (!g_pSteamGameServer) 555 | { 556 | g_pSM->LogError(myself, "Could not find interface %s", STEAMGAMESERVER_INTERFACE_VERSION); 557 | g_SteamLoadFailed = true; 558 | } 559 | 560 | if (!g_pSteamUtils) 561 | { 562 | g_pSM->LogError(myself, "Could not find interface %s", STEAMUTILS_INTERFACE_VERSION); 563 | g_SteamLoadFailed = true; 564 | } 565 | 566 | if (!g_pSteamGameServerStats) 567 | { 568 | g_pSM->LogError(myself, "Could not find interface %s", STEAMGAMESERVERSTATS_INTERFACE_VERSION); 569 | g_SteamLoadFailed = true; 570 | } 571 | 572 | if (!g_pSteamHTTP) 573 | { 574 | g_pSM->LogError(myself, "Could not find interface %s", STEAMHTTP_INTERFACE_VERSION); 575 | g_SteamLoadFailed = true; 576 | } 577 | 578 | if (g_SteamLoadFailed) 579 | { 580 | return false; 581 | } else { 582 | return true; 583 | } 584 | } 585 | 586 | bool LoadSteamclient(ISteamClient **pSteamClient, int method) 587 | { 588 | if(!g_GameServerSteamPipe || !g_GameServerSteamUser || !g_GameServerSteamPipe() || !g_GameServerSteamUser()) 589 | return false; 590 | 591 | HMODULE steamclient_library = NULL; 592 | ISteamClient *pLocalSteamClient = NULL; 593 | 594 | g_SMAPI->ConPrintf("[STEAMTOOLS] Trying method %d ...\n", (method + 1)); 595 | 596 | switch(method) 597 | { 598 | case 0: 599 | { 600 | #ifdef _LINUX 601 | #if defined _WIN32 602 | CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule("../bin/steamclient.dll", "MOD", false); 603 | #elif defined _LINUX 604 | CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule("../bin/steamclient.so", "MOD", false); 605 | #endif 606 | if (!pModSteamClient) 607 | { 608 | g_pSM->LogError(myself, "Unable to get steamclient handle."); 609 | break; 610 | } 611 | steamclient_library = reinterpret_cast(pModSteamClient); 612 | #else 613 | g_SMAPI->ConPrintf("[STEAMTOOLS] Method 1 disabled on Windows...\n", (method + 1)); 614 | #endif 615 | break; 616 | } 617 | #ifdef _WIN32 618 | case 1: 619 | { 620 | steamclient_library = GetModuleHandle("steamclient.dll"); 621 | break; 622 | } 623 | case 2: 624 | { 625 | HKEY hRegKey; 626 | char pchSteamDir[MAX_PATH]; 627 | if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, &hRegKey) != ERROR_SUCCESS) 628 | { 629 | g_pSM->LogError(myself, "Steam registry key not found."); 630 | break; 631 | } 632 | DWORD dwLength = sizeof(pchSteamDir); 633 | RegQueryValueExA(hRegKey, "InstallPath", NULL, NULL, (BYTE*)pchSteamDir, &dwLength); 634 | RegCloseKey(hRegKey); 635 | strcat(pchSteamDir, "/steamclient.dll"); 636 | CSysModule *pModSteamClient = g_pFullFileSystem->LoadModule(pchSteamDir, "MOD", false); 637 | if (!pModSteamClient) 638 | { 639 | g_pSM->LogError(myself, "Unable to get steamclient handle."); 640 | break; 641 | } 642 | steamclient_library = reinterpret_cast(pModSteamClient); 643 | break; 644 | } 645 | #endif //_WIN32 646 | default: 647 | { 648 | g_pSM->LogError(myself, "Ran out of methods to acquire SteamWorks interfaces."); 649 | return false; 650 | } 651 | } 652 | 653 | if (!steamclient_library) 654 | { 655 | return LoadSteamclient(pSteamClient, (method + 1)); 656 | } 657 | 658 | CreateInterfaceFn steamclient = (CreateInterfaceFn)GetProcAddress(steamclient_library, "CreateInterface"); 659 | 660 | pLocalSteamClient = (ISteamClient *)steamclient(STEAMCLIENT_INTERFACE_VERSION, NULL); 661 | 662 | ISteamGameServer *gameserver = (ISteamGameServer *)pLocalSteamClient->GetISteamGenericInterface(g_GameServerSteamUser(), g_GameServerSteamPipe(), STEAMGAMESERVER_INTERFACE_VERSION); 663 | 664 | if (!gameserver) 665 | { 666 | return LoadSteamclient(pSteamClient, (method + 1)); 667 | } 668 | 669 | g_SMAPI->ConPrintf("[STEAMTOOLS] Method %d worked!\n", (method + 1)); 670 | 671 | *pSteamClient = pLocalSteamClient; 672 | 673 | GetCallback = (GetCallbackFn)GetProcAddress(steamclient_library, "Steam_BGetCallback"); 674 | FreeLastCallback = (FreeLastCallbackFn)GetProcAddress(steamclient_library, "Steam_FreeLastCallback"); 675 | 676 | if (g_pGetCallbackDetour) { 677 | g_pGetCallbackDetour->DisableDetour(); 678 | } 679 | g_pGetCallbackDetour = DETOUR_CREATE_STATIC(DetourGetCallback, GetCallback); 680 | if (g_pGetCallbackDetour) { 681 | g_SMAPI->ConPrintf("[STEAMTOOLS] Detoured Steam_BGetCallback!\n"); 682 | g_pGetCallbackDetour->EnableDetour(); 683 | } else { 684 | g_SMAPI->ConPrintf("[STEAMTOOLS] FAILED to detour Steam_BGetCallback!\n"); 685 | } 686 | 687 | return true; 688 | } 689 | 690 | bool SteamTools::SDK_OnLoad(char *error, size_t maxlen, bool late) 691 | { 692 | CDetourManager::Init(smutils->GetScriptingEngine(), NULL); 693 | 694 | g_GameServerSteamAPIActivatedHookID = SH_ADD_HOOK(IServerGameDLL, GameServerSteamAPIActivated, g_pServerGameDLL, SH_STATIC(Hook_GameServerSteamAPIActivated), true); 695 | 696 | g_pShareSys->AddNatives(myself, g_ExtensionNatives); 697 | g_pShareSys->RegisterLibrary(myself, "SteamTools"); 698 | 699 | plsys->AddPluginsListener(this); 700 | 701 | g_pForwardGroupStatusResult = g_pForwards->CreateForward("Steam_GroupStatusResult", ET_Ignore, 4, NULL, Param_Cell, Param_Cell, Param_Cell, Param_Cell); 702 | g_pForwardReputation = g_pForwards->CreateForward("Steam_Reputation", ET_Ignore, 6, NULL, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_Cell); 703 | g_pForwardRestartRequested = g_pForwards->CreateForward("Steam_RestartRequested", ET_Ignore, 0, NULL); 704 | 705 | g_pForwardSteamServersConnected = g_pForwards->CreateForward("Steam_SteamServersConnected", ET_Ignore, 0, NULL); 706 | g_pForwardSteamServersDisconnected = g_pForwards->CreateForward("Steam_SteamServersDisconnected", ET_Ignore, 0, NULL); 707 | 708 | g_pForwardClientReceivedStats = g_pForwards->CreateForward("Steam_StatsReceived", ET_Ignore, 1, NULL, Param_Cell); 709 | g_pForwardClientUnloadedStats = g_pForwards->CreateForward("Steam_StatsUnloaded", ET_Ignore, 1, NULL, Param_Cell); 710 | 711 | g_pForwardLoaded = g_pForwards->CreateForward("Steam_FullyLoaded", ET_Ignore, 0, NULL); 712 | g_pForwardShutdown = g_pForwards->CreateForward("Steam_Shutdown", ET_Ignore, 0, NULL); 713 | 714 | g_SMAPI->ConPrintf("[STEAMTOOLS] Initial loading stage complete...\n"); 715 | 716 | //If this was a late load, we should try now since we wont get this until a reinit if already loaded... 717 | if (late) 718 | { 719 | Hook_GameServerSteamAPIActivated(); 720 | 721 | if (g_SteamLoadFailed) // Hook_GameServerSteamAPIActivated() will have called CheckInterfaces() already. 722 | { 723 | snprintf(error, maxlen, "One or more SteamWorks interfaces failed to be acquired."); 724 | return false; 725 | } 726 | } 727 | 728 | return true; 729 | } 730 | 731 | void Hook_EndAuthSession(CSteamID steamID) 732 | { 733 | if (steamID.BIndividualAccount() && steamID.GetUnAccountInstance() == 1) 734 | { 735 | g_subIDs.Remove(steamID.GetAccountID()); 736 | g_DLCs.Remove(steamID.GetAccountID()); 737 | } 738 | 739 | RETURN_META(MRES_IGNORED); 740 | } 741 | 742 | void SteamTools::OnPluginLoaded(IPlugin *plugin) 743 | { 744 | if (!g_pSteamGameServer) 745 | return; 746 | 747 | cell_t result; 748 | 749 | IPluginContext *pluginContext = plugin->GetRuntime()->GetDefaultContext(); 750 | 751 | IPluginFunction *steamToolsLoadedCallback = pluginContext->GetFunctionByName("Steam_FullyLoaded"); 752 | 753 | if (steamToolsLoadedCallback) 754 | { 755 | steamToolsLoadedCallback->CallFunction(NULL, 0, &result); 756 | } else { 757 | // This plugin doesn't use SteamTools 758 | return; 759 | } 760 | 761 | IPluginFunction *steamConnectionStateCallback = NULL; 762 | if (g_SteamServersConnected) 763 | { 764 | steamConnectionStateCallback = pluginContext->GetFunctionByName("Steam_SteamServersConnected"); 765 | } else { 766 | steamConnectionStateCallback = pluginContext->GetFunctionByName("Steam_SteamServersDisconnected"); 767 | } 768 | 769 | if (steamConnectionStateCallback) 770 | { 771 | steamConnectionStateCallback->CallFunction(NULL, 0, &result); 772 | } 773 | } 774 | 775 | bool Hook_WasRestartRequested() 776 | { 777 | cell_t cellResults = 0; 778 | bool bWasRestartRequested = false; 779 | if ((bWasRestartRequested = SH_CALL(g_pSteamGameServer, &ISteamGameServer::WasRestartRequested)())) 780 | { 781 | g_pForwardRestartRequested->Execute(&cellResults); 782 | } 783 | RETURN_META_VALUE(MRES_SUPERCEDE, (cellResults < Pl_Handled)?bWasRestartRequested:false); 784 | } 785 | 786 | /* 787 | CON_COMMAND(st_ticket, "") 788 | { 789 | FileHandle_t ticketFile = g_pFullFileSystem->Open("ticket.bin", "rb", "MOD"); 790 | if (!ticketFile) 791 | { 792 | META_CONPRINT("Unable to open ticket.bin for reading\n"); 793 | } 794 | 795 | int ticketSize = g_pFullFileSystem->Size(ticketFile); 796 | 797 | void *ticketBuffer = malloc(ticketSize); 798 | if (!ticketBuffer) 799 | { 800 | META_CONPRINT("Unable to allocate memory to read ticket.bin\n"); 801 | 802 | free(ticketBuffer); 803 | return; 804 | } 805 | 806 | if (!g_pFullFileSystem->Read(ticketBuffer, ticketSize, ticketFile)) 807 | { 808 | META_CONPRINT("Unable to read ticket.bin\n"); 809 | 810 | free(ticketBuffer); 811 | return; 812 | } 813 | 814 | g_pFullFileSystem->Close(ticketFile); 815 | 816 | bool error = false; 817 | AuthBlob_t authblob(ticketBuffer, ticketSize, &error); 818 | 819 | if (error) // An error was encountered trying to parse the ticket. 820 | { 821 | CBlob authBlob(ticketBuffer, ticketSize); 822 | uint8 revVersion; 823 | if (authBlob.Read(&revVersion) && revVersion == 83) 824 | { 825 | META_CONPRINT("Error detected parsing ticket. (RevEmu)\n"); 826 | } else { 827 | META_CONPRINT("Error detected parsing ticket. (unknown)\n"); 828 | } 829 | 830 | free(ticketBuffer); 831 | return; 832 | } 833 | 834 | META_CONPRINT("No error detected while parsing ticket.\n"); 835 | 836 | free(ticketBuffer); 837 | } 838 | */ 839 | 840 | //ConVar ParseBadTickets("steamtools_parse_bad_tickets", "1", FCVAR_NONE, "", true, 0.0, true, 1.0); 841 | //ConVar DumpBadTickets("steamtools_dump_unknown_tickets", "1", FCVAR_NONE, "", true, 0.0, true, 1.0); 842 | ConVar DumpTickets("steamtools_dump_tickets", "0", FCVAR_NONE, "", true, 0.0, true, 1.0); 843 | 844 | EBeginAuthSessionResult Hook_BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID) 845 | { 846 | EBeginAuthSessionResult ret = META_RESULT_ORIG_RET(EBeginAuthSessionResult); 847 | 848 | bool bInvalidTicket = (ret == k_EBeginAuthSessionResultInvalidTicket); 849 | if (bInvalidTicket || DumpTickets.GetBool()) 850 | { 851 | if (bInvalidTicket) 852 | g_pSM->LogMessage(myself, "Dumping Steam ticket as it's invalid..."); 853 | 854 | char fileName[64]; 855 | g_pSM->Format(fileName, 64, "ticket_%u_%u_%u.bin", steamID.GetAccountID(), cbAuthTicket, time(NULL)); 856 | 857 | FileHandle_t ticketFile = g_pFullFileSystem->Open(fileName, "wb", "MOD"); 858 | if (!ticketFile) 859 | { 860 | g_pSM->LogError(myself, "Unable to open %s for writing.", fileName); 861 | } else { 862 | g_pFullFileSystem->Write(pAuthTicket, cbAuthTicket, ticketFile); 863 | g_pFullFileSystem->Close(ticketFile); 864 | 865 | g_pSM->LogMessage(myself, "Wrote ticket to %s", fileName); 866 | } 867 | 868 | if (bInvalidTicket) // Bail out. 869 | RETURN_META_VALUE(MRES_IGNORED, (EBeginAuthSessionResult)NULL); 870 | } 871 | 872 | bool error = false; 873 | AuthBlob_t authblob(pAuthTicket, cbAuthTicket, &error); 874 | 875 | if (error) // An error was encountered trying to parse the ticket. 876 | { 877 | g_pSM->LogError(myself, "Failed to parse ticket from %s, subscription and DLC info will not be available.", steamID.Render()); 878 | RETURN_META_VALUE(MRES_IGNORED, (EBeginAuthSessionResult)NULL); 879 | } 880 | 881 | if (authblob.ownership == NULL || authblob.ownership->ticket == NULL) 882 | { 883 | g_pSM->LogError(myself, "Missing sections in ticket from %s, subscription and DLC info will not be available.", steamID.Render()); 884 | RETURN_META_VALUE(MRES_IGNORED, (EBeginAuthSessionResult)NULL); 885 | } 886 | 887 | SubIDMap::IndexType_t subIndex = g_subIDs.Insert(steamID.GetAccountID()); 888 | g_subIDs.Element(subIndex).CopyArray(authblob.ownership->ticket->licenses, authblob.ownership->ticket->numlicenses); 889 | 890 | DLCMap::IndexType_t DLCIndex = g_DLCs.Insert(steamID.GetAccountID()); 891 | g_DLCs.Element(DLCIndex).CopyArray(authblob.ownership->ticket->dlcs, authblob.ownership->ticket->numdlcs); 892 | 893 | RETURN_META_VALUE(MRES_IGNORED, (EBeginAuthSessionResult)NULL); 894 | } 895 | 896 | /* 897 | bool Hook_SendUserConnectAndAuthenticate(uint32 unIPClient, const void *pvAuthBlob, uint32 cubAuthBlobSize, CSteamID *pSteamIDUser) 898 | { 899 | bool ret = META_RESULT_ORIG_RET(bool); 900 | 901 | if (!ret && !ParseBadTickets.GetBool()) 902 | { 903 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u was denied by Steam, but SteamTools has been configured not to gather additional info.", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF); 904 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 905 | } 906 | 907 | bool error = false; 908 | AuthBlob_t authblob(pvAuthBlob, cubAuthBlobSize, &error); 909 | 910 | if (error) // An error was encountered trying to parse the ticket. 911 | { 912 | CBlob authBlob(pvAuthBlob, cubAuthBlobSize); 913 | uint8 revVersion; 914 | if (authBlob.Read(&revVersion) && revVersion == 83) 915 | { 916 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u sent a non-steam auth blob. (RevEmu ticket detected)", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF); 917 | } else { 918 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u sent a non-steam auth blob.", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF); 919 | 920 | if (DumpBadTickets.GetBool()) 921 | { 922 | char fileName[64]; 923 | g_pSM->Format(fileName, 64, "ticket_%u_%u_%u_%u_%u_%u.bin", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF, cubAuthBlobSize, time(NULL)); 924 | 925 | FileHandle_t ticketFile = g_pFullFileSystem->Open(fileName, "wb", "MOD"); 926 | if (!ticketFile) 927 | { 928 | g_pSM->LogError(myself, "Unable to open %s for writing.", fileName); 929 | } else { 930 | g_pFullFileSystem->Write(pvAuthBlob, cubAuthBlobSize, ticketFile); 931 | 932 | g_pFullFileSystem->Close(ticketFile); 933 | 934 | g_pSM->LogMessage(myself, "Wrote unknown ticket to %s, please send this file to asherkin@gmail.com", fileName); 935 | } 936 | } 937 | } 938 | 939 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 940 | } 941 | 942 | if (!ret) 943 | { 944 | if (!authblob.ownership) 945 | { 946 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u (%s) isn't using Steam.", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF, (authblob.section)?(authblob.section->steamid.Render()):("NO STEAMID")); 947 | } else if (!authblob.section) { 948 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u (%s) is in offline mode.", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF, authblob.ownership->ticket->steamid.Render()); 949 | } else { 950 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u (%s) was denied by Steam for an unknown reason. (Maybe an expired or stolen ticket?).", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF, authblob.ownership->ticket->steamid.Render()); 951 | } 952 | 953 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 954 | } 955 | 956 | if (!authblob.section && authblob.ownership) 957 | { 958 | g_pSM->LogMessage(myself, "Client connecting from %u.%u.%u.%u (%s) is in offline mode but their ticket hasn't expired yet.", (unIPClient) & 0xFF, (unIPClient >> 8) & 0xFF, (unIPClient >> 16) & 0xFF, (unIPClient >> 24) & 0xFF, authblob.ownership->ticket->steamid.Render()); 959 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 960 | } else if (!authblob.section || !authblob.ownership) { 961 | g_pSM->LogError(myself, "SendUserConnectAndAuthenticate: Aborting due to missing sections in ticket. (authblob.length = %u)", authblob.length); 962 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 963 | } 964 | 965 | if (authblob.ownership->ticket->version != 4) 966 | { 967 | g_pSM->LogError(myself, "SendUserConnectAndAuthenticate: Aborting due to unexpected ticket version. (ticketVersion = %u)", authblob.ownership->ticket->version); 968 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 969 | } 970 | 971 | SubIDMap::IndexType_t index = g_subIDs.Insert(pSteamIDUser->GetAccountID()); 972 | g_subIDs.Element(index).CopyArray(authblob.ownership->ticket->licenses, authblob.ownership->ticket->numlicenses); 973 | 974 | RETURN_META_VALUE(MRES_IGNORED, (bool)NULL); 975 | } 976 | */ 977 | 978 | bool SteamTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) 979 | { 980 | GET_V_IFACE_CURRENT(GetServerFactory, g_pServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); 981 | GET_V_IFACE_CURRENT(GetEngineFactory, g_pLocalCVar, ICvar, CVAR_INTERFACE_VERSION); 982 | GET_V_IFACE_CURRENT(GetFileSystemFactory, g_pFullFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION); 983 | 984 | g_pCVar = g_pLocalCVar; 985 | ConVar_Register(FCVAR_NONE, this); 986 | 987 | return true; 988 | } 989 | 990 | bool SteamTools::RegisterConCommandBase(ConCommandBase *pCommand) 991 | { 992 | META_REGCVAR(pCommand); 993 | return true; 994 | } 995 | 996 | void SteamTools::SDK_OnUnload() 997 | { 998 | plsys->RemovePluginsListener(this); 999 | 1000 | if (g_GameServerSteamAPIActivatedHookID != 0) 1001 | { 1002 | SH_REMOVE_HOOK_ID(g_GameServerSteamAPIActivatedHookID); 1003 | g_GameServerSteamAPIActivatedHookID = 0; 1004 | } 1005 | if (g_GameServerSteamAPIShutdownHookID != 0) 1006 | { 1007 | SH_REMOVE_HOOK_ID(g_GameServerSteamAPIShutdownHookID); 1008 | g_GameServerSteamAPIShutdownHookID = 0; 1009 | } 1010 | if (g_WasRestartRequestedHookID != 0) 1011 | { 1012 | SH_REMOVE_HOOK_ID(g_WasRestartRequestedHookID); 1013 | g_WasRestartRequestedHookID = 0; 1014 | } 1015 | if (g_BeginAuthSessionHookID != 0) 1016 | { 1017 | SH_REMOVE_HOOK_ID(g_BeginAuthSessionHookID); 1018 | g_BeginAuthSessionHookID = 0; 1019 | } 1020 | if (g_EndAuthSessionHookID != 0) 1021 | { 1022 | SH_REMOVE_HOOK_ID(g_EndAuthSessionHookID); 1023 | g_EndAuthSessionHookID = 0; 1024 | } 1025 | 1026 | g_pForwards->ReleaseForward(g_pForwardGroupStatusResult); 1027 | g_pForwards->ReleaseForward(g_pForwardReputation); 1028 | 1029 | g_pForwards->ReleaseForward(g_pForwardRestartRequested); 1030 | 1031 | g_pForwards->ReleaseForward(g_pForwardSteamServersConnected); 1032 | g_pForwards->ReleaseForward(g_pForwardSteamServersDisconnected); 1033 | } 1034 | 1035 | bool SteamTools::QueryRunning(char *error, size_t maxlen) 1036 | { 1037 | if (g_SteamLoadFailed) 1038 | { 1039 | snprintf(error, maxlen, "One or more SteamWorks interfaces failed to be acquired."); 1040 | return false; 1041 | } 1042 | return true; 1043 | } 1044 | 1045 | CSteamID atocsteamid(const char *pRenderedID) 1046 | { 1047 | // Convert the Steam2 ID string to a Steam2 ID structure 1048 | TSteamGlobalUserID steam2ID; 1049 | steam2ID.m_SteamInstanceID = 0; 1050 | steam2ID.m_SteamLocalUserID.Split.High32bits = 0; 1051 | steam2ID.m_SteamLocalUserID.Split.Low32bits = 0; 1052 | 1053 | const char *pchTSteam2ID = pRenderedID; 1054 | 1055 | const char *pchOptionalLeadString = "STEAM_"; 1056 | if (Q_strnicmp(pRenderedID, pchOptionalLeadString, Q_strlen(pchOptionalLeadString)) == 0) 1057 | pchTSteam2ID = pRenderedID + Q_strlen(pchOptionalLeadString); 1058 | 1059 | char cExtraCharCheck = 0; 1060 | 1061 | int cFieldConverted = sscanf(pchTSteam2ID, "%hu:%u:%u%c", &steam2ID.m_SteamInstanceID, &steam2ID.m_SteamLocalUserID.Split.High32bits, &steam2ID.m_SteamLocalUserID.Split.Low32bits, &cExtraCharCheck); 1062 | 1063 | // Validate the conversion ... a special case is steam2 instance ID 1 which is reserved for special DoD handling 1064 | if (cExtraCharCheck != 0 || cFieldConverted == EOF || cFieldConverted < 2 || (cFieldConverted < 3 && steam2ID.m_SteamInstanceID != 1)) 1065 | return k_steamIDNil; 1066 | 1067 | // Now convert to steam ID from the Steam2 ID structure 1068 | CSteamID steamID; 1069 | steamID.SetFromSteam2(&steam2ID, k_EUniversePublic); 1070 | return steamID; 1071 | } 1072 | 1073 | static cell_t RequestGroupStatus(IPluginContext *pContext, const cell_t *params) 1074 | { 1075 | if (!g_pSteamGameServer) 1076 | return 0; 1077 | 1078 | const CSteamID *pSteamID; 1079 | if(params[1] > -1) 1080 | { 1081 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1082 | } else { 1083 | if (g_CustomSteamID.IsValid()) 1084 | pSteamID = &g_CustomSteamID; 1085 | else 1086 | return pContext->ThrowNativeError("Custom SteamID not set."); 1087 | } 1088 | if (!pSteamID) 1089 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1090 | 1091 | return g_pSteamGameServer->RequestUserGroupStatus(*pSteamID, CSteamID(params[2], k_EUniversePublic, k_EAccountTypeClan)); 1092 | } 1093 | 1094 | static cell_t RequestGameplayStats(IPluginContext *pContext, const cell_t *params) 1095 | { 1096 | return pContext->ThrowNativeError("RequestGameplayStats function no longer operational."); 1097 | } 1098 | 1099 | static cell_t RequestServerReputation(IPluginContext *pContext, const cell_t *params) 1100 | { 1101 | return pContext->ThrowNativeError("RequestServerReputation function no longer operational."); 1102 | } 1103 | 1104 | static cell_t ForceHeartbeat(IPluginContext *pContext, const cell_t *params) 1105 | { 1106 | if (!g_pSteamGameServer) 1107 | return 0; 1108 | 1109 | g_pSteamGameServer->ForceHeartbeat(); 1110 | return 0; 1111 | } 1112 | 1113 | static cell_t IsVACEnabled(IPluginContext *pContext, const cell_t *params) 1114 | { 1115 | if (!g_pSteamGameServer) 1116 | return 0; 1117 | 1118 | return g_pSteamGameServer->BSecure(); 1119 | } 1120 | 1121 | static cell_t IsConnected(IPluginContext *pContext, const cell_t *params) 1122 | { 1123 | /* 1124 | if (!g_pSteamGameServer) 1125 | return 0; 1126 | 1127 | return g_pSteamGameServer->LoggedOn(); 1128 | */ 1129 | 1130 | return g_SteamServersConnected; 1131 | } 1132 | 1133 | static cell_t GetPublicIP(IPluginContext *pContext, const cell_t *params) 1134 | { 1135 | if (!g_pSteamGameServer) 1136 | return 0; 1137 | 1138 | uint32 ipAddress = g_pSteamGameServer->GetPublicIP(); 1139 | unsigned char octet[4] = {0,0,0,0}; 1140 | 1141 | for (int i=0; i<4; i++) 1142 | { 1143 | octet[i] = ( ipAddress >> (i*8) ) & 0xFF; 1144 | } 1145 | 1146 | cell_t *addr; 1147 | pContext->LocalToPhysAddr(params[1], &addr); 1148 | 1149 | addr[0] = octet[3]; 1150 | addr[1] = octet[2]; 1151 | addr[2] = octet[1]; 1152 | addr[3] = octet[0]; 1153 | 1154 | return 0; 1155 | } 1156 | 1157 | static cell_t SetKeyValue(IPluginContext *pContext, const cell_t *params) 1158 | { 1159 | if (!g_pSteamGameServer) 1160 | return 0; 1161 | 1162 | char *pKey; 1163 | pContext->LocalToString(params[1], &pKey); 1164 | char *pValue; 1165 | pContext->LocalToString(params[2], &pValue); 1166 | g_pSteamGameServer->SetKeyValue(pKey, pValue); 1167 | return 0; 1168 | } 1169 | 1170 | static cell_t ClearAllKeyValues(IPluginContext *pContext, const cell_t *params) 1171 | { 1172 | if (!g_pSteamGameServer) 1173 | return 0; 1174 | 1175 | g_pSteamGameServer->ClearAllKeyValues(); 1176 | return 0; 1177 | } 1178 | 1179 | static cell_t AddMasterServer(IPluginContext *pContext, const cell_t *params) 1180 | { 1181 | return pContext->ThrowNativeError("AddMasterServer function no longer operational."); 1182 | } 1183 | 1184 | static cell_t RemoveMasterServer(IPluginContext *pContext, const cell_t *params) 1185 | { 1186 | return pContext->ThrowNativeError("RemoveMasterServer function no longer operational."); 1187 | } 1188 | 1189 | static cell_t GetNumMasterServers(IPluginContext *pContext, const cell_t *params) 1190 | { 1191 | return pContext->ThrowNativeError("GetNumMasterServers function no longer operational."); 1192 | } 1193 | 1194 | static cell_t GetMasterServerAddress(IPluginContext *pContext, const cell_t *params) 1195 | { 1196 | return pContext->ThrowNativeError("GetMasterServerAddress function no longer operational."); 1197 | } 1198 | 1199 | static cell_t SetGameDescription(IPluginContext *pContext, const cell_t *params) 1200 | { 1201 | if (!g_pSteamGameServer) 1202 | return 0; 1203 | 1204 | char *strGameDesc; 1205 | pContext->LocalToString(params[1], &strGameDesc); 1206 | 1207 | g_pSteamGameServer->SetGameDescription(strGameDesc); 1208 | return 0; 1209 | } 1210 | 1211 | static cell_t RequestStats(IPluginContext *pContext, const cell_t *params) 1212 | { 1213 | if (!g_pSteamGameServerStats) 1214 | return 0; 1215 | 1216 | const CSteamID *pSteamID; 1217 | if(params[1] > -1) 1218 | { 1219 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1220 | } else { 1221 | if (g_CustomSteamID.IsValid()) 1222 | pSteamID = &g_CustomSteamID; 1223 | else 1224 | return pContext->ThrowNativeError("Custom SteamID not set."); 1225 | } 1226 | if (!pSteamID) 1227 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1228 | 1229 | g_RequestUserStatsSteamAPICalls.AddToTail(g_pSteamGameServerStats->RequestUserStats(*pSteamID)); 1230 | return 0; 1231 | } 1232 | 1233 | static cell_t GetStatInt(IPluginContext *pContext, const cell_t *params) 1234 | { 1235 | if (!g_pSteamGameServerStats) 1236 | return 0; 1237 | 1238 | const CSteamID *pSteamID; 1239 | if(params[1] > -1) 1240 | { 1241 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1242 | } else { 1243 | if (g_CustomSteamID.IsValid()) 1244 | pSteamID = &g_CustomSteamID; 1245 | else 1246 | return pContext->ThrowNativeError("Custom SteamID not set."); 1247 | } 1248 | if (!pSteamID) 1249 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1250 | 1251 | char *strStatName; 1252 | pContext->LocalToString(params[2], &strStatName); 1253 | 1254 | int32 data; 1255 | if (g_pSteamGameServerStats->GetUserStat(*pSteamID, strStatName, &data)) 1256 | { 1257 | return data; 1258 | } else { 1259 | return pContext->ThrowNativeError("Failed to get stat %s for client %d", strStatName, params[1]); 1260 | } 1261 | } 1262 | 1263 | static cell_t GetStatFloat(IPluginContext *pContext, const cell_t *params) 1264 | { 1265 | if (!g_pSteamGameServerStats) 1266 | return 0; 1267 | 1268 | const CSteamID *pSteamID; 1269 | if(params[1] > -1) 1270 | { 1271 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1272 | } else { 1273 | if (g_CustomSteamID.IsValid()) 1274 | pSteamID = &g_CustomSteamID; 1275 | else 1276 | return pContext->ThrowNativeError("Custom SteamID not set."); 1277 | } 1278 | if (!pSteamID) 1279 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1280 | 1281 | char *strStatName; 1282 | pContext->LocalToString(params[2], &strStatName); 1283 | 1284 | float data; 1285 | if (g_pSteamGameServerStats->GetUserStat(*pSteamID, strStatName, &data)) 1286 | { 1287 | return sp_ftoc(data); 1288 | } else { 1289 | return pContext->ThrowNativeError("Failed to get stat %s for client %d", strStatName, params[1]); 1290 | } 1291 | } 1292 | 1293 | static cell_t IsAchieved(IPluginContext *pContext, const cell_t *params) 1294 | { 1295 | if (!g_pSteamGameServerStats) 1296 | return 0; 1297 | 1298 | const CSteamID *pSteamID; 1299 | if(params[1] > -1) 1300 | { 1301 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1302 | } else { 1303 | if (g_CustomSteamID.IsValid()) 1304 | pSteamID = &g_CustomSteamID; 1305 | else 1306 | return pContext->ThrowNativeError("Custom SteamID not set."); 1307 | } 1308 | if (!pSteamID) 1309 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1310 | 1311 | char *strAchName; 1312 | pContext->LocalToString(params[2], &strAchName); 1313 | 1314 | bool bAchieved; 1315 | if (g_pSteamGameServerStats->GetUserAchievement(*pSteamID, strAchName, &bAchieved)) 1316 | { 1317 | return bAchieved; 1318 | } else { 1319 | return pContext->ThrowNativeError("Failed to get achievement %s for client %d", strAchName, params[1]); 1320 | } 1321 | } 1322 | 1323 | static cell_t GetNumClientSubscriptions(IPluginContext *pContext, const cell_t *params) 1324 | { 1325 | const CSteamID *pSteamID; 1326 | if(params[1] > -1) 1327 | { 1328 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1329 | } else { 1330 | if (g_CustomSteamID.IsValid()) 1331 | pSteamID = &g_CustomSteamID; 1332 | else 1333 | return pContext->ThrowNativeError("Custom SteamID not set."); 1334 | } 1335 | if (!pSteamID) 1336 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1337 | 1338 | SubIDMap::IndexType_t index = g_subIDs.Find(pSteamID->GetAccountID()); 1339 | if (!g_subIDs.IsValidIndex(index)) 1340 | return 0; 1341 | 1342 | return g_subIDs.Element(index).Count(); 1343 | } 1344 | 1345 | static cell_t GetClientSubscription(IPluginContext *pContext, const cell_t *params) 1346 | { 1347 | const CSteamID *pSteamID; 1348 | if(params[1] > -1) 1349 | { 1350 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1351 | } else { 1352 | if (g_CustomSteamID.IsValid()) 1353 | pSteamID = &g_CustomSteamID; 1354 | else 1355 | return pContext->ThrowNativeError("Custom SteamID not set."); 1356 | } 1357 | if (!pSteamID) 1358 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1359 | 1360 | SubIDMap::IndexType_t index = g_subIDs.Find(pSteamID->GetAccountID()); 1361 | if (!g_subIDs.IsValidIndex(index)) 1362 | return pContext->ThrowNativeError("No subscriptions were found for client %d", params[1]); 1363 | 1364 | if(!g_subIDs.Element(index).IsValidIndex(params[2])) 1365 | return pContext->ThrowNativeError("Subscription index %u is out of bounds for client %d", index, params[1]); 1366 | 1367 | return g_subIDs.Element(index).Element(params[2]); 1368 | } 1369 | 1370 | static cell_t GetNumClientDLCs(IPluginContext *pContext, const cell_t *params) 1371 | { 1372 | const CSteamID *pSteamID; 1373 | if(params[1] > -1) 1374 | { 1375 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1376 | } else { 1377 | if (g_CustomSteamID.IsValid()) 1378 | pSteamID = &g_CustomSteamID; 1379 | else 1380 | return pContext->ThrowNativeError("Custom SteamID not set."); 1381 | } 1382 | if (!pSteamID) 1383 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1384 | 1385 | DLCMap::IndexType_t index = g_DLCs.Find(pSteamID->GetAccountID()); 1386 | if (!g_DLCs.IsValidIndex(index)) 1387 | return 0; 1388 | 1389 | return g_DLCs.Element(index).Count(); 1390 | } 1391 | 1392 | static cell_t GetClientDLC(IPluginContext *pContext, const cell_t *params) 1393 | { 1394 | const CSteamID *pSteamID; 1395 | if(params[1] > -1) 1396 | { 1397 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1398 | } else { 1399 | if (g_CustomSteamID.IsValid()) 1400 | pSteamID = &g_CustomSteamID; 1401 | else 1402 | return pContext->ThrowNativeError("Custom SteamID not set."); 1403 | } 1404 | if (!pSteamID) 1405 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1406 | 1407 | DLCMap::IndexType_t index = g_DLCs.Find(pSteamID->GetAccountID()); 1408 | if (!g_DLCs.IsValidIndex(index)) 1409 | return pContext->ThrowNativeError("No DLCs were found for client %d", params[1]); 1410 | 1411 | if(!g_DLCs.Element(index).IsValidIndex(params[2])) 1412 | return pContext->ThrowNativeError("DLC index %u is out of bounds for client %d", index, params[1]); 1413 | 1414 | return g_DLCs.Element(index).Element(params[2]); 1415 | } 1416 | 1417 | static cell_t GetCSteamIDForClient(IPluginContext *pContext, const cell_t *params) 1418 | { 1419 | const CSteamID *pSteamID; 1420 | if(params[1] > -1) 1421 | { 1422 | pSteamID = engine->GetClientSteamID(engine->PEntityOfEntIndex(params[1])); 1423 | } else { 1424 | if (g_CustomSteamID.IsValid()) 1425 | pSteamID = &g_CustomSteamID; 1426 | else 1427 | return pContext->ThrowNativeError("Custom SteamID not set."); 1428 | } 1429 | if (!pSteamID) 1430 | return pContext->ThrowNativeError("No SteamID found for client %d", params[1]); 1431 | 1432 | char *steamIDString; 1433 | pContext->LocalToString(params[2], &steamIDString); 1434 | 1435 | int numbytes = g_pSM->Format(steamIDString, params[3], "%llu", pSteamID->ConvertToUint64()); 1436 | numbytes++; // Format's return value doesn't include the NULL terminator. 1437 | 1438 | return numbytes; 1439 | } 1440 | 1441 | static cell_t RenderedIDToCSteamID(IPluginContext *pContext, const cell_t *params) 1442 | { 1443 | char *pRenderedSteamID; 1444 | pContext->LocalToString(params[1], &pRenderedSteamID); 1445 | 1446 | CSteamID steamID = atocsteamid(pRenderedSteamID); 1447 | 1448 | if (steamID.IsValid()) 1449 | { 1450 | char *steamIDString; 1451 | pContext->LocalToString(params[2], &steamIDString); 1452 | 1453 | int numbytes = g_pSM->Format(steamIDString, params[3], "%llu", steamID.ConvertToUint64()); 1454 | numbytes++; // Format's return value doesn't include the NULL terminator. 1455 | 1456 | return numbytes; 1457 | } else { 1458 | return pContext->ThrowNativeError("%s is not a valid SteamID", pRenderedSteamID); 1459 | } 1460 | } 1461 | 1462 | static cell_t CSteamIDToRenderedID(IPluginContext *pContext, const cell_t *params) 1463 | { 1464 | char *pSteamID; 1465 | pContext->LocalToString(params[1], &pSteamID); 1466 | 1467 | CSteamID steamID(atoui64(pSteamID)); 1468 | 1469 | if (steamID.IsValid()) 1470 | { 1471 | char *pRenderedSteamID; 1472 | pContext->LocalToString(params[2], &pRenderedSteamID); 1473 | 1474 | int numbytes = g_pSM->Format(pRenderedSteamID, params[3], "%s", steamID.Render()); 1475 | numbytes++; // Format's return value doesn't include the NULL terminator. 1476 | 1477 | return numbytes; 1478 | } else { 1479 | return pContext->ThrowNativeError("%s is not a valid SteamID", pSteamID); 1480 | } 1481 | } 1482 | 1483 | static cell_t SetCustomSteamID(IPluginContext *pContext, const cell_t *params) 1484 | { 1485 | char *pRenderedSteamID; 1486 | pContext->LocalToString(params[1], &pRenderedSteamID); 1487 | 1488 | CSteamID steamID = atocsteamid(pRenderedSteamID); 1489 | 1490 | if (steamID.IsValid()) 1491 | { 1492 | g_CustomSteamID = steamID; 1493 | return true; 1494 | } else { 1495 | g_CustomSteamID = k_steamIDNil; 1496 | return pContext->ThrowNativeError("%s is not a valid SteamID", pRenderedSteamID); 1497 | } 1498 | } 1499 | 1500 | static cell_t GetCustomSteamID(IPluginContext *pContext, const cell_t *params) 1501 | { 1502 | if (!g_CustomSteamID.IsValid()) 1503 | return pContext->ThrowNativeError("Custom SteamID not set."); 1504 | 1505 | char *steamIDString; 1506 | pContext->LocalToString(params[1], &steamIDString); 1507 | 1508 | int numbytes = g_pSM->Format(steamIDString, params[2], "%s", g_CustomSteamID.Render()); 1509 | numbytes++; // Format's return value doesn't include the NULL terminator. 1510 | 1511 | return numbytes; 1512 | } 1513 | 1514 | static cell_t GroupIDToCSteamID(IPluginContext *pContext, const cell_t *params) 1515 | { 1516 | char *steamIDString; 1517 | pContext->LocalToString(params[2], &steamIDString); 1518 | 1519 | int numbytes = g_pSM->Format(steamIDString, params[3], "%llu", CSteamID(params[1], k_EUniversePublic, k_EAccountTypeClan).ConvertToUint64()); 1520 | numbytes++; // Format's return value doesn't include the NULL terminator. 1521 | 1522 | return numbytes; 1523 | } 1524 | 1525 | static cell_t CSteamIDToGroupID(IPluginContext *pContext, const cell_t *params) 1526 | { 1527 | char *pSteamID; 1528 | pContext->LocalToString(params[1], &pSteamID); 1529 | 1530 | CSteamID steamID(atoui64(pSteamID)); 1531 | 1532 | if (steamID.IsValid()) 1533 | { 1534 | return steamID.GetAccountID(); 1535 | } else { 1536 | return pContext->ThrowNativeError("%s is not a valid SteamID", pSteamID); 1537 | } 1538 | } 1539 | 1540 | static cell_t CreateHTTPRequest(IPluginContext *pContext, const cell_t *params) 1541 | { 1542 | if (!g_pSteamHTTP) 1543 | return 0; 1544 | 1545 | EHTTPMethod eHttpRequestMethod = (EHTTPMethod)params[1]; 1546 | 1547 | char *pchAbsoluteURL; 1548 | pContext->LocalToString(params[2], &pchAbsoluteURL); 1549 | 1550 | return g_pSteamHTTP->CreateHTTPRequest(eHttpRequestMethod, pchAbsoluteURL); 1551 | } 1552 | 1553 | static cell_t SetHTTPRequestNetworkActivityTimeout(IPluginContext *pContext, const cell_t *params) 1554 | { 1555 | if (!g_pSteamHTTP) 1556 | return 0; 1557 | 1558 | HTTPRequestHandle hRequest = params[1]; 1559 | uint32 unTimeoutSeconds = params[2]; 1560 | 1561 | if (!g_pSteamHTTP->SetHTTPRequestNetworkActivityTimeout(hRequest, unTimeoutSeconds)) 1562 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or already sent"); 1563 | 1564 | return 0; 1565 | } 1566 | 1567 | static cell_t SetHTTPRequestHeaderValue(IPluginContext *pContext, const cell_t *params) 1568 | { 1569 | if (!g_pSteamHTTP) 1570 | return 0; 1571 | 1572 | HTTPRequestHandle hRequest = params[1]; 1573 | 1574 | char *pchHeaderName; 1575 | pContext->LocalToString(params[2], &pchHeaderName); 1576 | 1577 | char *pchHeaderValue; 1578 | pContext->LocalToString(params[3], &pchHeaderValue); 1579 | 1580 | if (!g_pSteamHTTP->SetHTTPRequestHeaderValue(hRequest, pchHeaderName, pchHeaderValue)) 1581 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or already sent"); 1582 | 1583 | return 0; 1584 | } 1585 | 1586 | static cell_t SetHTTPRequestGetOrPostParameter(IPluginContext *pContext, const cell_t *params) 1587 | { 1588 | if (!g_pSteamHTTP) 1589 | return 0; 1590 | 1591 | HTTPRequestHandle hRequest = params[1]; 1592 | 1593 | char *pchParamName; 1594 | pContext->LocalToString(params[2], &pchParamName); 1595 | 1596 | char *pchParamValue; 1597 | pContext->LocalToString(params[3], &pchParamValue); 1598 | 1599 | if (!g_pSteamHTTP->SetHTTPRequestGetOrPostParameter(hRequest, pchParamName, pchParamValue)) 1600 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or already sent"); 1601 | 1602 | return 0; 1603 | } 1604 | 1605 | static cell_t SendHTTPRequest(IPluginContext *pContext, const cell_t *params) 1606 | { 1607 | if (!g_pSteamHTTP) 1608 | return 0; 1609 | 1610 | HTTPRequestHandle hRequest = params[1]; 1611 | 1612 | HTTPRequestCompletedContextPack contextPack; 1613 | contextPack.pCallbackFunction = new HTTPRequestCompletedContextFunction; 1614 | 1615 | contextPack.pCallbackFunction->pContext = pContext; 1616 | contextPack.pCallbackFunction->uPluginFunction = params[2]; 1617 | 1618 | if (params[0] >= 3) 1619 | { 1620 | contextPack.pCallbackFunction->bHasContext = true; 1621 | contextPack.iPluginContextValue = params[3]; 1622 | } 1623 | 1624 | if (!g_pSteamHTTP->SetHTTPRequestContextValue(hRequest, contextPack.ulContextValue)) 1625 | return pContext->ThrowNativeError("Unable to send HTTP request, couldn't pack context information"); 1626 | 1627 | SteamAPICall_t hAPICall; 1628 | if (!g_pSteamHTTP->SendHTTPRequest(hRequest, &hAPICall)) 1629 | return pContext->ThrowNativeError("Unable to send HTTP request, check handle is valid and that there is a network connection present"); 1630 | 1631 | g_HTTPRequestSteamAPICalls.AddToTail(hAPICall); 1632 | return 0; 1633 | } 1634 | 1635 | static cell_t DeferHTTPRequest(IPluginContext *pContext, const cell_t *params) 1636 | { 1637 | if (!g_pSteamHTTP) 1638 | return 0; 1639 | 1640 | HTTPRequestHandle hRequest = params[1]; 1641 | 1642 | if (!g_pSteamHTTP->DeferHTTPRequest(hRequest)) 1643 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or not yet sent"); 1644 | 1645 | return 0; 1646 | } 1647 | 1648 | static cell_t PrioritizeHTTPRequest(IPluginContext *pContext, const cell_t *params) 1649 | { 1650 | if (!g_pSteamHTTP) 1651 | return 0; 1652 | 1653 | HTTPRequestHandle hRequest = params[1]; 1654 | 1655 | if (!g_pSteamHTTP->PrioritizeHTTPRequest(hRequest)) 1656 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or not yet sent"); 1657 | 1658 | return 0; 1659 | } 1660 | 1661 | static cell_t GetHTTPResponseHeaderSize(IPluginContext *pContext, const cell_t *params) 1662 | { 1663 | if (!g_pSteamHTTP) 1664 | return 0; 1665 | 1666 | HTTPRequestHandle hRequest = params[1]; 1667 | 1668 | char *pchHeaderName; 1669 | pContext->LocalToString(params[2], &pchHeaderName); 1670 | 1671 | uint32 unResponseHeaderSize = 0; 1672 | if (!g_pSteamHTTP->GetHTTPResponseHeaderSize(hRequest, pchHeaderName, &unResponseHeaderSize)) 1673 | return -1; 1674 | 1675 | return unResponseHeaderSize; 1676 | } 1677 | 1678 | static cell_t GetHTTPResponseHeaderValue(IPluginContext *pContext, const cell_t *params) 1679 | { 1680 | if (!g_pSteamHTTP) 1681 | return 0; 1682 | 1683 | HTTPRequestHandle hRequest = params[1]; 1684 | 1685 | char *pchHeaderName; 1686 | pContext->LocalToString(params[2], &pchHeaderName); 1687 | 1688 | uint32 unBufferSize = params[4]; 1689 | char *pHeaderValueBuffer; 1690 | pContext->LocalToString(params[3], &pHeaderValueBuffer); 1691 | 1692 | if (!g_pSteamHTTP->GetHTTPResponseHeaderValue(hRequest, pchHeaderName, (uint8 *)pHeaderValueBuffer, unBufferSize)) 1693 | return pContext->ThrowNativeError("HTTPRequestHandle invalid, not yet sent, invalid buffer size or header not present"); 1694 | 1695 | return 0; 1696 | } 1697 | 1698 | static cell_t GetHTTPResponseBodySize(IPluginContext *pContext, const cell_t *params) 1699 | { 1700 | if (!g_pSteamHTTP) 1701 | return 0; 1702 | 1703 | HTTPRequestHandle hRequest = params[1]; 1704 | 1705 | uint32 unBodySize = 0; 1706 | if (!g_pSteamHTTP->GetHTTPResponseBodySize(hRequest, &unBodySize)) 1707 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or not yet sent"); 1708 | 1709 | return unBodySize; 1710 | } 1711 | 1712 | static cell_t GetHTTPResponseBodyData(IPluginContext *pContext, const cell_t *params) 1713 | { 1714 | if (!g_pSteamHTTP) 1715 | return 0; 1716 | 1717 | HTTPRequestHandle hRequest = params[1]; 1718 | 1719 | uint32 unBufferSize = params[3]; 1720 | char *pBodyDataBuffer; 1721 | pContext->LocalToString(params[2], &pBodyDataBuffer); 1722 | 1723 | uint32 unBodySize = 0; 1724 | if (!g_pSteamHTTP->GetHTTPResponseBodySize(hRequest, &unBodySize)) 1725 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or not yet sent"); 1726 | 1727 | if (unBufferSize < unBodySize) 1728 | return pContext->ThrowNativeError("Buffer too small"); 1729 | 1730 | if (!g_pSteamHTTP->GetHTTPResponseBodyData(hRequest, (uint8 *)pBodyDataBuffer, unBodySize)) 1731 | return pContext->ThrowNativeError("HTTPRequestHandle invalid, not yet sent or invalid buffer size"); 1732 | 1733 | if (unBufferSize > unBodySize) 1734 | pBodyDataBuffer[unBodySize] = '\0'; 1735 | 1736 | return 0; 1737 | } 1738 | 1739 | static cell_t WriteHTTPResponseBody(IPluginContext *pContext, const cell_t *params) 1740 | { 1741 | if (!g_pSteamHTTP) 1742 | return 0; 1743 | 1744 | HTTPRequestHandle hRequest = params[1]; 1745 | 1746 | uint32 unBodySize = 0; 1747 | if (!g_pSteamHTTP->GetHTTPResponseBodySize(hRequest, &unBodySize)) 1748 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or not yet sent"); 1749 | 1750 | uint8 *pBodyDataBuffer = (uint8 *)calloc(unBodySize, 1); 1751 | if (!pBodyDataBuffer) 1752 | return pContext->ThrowNativeError("Failed to allocate memory for response body"); 1753 | 1754 | if (!g_pSteamHTTP->GetHTTPResponseBodyData(hRequest, pBodyDataBuffer, unBodySize)) 1755 | return pContext->ThrowNativeError("HTTPRequestHandle invalid, not yet sent or invalid buffer size"); 1756 | 1757 | char *pchFilePath; 1758 | pContext->LocalToString(params[2], &pchFilePath); 1759 | 1760 | FileHandle_t hDataFile = g_pFullFileSystem->Open(pchFilePath, "wb", "MOD"); 1761 | if (!hDataFile) 1762 | return pContext->ThrowNativeError("Unable to open %s for writing", pchFilePath); 1763 | 1764 | g_pFullFileSystem->Write(pBodyDataBuffer, unBodySize, hDataFile); 1765 | 1766 | free(pBodyDataBuffer); 1767 | g_pFullFileSystem->Close(hDataFile); 1768 | 1769 | return 0; 1770 | } 1771 | 1772 | static cell_t ReleaseHTTPRequest(IPluginContext *pContext, const cell_t *params) 1773 | { 1774 | if (!g_pSteamHTTP) 1775 | return 0; 1776 | 1777 | HTTPRequestHandle hRequest = params[1]; 1778 | 1779 | if (!g_pSteamHTTP->ReleaseHTTPRequest(hRequest)) 1780 | return pContext->ThrowNativeError("HTTPRequestHandle invalid"); 1781 | 1782 | return 0; 1783 | } 1784 | 1785 | static cell_t GetHTTPDownloadProgressPercent(IPluginContext *pContext, const cell_t *params) 1786 | { 1787 | if (!g_pSteamHTTP) 1788 | return 0; 1789 | 1790 | HTTPRequestHandle hRequest = params[1]; 1791 | 1792 | float flPercent; 1793 | 1794 | if (!g_pSteamHTTP->GetHTTPDownloadProgressPct(hRequest, &flPercent)) 1795 | return pContext->ThrowNativeError("HTTPRequestHandle invalid or not yet sent"); 1796 | 1797 | return sp_ftoc(flPercent); 1798 | } 1799 | 1800 | static cell_t SetHTTPRequestRawPostBody(IPluginContext *pContext, const cell_t *params) 1801 | { 1802 | if (!g_pSteamHTTP) 1803 | return 0; 1804 | 1805 | HTTPRequestHandle hRequest = params[1]; 1806 | 1807 | char *pData; 1808 | pContext->LocalToString(params[2], &pData); 1809 | uint32 unDataSize = params[3]; 1810 | 1811 | char *pContentType; 1812 | pContext->LocalToString(params[4], &pContentType); 1813 | 1814 | return g_pSteamHTTP->SetHTTPRequestRawPostBody(hRequest, pContentType, (uint8*)pData, unDataSize); 1815 | } 1816 | 1817 | static cell_t SetHTTPRequestRawPostBodyFile(IPluginContext *pContext, const cell_t *params) 1818 | { 1819 | if (!g_pSteamHTTP) 1820 | return 0; 1821 | 1822 | HTTPRequestHandle hRequest = params[1]; 1823 | 1824 | char *pchFilePath; 1825 | pContext->LocalToString(params[2], &pchFilePath); 1826 | 1827 | char *pContentType; 1828 | pContext->LocalToString(params[3], &pContentType); 1829 | 1830 | FileHandle_t hDataFile = g_pFullFileSystem->Open(pchFilePath, "rb", "MOD"); 1831 | if (!hDataFile) 1832 | return pContext->ThrowNativeError("Unable to open %s for reading", pchFilePath); 1833 | 1834 | uint32 unDataSize = g_pFullFileSystem->Size(hDataFile); 1835 | uint8 *pData = new uint8[unDataSize]; 1836 | g_pFullFileSystem->Read((void*)pData, unDataSize, hDataFile); 1837 | 1838 | g_pFullFileSystem->Close(hDataFile); 1839 | 1840 | bool result = g_pSteamHTTP->SetHTTPRequestRawPostBody(hRequest, pContentType, pData, unDataSize); 1841 | 1842 | if (result == false) 1843 | g_pSM->LogError(myself, "Failed to insert %s into POST body.", pchFilePath); 1844 | 1845 | delete[] pData; 1846 | 1847 | return result; 1848 | } 1849 | 1850 | sp_nativeinfo_t g_ExtensionNatives[] = 1851 | { 1852 | { "Steam_RequestGroupStatus", RequestGroupStatus }, 1853 | { "Steam_RequestGameplayStats", RequestGameplayStats }, 1854 | { "Steam_RequestServerReputation", RequestServerReputation }, 1855 | { "Steam_ForceHeartbeat", ForceHeartbeat }, 1856 | { "Steam_IsVACEnabled", IsVACEnabled }, 1857 | { "Steam_IsConnected", IsConnected }, 1858 | { "Steam_GetPublicIP", GetPublicIP }, 1859 | { "Steam_SetRule", SetKeyValue }, 1860 | { "Steam_ClearRules", ClearAllKeyValues }, 1861 | { "Steam_AddMasterServer", AddMasterServer }, 1862 | { "Steam_RemoveMasterServer", RemoveMasterServer }, 1863 | { "Steam_GetNumMasterServers", GetNumMasterServers }, 1864 | { "Steam_GetMasterServerAddress", GetMasterServerAddress }, 1865 | { "Steam_SetGameDescription", SetGameDescription }, 1866 | { "Steam_RequestStats", RequestStats }, 1867 | { "Steam_GetStat", GetStatInt }, 1868 | { "Steam_GetStatFloat", GetStatFloat }, 1869 | { "Steam_IsAchieved", IsAchieved }, 1870 | { "Steam_GetNumClientSubscriptions", GetNumClientSubscriptions }, 1871 | { "Steam_GetClientSubscription", GetClientSubscription }, 1872 | { "Steam_GetNumClientDLCs", GetNumClientDLCs }, 1873 | { "Steam_GetClientDLC", GetClientDLC }, 1874 | { "Steam_GetCSteamIDForClient", GetCSteamIDForClient }, 1875 | { "Steam_RenderedIDToCSteamID", RenderedIDToCSteamID }, 1876 | { "Steam_CSteamIDToRenderedID", CSteamIDToRenderedID }, 1877 | { "Steam_SetCustomSteamID", SetCustomSteamID }, 1878 | { "Steam_GetCustomSteamID", GetCustomSteamID }, 1879 | { "Steam_GroupIDToCSteamID", GroupIDToCSteamID }, 1880 | { "Steam_CSteamIDToGroupID", CSteamIDToGroupID }, 1881 | { "Steam_CreateHTTPRequest", CreateHTTPRequest }, 1882 | { "Steam_SetHTTPRequestNetworkActivityTimeout", SetHTTPRequestNetworkActivityTimeout }, 1883 | { "Steam_SetHTTPRequestHeaderValue", SetHTTPRequestHeaderValue }, 1884 | { "Steam_SetHTTPRequestGetOrPostParameter", SetHTTPRequestGetOrPostParameter }, 1885 | { "Steam_SendHTTPRequest", SendHTTPRequest }, 1886 | { "Steam_DeferHTTPRequest", DeferHTTPRequest }, 1887 | { "Steam_PrioritizeHTTPRequest", PrioritizeHTTPRequest }, 1888 | { "Steam_GetHTTPResponseHeaderSize", GetHTTPResponseHeaderSize }, 1889 | { "Steam_GetHTTPResponseHeaderValue", GetHTTPResponseHeaderValue }, 1890 | { "Steam_GetHTTPResponseBodySize", GetHTTPResponseBodySize }, 1891 | { "Steam_GetHTTPResponseBodyData", GetHTTPResponseBodyData }, 1892 | { "Steam_WriteHTTPResponseBody", WriteHTTPResponseBody }, 1893 | { "Steam_ReleaseHTTPRequest", ReleaseHTTPRequest }, 1894 | { "Steam_GetHTTPDownloadProgressPercent", GetHTTPDownloadProgressPercent }, 1895 | { "Steam_SetHTTPRequestRawPostBody", SetHTTPRequestRawPostBody }, 1896 | { "Steam_SetHTTPRequestRawPostBodyFile", SetHTTPRequestRawPostBodyFile }, 1897 | { NULL, NULL } 1898 | }; 1899 | -------------------------------------------------------------------------------- /extension/extension.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * SteamTools - Exposes some SteamClient functions to SourceMod plugins. 4 | * Copyright (C) 2010 Asher Baker (asherkin). All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | * As a special exception, AlliedModders LLC gives you permission to link the 20 | * code of this program (as well as its derivative works) to "Half-Life 2," the 21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 22 | * by the Valve Corporation. You must obey the GNU General Public License in 23 | * all respects for all other code used. Additionally, AlliedModders LLC grants 24 | * this exception to all derivative works. AlliedModders LLC defines further 25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 26 | * or . 27 | * ============================================================================= 28 | */ 29 | 30 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ 31 | #define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ 32 | 33 | /** 34 | * @file extension.h 35 | * @brief SteamTools extension code header. 36 | */ 37 | 38 | #include "smsdk_ext.h" 39 | 40 | #define NO_CSTEAMID_STL 41 | #define INTERFACEOSW_H 42 | #include 43 | 44 | class ISteamClient: public ISteamClient012 {}; 45 | #define STEAMCLIENT_INTERFACE_VERSION STEAMCLIENT_INTERFACE_VERSION_012 46 | 47 | class ISteamGameServer: public ISteamGameServer012 {}; 48 | #define STEAMGAMESERVER_INTERFACE_VERSION STEAMGAMESERVER_INTERFACE_VERSION_012 49 | 50 | class ISteamUtils: public ISteamUtils006 {}; 51 | #define STEAMUTILS_INTERFACE_VERSION STEAMUTILS_INTERFACE_VERSION_006 52 | 53 | class ISteamGameServerStats: public ISteamGameServerStats001 {}; 54 | #define STEAMGAMESERVERSTATS_INTERFACE_VERSION STEAMGAMESERVERSTATS_INTERFACE_VERSION_001 55 | 56 | class ISteamHTTP: public ISteamHTTP001 {}; 57 | #define STEAMHTTP_INTERFACE_VERSION STEAMHTTP_INTERFACE_VERSION_001 58 | 59 | /** 60 | * @brief Sample implementation of the SDK Extension. 61 | * Note: Uncomment one of the pre-defined virtual functions in order to use it. 62 | */ 63 | class SteamTools: 64 | public SDKExtension, 65 | public IConCommandBaseAccessor, 66 | public IPluginsListener 67 | { 68 | public: 69 | /** 70 | * @brief This is called after the initial loading sequence has been processed. 71 | * 72 | * @param error Error message buffer. 73 | * @param maxlength Size of error message buffer. 74 | * @param late Whether or not the module was loaded after map load. 75 | * @return True to succeed loading, false to fail. 76 | */ 77 | virtual bool SDK_OnLoad(char *error, size_t maxlen, bool late); 78 | 79 | /** 80 | * @brief This is called right before the extension is unloaded. 81 | */ 82 | virtual void SDK_OnUnload(); 83 | 84 | /** 85 | * @brief Called when the pause state is changed. 86 | */ 87 | //virtual void SDK_OnPauseChange(bool paused); 88 | 89 | /** 90 | * @brief this is called when Core wants to know if your extension is working. 91 | * 92 | * @param error Error message buffer. 93 | * @param maxlength Size of error message buffer. 94 | * @return True if working, false otherwise. 95 | */ 96 | virtual bool QueryRunning(char *error, size_t maxlen); 97 | public: 98 | #if defined SMEXT_CONF_METAMOD 99 | /** 100 | * @brief Called when Metamod is attached, before the extension version is called. 101 | * 102 | * @param error Error buffer. 103 | * @param maxlength Maximum size of error buffer. 104 | * @param late Whether or not Metamod considers this a late load. 105 | * @return True to succeed, false to fail. 106 | */ 107 | virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late); 108 | 109 | /** 110 | * @brief Called when Metamod is detaching, after the extension version is called. 111 | * NOTE: By default this is blocked unless sent from SourceMod. 112 | * 113 | * @param error Error buffer. 114 | * @param maxlength Maximum size of error buffer. 115 | * @return True to succeed, false to fail. 116 | */ 117 | //virtual bool SDK_OnMetamodUnload(char *error, size_t maxlen); 118 | 119 | /** 120 | * @brief Called when Metamod's pause state is changing. 121 | * NOTE: By default this is blocked unless sent from SourceMod. 122 | * 123 | * @param paused Pause state being set. 124 | * @param error Error buffer. 125 | * @param maxlength Maximum size of error buffer. 126 | * @return True to succeed, false to fail. 127 | */ 128 | //virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlen); 129 | #endif 130 | 131 | public: //IConCommandBaseAccessor 132 | bool RegisterConCommandBase(ConCommandBase *pCommand); 133 | 134 | public: //IPluginsListener 135 | void OnPluginLoaded(IPlugin *plugin); 136 | }; 137 | 138 | void Hook_Think(bool finalTick); 139 | void Hook_GameServerSteamAPIActivated(void); 140 | void Hook_GameServerSteamAPIShutdown(void); 141 | 142 | bool Hook_WasRestartRequested(); 143 | 144 | EBeginAuthSessionResult Hook_BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, CSteamID steamID); 145 | void Hook_EndAuthSession(CSteamID steamID); 146 | 147 | bool CheckInterfaces(); 148 | bool LoadSteamclient(ISteamClient **pSteamClient, int method = 0); 149 | 150 | CSteamID atocsteamid(const char *pRenderedID); 151 | 152 | extern sp_nativeinfo_t g_ExtensionNatives[]; 153 | 154 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ 155 | -------------------------------------------------------------------------------- /extension/sdk/smsdk_config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * SteamTools - Exposes some SteamClient functions to SourceMod plugins. 4 | * Copyright (C) 2010 Asher Baker (asherkin). All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | * As a special exception, AlliedModders LLC gives you permission to link the 20 | * code of this program (as well as its derivative works) to "Half-Life 2," the 21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 22 | * by the Valve Corporation. You must obey the GNU General Public License in 23 | * all respects for all other code used. Additionally, AlliedModders LLC grants 24 | * this exception to all derivative works. AlliedModders LLC defines further 25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 26 | * or . 27 | * ============================================================================= 28 | */ 29 | 30 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 31 | #define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 32 | 33 | #include "version.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 "SteamTools" 42 | #define SMEXT_CONF_DESCRIPTION "SteamWorks for SourceMod" 43 | #define SMEXT_CONF_VERSION SM_FULL_VERSION 44 | #define SMEXT_CONF_AUTHOR "Asher \"asherkin\" Baker" 45 | #define SMEXT_CONF_URL "https://limetech.io/" 46 | #define SMEXT_CONF_LOGTAG "STEAMTOOLS" 47 | #define SMEXT_CONF_LICENSE "GPLv3" 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 | 81 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_ 82 | -------------------------------------------------------------------------------- /extension/sdk/smsdk_ext.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * SourceMod Base Extension Code 4 | * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | * As a special exception, AlliedModders LLC gives you permission to link the 20 | * code of this program (as well as its derivative works) to "Half-Life 2," the 21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 22 | * by the Valve Corporation. You must obey the GNU General Public License in 23 | * all respects for all other code used. Additionally, AlliedModders LLC grants 24 | * this exception to all derivative works. AlliedModders LLC defines further 25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 26 | * or . 27 | * ============================================================================= 28 | */ 29 | 30 | #include 31 | #include 32 | #include "smsdk_ext.h" 33 | 34 | /** 35 | * @file smsdk_ext.cpp 36 | * @brief Contains wrappers for making Extensions easier to write. 37 | */ 38 | 39 | IExtension *myself = NULL; /**< Ourself */ 40 | IShareSys *g_pShareSys = NULL; /**< Share system */ 41 | IShareSys *sharesys = NULL; /**< Share system */ 42 | ISourceMod *g_pSM = NULL; /**< SourceMod helpers */ 43 | ISourceMod *smutils = NULL; /**< SourceMod helpers */ 44 | 45 | #if defined SMEXT_ENABLE_FORWARDSYS 46 | IForwardManager *g_pForwards = NULL; /**< Forward system */ 47 | IForwardManager *forwards = NULL; /**< Forward system */ 48 | #endif 49 | #if defined SMEXT_ENABLE_HANDLESYS 50 | IHandleSys *g_pHandleSys = NULL; /**< Handle system */ 51 | IHandleSys *handlesys = NULL; /**< Handle system */ 52 | #endif 53 | #if defined SMEXT_ENABLE_PLAYERHELPERS 54 | IPlayerManager *playerhelpers = NULL; /**< Player helpers */ 55 | #endif //SMEXT_ENABLE_PLAYERHELPERS 56 | #if defined SMEXT_ENABLE_DBMANAGER 57 | IDBManager *dbi = NULL; /**< DB Manager */ 58 | #endif //SMEXT_ENABLE_DBMANAGER 59 | #if defined SMEXT_ENABLE_GAMECONF 60 | IGameConfigManager *gameconfs = NULL; /**< Game config manager */ 61 | #endif //SMEXT_ENABLE_DBMANAGER 62 | #if defined SMEXT_ENABLE_MEMUTILS 63 | IMemoryUtils *memutils = NULL; 64 | #endif //SMEXT_ENABLE_DBMANAGER 65 | #if defined SMEXT_ENABLE_GAMEHELPERS 66 | IGameHelpers *gamehelpers = NULL; 67 | #endif 68 | #if defined SMEXT_ENABLE_TIMERSYS 69 | ITimerSystem *timersys = NULL; 70 | #endif 71 | #if defined SMEXT_ENABLE_ADTFACTORY 72 | IADTFactory *adtfactory = NULL; 73 | #endif 74 | #if defined SMEXT_ENABLE_THREADER 75 | IThreader *threader = NULL; 76 | #endif 77 | #if defined SMEXT_ENABLE_LIBSYS 78 | ILibrarySys *libsys = NULL; 79 | #endif 80 | #if defined SMEXT_ENABLE_PLUGINSYS 81 | SourceMod::IPluginManager *plsys; 82 | #endif 83 | #if defined SMEXT_ENABLE_MENUS 84 | IMenuManager *menus = NULL; 85 | #endif 86 | #if defined SMEXT_ENABLE_ADMINSYS 87 | IAdminSystem *adminsys = NULL; 88 | #endif 89 | #if defined SMEXT_ENABLE_TEXTPARSERS 90 | ITextParsers *textparsers = NULL; 91 | #endif 92 | #if defined SMEXT_ENABLE_USERMSGS 93 | IUserMessages *usermsgs = NULL; 94 | #endif 95 | #if defined SMEXT_ENABLE_TRANSLATOR 96 | ITranslator *translator = NULL; 97 | #endif 98 | #if defined SMEXT_ENABLE_NINVOKE 99 | INativeInterface *ninvoke = NULL; 100 | #endif 101 | 102 | /** Exports the main interface */ 103 | PLATFORM_EXTERN_C IExtensionInterface *GetSMExtAPI() 104 | { 105 | return g_pExtensionIface; 106 | } 107 | 108 | SDKExtension::SDKExtension() 109 | { 110 | #if defined SMEXT_CONF_METAMOD 111 | m_SourceMMLoaded = false; 112 | m_WeAreUnloaded = false; 113 | m_WeGotPauseChange = false; 114 | #endif 115 | } 116 | 117 | bool SDKExtension::OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late) 118 | { 119 | g_pShareSys = sharesys = sys; 120 | myself = me; 121 | 122 | #if defined SMEXT_CONF_METAMOD 123 | m_WeAreUnloaded = true; 124 | 125 | if (!m_SourceMMLoaded) 126 | { 127 | if (error) 128 | { 129 | snprintf(error, maxlength, "Metamod attach failed"); 130 | } 131 | return false; 132 | } 133 | #endif 134 | SM_GET_IFACE(SOURCEMOD, g_pSM); 135 | smutils = g_pSM; 136 | #if defined SMEXT_ENABLE_HANDLESYS 137 | SM_GET_IFACE(HANDLESYSTEM, g_pHandleSys); 138 | handlesys = g_pHandleSys; 139 | #endif 140 | #if defined SMEXT_ENABLE_FORWARDSYS 141 | SM_GET_IFACE(FORWARDMANAGER, g_pForwards); 142 | forwards = g_pForwards; 143 | #endif 144 | #if defined SMEXT_ENABLE_PLAYERHELPERS 145 | SM_GET_IFACE(PLAYERMANAGER, playerhelpers); 146 | #endif 147 | #if defined SMEXT_ENABLE_DBMANAGER 148 | SM_GET_IFACE(DBI, dbi); 149 | #endif 150 | #if defined SMEXT_ENABLE_GAMECONF 151 | SM_GET_IFACE(GAMECONFIG, gameconfs); 152 | #endif 153 | #if defined SMEXT_ENABLE_MEMUTILS 154 | SM_GET_IFACE(MEMORYUTILS, memutils); 155 | #endif 156 | #if defined SMEXT_ENABLE_GAMEHELPERS 157 | SM_GET_IFACE(GAMEHELPERS, gamehelpers); 158 | #endif 159 | #if defined SMEXT_ENABLE_TIMERSYS 160 | SM_GET_IFACE(TIMERSYS, timersys); 161 | #endif 162 | #if defined SMEXT_ENABLE_ADTFACTORY 163 | SM_GET_IFACE(ADTFACTORY, adtfactory); 164 | #endif 165 | #if defined SMEXT_ENABLE_THREADER 166 | SM_GET_IFACE(THREADER, threader); 167 | #endif 168 | #if defined SMEXT_ENABLE_LIBSYS 169 | SM_GET_IFACE(LIBRARYSYS, libsys); 170 | #endif 171 | #if defined SMEXT_ENABLE_PLUGINSYS 172 | SM_GET_IFACE(PLUGINSYSTEM, plsys); 173 | #endif 174 | #if defined SMEXT_ENABLE_MENUS 175 | SM_GET_IFACE(MENUMANAGER, menus); 176 | #endif 177 | #if defined SMEXT_ENABLE_ADMINSYS 178 | SM_GET_IFACE(ADMINSYS, adminsys); 179 | #endif 180 | #if defined SMEXT_ENABLE_TEXTPARSERS 181 | SM_GET_IFACE(TEXTPARSERS, textparsers); 182 | #endif 183 | #if defined SMEXT_ENABLE_USERMSGS 184 | SM_GET_IFACE(USERMSGS, usermsgs); 185 | #endif 186 | #if defined SMEXT_ENABLE_TRANSLATOR 187 | SM_GET_IFACE(TRANSLATOR, translator); 188 | #endif 189 | 190 | if (SDK_OnLoad(error, maxlength, late)) 191 | { 192 | #if defined SMEXT_CONF_METAMOD 193 | m_WeAreUnloaded = true; 194 | #endif 195 | return true; 196 | } 197 | 198 | return false; 199 | } 200 | 201 | bool SDKExtension::IsMetamodExtension() 202 | { 203 | #if defined SMEXT_CONF_METAMOD 204 | return true; 205 | #else 206 | return false; 207 | #endif 208 | } 209 | 210 | void SDKExtension::OnExtensionPauseChange(bool state) 211 | { 212 | #if defined SMEXT_CONF_METAMOD 213 | m_WeGotPauseChange = true; 214 | #endif 215 | SDK_OnPauseChange(state); 216 | } 217 | 218 | void SDKExtension::OnExtensionsAllLoaded() 219 | { 220 | SDK_OnAllLoaded(); 221 | } 222 | 223 | void SDKExtension::OnExtensionUnload() 224 | { 225 | #if defined SMEXT_CONF_METAMOD 226 | m_WeAreUnloaded = true; 227 | #endif 228 | SDK_OnUnload(); 229 | } 230 | 231 | const char *SDKExtension::GetExtensionAuthor() 232 | { 233 | return SMEXT_CONF_AUTHOR; 234 | } 235 | 236 | const char *SDKExtension::GetExtensionDateString() 237 | { 238 | return SMEXT_CONF_DATESTRING; 239 | } 240 | 241 | const char *SDKExtension::GetExtensionDescription() 242 | { 243 | return SMEXT_CONF_DESCRIPTION; 244 | } 245 | 246 | const char *SDKExtension::GetExtensionVerString() 247 | { 248 | return SMEXT_CONF_VERSION; 249 | } 250 | 251 | const char *SDKExtension::GetExtensionName() 252 | { 253 | return SMEXT_CONF_NAME; 254 | } 255 | 256 | const char *SDKExtension::GetExtensionTag() 257 | { 258 | return SMEXT_CONF_LOGTAG; 259 | } 260 | 261 | const char *SDKExtension::GetExtensionURL() 262 | { 263 | return SMEXT_CONF_URL; 264 | } 265 | 266 | bool SDKExtension::SDK_OnLoad(char *error, size_t maxlength, bool late) 267 | { 268 | return true; 269 | } 270 | 271 | void SDKExtension::SDK_OnUnload() 272 | { 273 | } 274 | 275 | void SDKExtension::SDK_OnPauseChange(bool paused) 276 | { 277 | } 278 | 279 | void SDKExtension::SDK_OnAllLoaded() 280 | { 281 | } 282 | 283 | #if defined SMEXT_CONF_METAMOD 284 | 285 | PluginId g_PLID = 0; /**< Metamod plugin ID */ 286 | ISmmPlugin *g_PLAPI = NULL; /**< Metamod plugin API */ 287 | SourceHook::ISourceHook *g_SHPtr = NULL; /**< SourceHook pointer */ 288 | ISmmAPI *g_SMAPI = NULL; /**< SourceMM API pointer */ 289 | 290 | IVEngineServer *engine = NULL; /**< IVEngineServer pointer */ 291 | IServerGameDLL *gamedll = NULL; /**< IServerGameDLL pointer */ 292 | 293 | /** Exposes the extension to Metamod */ 294 | SMM_API void *PL_EXPOSURE(const char *name, int *code) 295 | { 296 | #if defined METAMOD_PLAPI_VERSION 297 | if (name && !strcmp(name, METAMOD_PLAPI_NAME)) 298 | #else 299 | if (name && !strcmp(name, PLAPI_NAME)) 300 | #endif 301 | { 302 | if (code) 303 | { 304 | *code = IFACE_OK; 305 | } 306 | return static_cast(g_pExtensionIface); 307 | } 308 | 309 | if (code) 310 | { 311 | *code = IFACE_FAILED; 312 | } 313 | 314 | return NULL; 315 | } 316 | 317 | bool SDKExtension::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool late) 318 | { 319 | PLUGIN_SAVEVARS(); 320 | 321 | #if !defined METAMOD_PLAPI_VERSION 322 | GET_V_IFACE_ANY(serverFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); 323 | GET_V_IFACE_CURRENT(engineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); 324 | #else 325 | GET_V_IFACE_ANY(GetServerFactory, gamedll, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL); 326 | GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER); 327 | #endif 328 | 329 | m_SourceMMLoaded = true; 330 | 331 | return SDK_OnMetamodLoad(ismm, error, maxlen, late); 332 | } 333 | 334 | bool SDKExtension::Unload(char *error, size_t maxlen) 335 | { 336 | if (!m_WeAreUnloaded) 337 | { 338 | if (error) 339 | { 340 | snprintf(error, maxlen, "This extension must be unloaded by SourceMod."); 341 | } 342 | return false; 343 | } 344 | 345 | return SDK_OnMetamodUnload(error, maxlen); 346 | } 347 | 348 | bool SDKExtension::Pause(char *error, size_t maxlen) 349 | { 350 | if (!m_WeGotPauseChange) 351 | { 352 | if (error) 353 | { 354 | snprintf(error, maxlen, "This extension must be paused by SourceMod."); 355 | } 356 | return false; 357 | } 358 | 359 | m_WeGotPauseChange = false; 360 | 361 | return SDK_OnMetamodPauseChange(true, error, maxlen); 362 | } 363 | 364 | bool SDKExtension::Unpause(char *error, size_t maxlen) 365 | { 366 | if (!m_WeGotPauseChange) 367 | { 368 | if (error) 369 | { 370 | snprintf(error, maxlen, "This extension must be unpaused by SourceMod."); 371 | } 372 | return false; 373 | } 374 | 375 | m_WeGotPauseChange = false; 376 | 377 | return SDK_OnMetamodPauseChange(false, error, maxlen); 378 | } 379 | 380 | const char *SDKExtension::GetAuthor() 381 | { 382 | return GetExtensionAuthor(); 383 | } 384 | 385 | const char *SDKExtension::GetDate() 386 | { 387 | return GetExtensionDateString(); 388 | } 389 | 390 | const char *SDKExtension::GetDescription() 391 | { 392 | return GetExtensionDescription(); 393 | } 394 | 395 | const char *SDKExtension::GetLicense() 396 | { 397 | return SMEXT_CONF_LICENSE; 398 | } 399 | 400 | const char *SDKExtension::GetLogTag() 401 | { 402 | return GetExtensionTag(); 403 | } 404 | 405 | const char *SDKExtension::GetName() 406 | { 407 | return GetExtensionName(); 408 | } 409 | 410 | const char *SDKExtension::GetURL() 411 | { 412 | return GetExtensionURL(); 413 | } 414 | 415 | const char *SDKExtension::GetVersion() 416 | { 417 | return GetExtensionVerString(); 418 | } 419 | 420 | bool SDKExtension::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late) 421 | { 422 | return true; 423 | } 424 | 425 | bool SDKExtension::SDK_OnMetamodUnload(char *error, size_t maxlength) 426 | { 427 | return true; 428 | } 429 | 430 | bool SDKExtension::SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength) 431 | { 432 | return true; 433 | } 434 | 435 | #endif 436 | 437 | /* Overload a few things to prevent libstdc++ linking */ 438 | #if defined __linux__ || defined __APPLE__ 439 | extern "C" void __cxa_pure_virtual(void) 440 | { 441 | } 442 | 443 | void *operator new(size_t size) 444 | { 445 | return malloc(size); 446 | } 447 | 448 | void *operator new[](size_t size) 449 | { 450 | return malloc(size); 451 | } 452 | 453 | void operator delete(void *ptr) 454 | { 455 | free(ptr); 456 | } 457 | 458 | void operator delete[](void * ptr) 459 | { 460 | free(ptr); 461 | } 462 | #endif 463 | 464 | -------------------------------------------------------------------------------- /extension/sdk/smsdk_ext.h: -------------------------------------------------------------------------------- 1 | /** 2 | * ============================================================================= 3 | * SourceMod Base Extension Code 4 | * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. 5 | * ============================================================================= 6 | * 7 | * This program is free software; you can redistribute it and/or modify it under 8 | * the terms of the GNU General Public License, version 3.0, as published by the 9 | * Free Software Foundation. 10 | * 11 | * This program is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 14 | * details. 15 | * 16 | * You should have received a copy of the GNU General Public License along with 17 | * this program. If not, see . 18 | * 19 | * As a special exception, AlliedModders LLC gives you permission to link the 20 | * code of this program (as well as its derivative works) to "Half-Life 2," the 21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software 22 | * by the Valve Corporation. You must obey the GNU General Public License in 23 | * all respects for all other code used. Additionally, AlliedModders LLC grants 24 | * this exception to all derivative works. AlliedModders LLC defines further 25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), 26 | * or . 27 | * ============================================================================= 28 | */ 29 | 30 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ 31 | #define _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ 32 | 33 | /** 34 | * @file smsdk_ext.h 35 | * @brief Contains wrappers for making Extensions easier to write. 36 | */ 37 | 38 | #include "smsdk_config.h" 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #if defined SMEXT_ENABLE_FORWARDSYS 45 | #include 46 | #endif //SMEXT_ENABLE_FORWARDSYS 47 | #if defined SMEXT_ENABLE_PLAYERHELPERS 48 | #include 49 | #endif //SMEXT_ENABLE_PlAYERHELPERS 50 | #if defined SMEXT_ENABLE_DBMANAGER 51 | #include 52 | #endif //SMEXT_ENABLE_DBMANAGER 53 | #if defined SMEXT_ENABLE_GAMECONF 54 | #include 55 | #endif 56 | #if defined SMEXT_ENABLE_MEMUTILS 57 | #include 58 | #endif 59 | #if defined SMEXT_ENABLE_GAMEHELPERS 60 | #include 61 | #endif 62 | #if defined SMEXT_ENABLE_TIMERSYS 63 | #include 64 | #endif 65 | #if defined SMEXT_ENABLE_ADTFACTORY 66 | #include 67 | #endif 68 | #if defined SMEXT_ENABLE_THREADER 69 | #include 70 | #endif 71 | #if defined SMEXT_ENABLE_LIBSYS 72 | #include 73 | #endif 74 | #if defined SMEXT_ENABLE_PLUGINSYS 75 | #include 76 | #endif 77 | #if defined SMEXT_ENABLE_MENUS 78 | #include 79 | #endif 80 | #if defined SMEXT_ENABLE_ADMINSYS 81 | #include 82 | #endif 83 | #if defined SMEXT_ENABLE_TEXTPARSERS 84 | #include 85 | #endif 86 | #if defined SMEXT_ENABLE_USERMSGS 87 | #include 88 | #endif 89 | #if defined SMEXT_ENABLE_TRANSLATOR 90 | #include 91 | #endif 92 | #if defined SMEXT_ENABLE_NINVOKE 93 | #include 94 | #endif 95 | 96 | #if defined SMEXT_CONF_METAMOD 97 | #include 98 | #include 99 | #endif 100 | 101 | using namespace SourceMod; 102 | using namespace SourcePawn; 103 | 104 | class SDKExtension : 105 | #if defined SMEXT_CONF_METAMOD 106 | public ISmmPlugin, 107 | #endif 108 | public IExtensionInterface 109 | { 110 | public: 111 | /** Constructor */ 112 | SDKExtension(); 113 | public: 114 | /** 115 | * @brief This is called after the initial loading sequence has been processed. 116 | * 117 | * @param error Error message buffer. 118 | * @param maxlength Size of error message buffer. 119 | * @param late Whether or not the module was loaded after map load. 120 | * @return True to succeed loading, false to fail. 121 | */ 122 | virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late); 123 | 124 | /** 125 | * @brief This is called right before the extension is unloaded. 126 | */ 127 | virtual void SDK_OnUnload(); 128 | 129 | /** 130 | * @brief This is called once all known extensions have been loaded. 131 | */ 132 | virtual void SDK_OnAllLoaded(); 133 | 134 | /** 135 | * @brief Called when the pause state is changed. 136 | */ 137 | virtual void SDK_OnPauseChange(bool paused); 138 | 139 | #if defined SMEXT_CONF_METAMOD 140 | /** 141 | * @brief Called when Metamod is attached, before the extension version is called. 142 | * 143 | * @param error Error buffer. 144 | * @param maxlength Maximum size of error buffer. 145 | * @param late Whether or not Metamod considers this a late load. 146 | * @return True to succeed, false to fail. 147 | */ 148 | virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late); 149 | 150 | /** 151 | * @brief Called when Metamod is detaching, after the extension version is called. 152 | * NOTE: By default this is blocked unless sent from SourceMod. 153 | * 154 | * @param error Error buffer. 155 | * @param maxlength Maximum size of error buffer. 156 | * @return True to succeed, false to fail. 157 | */ 158 | virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength); 159 | 160 | /** 161 | * @brief Called when Metamod's pause state is changing. 162 | * NOTE: By default this is blocked unless sent from SourceMod. 163 | * 164 | * @param paused Pause state being set. 165 | * @param error Error buffer. 166 | * @param maxlength Maximum size of error buffer. 167 | * @return True to succeed, false to fail. 168 | */ 169 | virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength); 170 | #endif 171 | 172 | public: //IExtensionInterface 173 | virtual bool OnExtensionLoad(IExtension *me, IShareSys *sys, char *error, size_t maxlength, bool late); 174 | virtual void OnExtensionUnload(); 175 | virtual void OnExtensionsAllLoaded(); 176 | 177 | /** Returns whether or not this is a Metamod-based extension */ 178 | virtual bool IsMetamodExtension(); 179 | 180 | /** 181 | * @brief Called when the pause state changes. 182 | * 183 | * @param state True if being paused, false if being unpaused. 184 | */ 185 | virtual void OnExtensionPauseChange(bool state); 186 | 187 | /** Returns name */ 188 | virtual const char *GetExtensionName(); 189 | /** Returns URL */ 190 | virtual const char *GetExtensionURL(); 191 | /** Returns log tag */ 192 | virtual const char *GetExtensionTag(); 193 | /** Returns author */ 194 | virtual const char *GetExtensionAuthor(); 195 | /** Returns version string */ 196 | virtual const char *GetExtensionVerString(); 197 | /** Returns description string */ 198 | virtual const char *GetExtensionDescription(); 199 | /** Returns date string */ 200 | virtual const char *GetExtensionDateString(); 201 | #if defined SMEXT_CONF_METAMOD 202 | public: //ISmmPlugin 203 | /** Called when the extension is attached to Metamod. */ 204 | virtual bool Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlength, bool late); 205 | /** Returns the author to MM */ 206 | virtual const char *GetAuthor(); 207 | /** Returns the name to MM */ 208 | virtual const char *GetName(); 209 | /** Returns the description to MM */ 210 | virtual const char *GetDescription(); 211 | /** Returns the URL to MM */ 212 | virtual const char *GetURL(); 213 | /** Returns the license to MM */ 214 | virtual const char *GetLicense(); 215 | /** Returns the version string to MM */ 216 | virtual const char *GetVersion(); 217 | /** Returns the date string to MM */ 218 | virtual const char *GetDate(); 219 | /** Returns the logtag to MM */ 220 | virtual const char *GetLogTag(); 221 | /** Called on unload */ 222 | virtual bool Unload(char *error, size_t maxlength); 223 | /** Called on pause */ 224 | virtual bool Pause(char *error, size_t maxlength); 225 | /** Called on unpause */ 226 | virtual bool Unpause(char *error, size_t maxlength); 227 | private: 228 | bool m_SourceMMLoaded; 229 | bool m_WeAreUnloaded; 230 | bool m_WeGotPauseChange; 231 | #endif 232 | }; 233 | 234 | extern SDKExtension *g_pExtensionIface; 235 | extern IExtension *myself; 236 | 237 | extern IShareSys *g_pShareSys; 238 | extern IShareSys *sharesys; /* Note: Newer name */ 239 | extern ISourceMod *g_pSM; 240 | extern ISourceMod *smutils; /* Note: Newer name */ 241 | 242 | /* Optional interfaces are below */ 243 | #if defined SMEXT_ENABLE_FORWARDSYS 244 | extern IForwardManager *g_pForwards; 245 | extern IForwardManager *forwards; /* Note: Newer name */ 246 | #endif //SMEXT_ENABLE_FORWARDSYS 247 | #if defined SMEXT_ENABLE_HANDLESYS 248 | extern IHandleSys *g_pHandleSys; 249 | extern IHandleSys *handlesys; /* Note: Newer name */ 250 | #endif //SMEXT_ENABLE_HANDLESYS 251 | #if defined SMEXT_ENABLE_PLAYERHELPERS 252 | extern IPlayerManager *playerhelpers; 253 | #endif //SMEXT_ENABLE_PLAYERHELPERS 254 | #if defined SMEXT_ENABLE_DBMANAGER 255 | extern IDBManager *dbi; 256 | #endif //SMEXT_ENABLE_DBMANAGER 257 | #if defined SMEXT_ENABLE_GAMECONF 258 | extern IGameConfigManager *gameconfs; 259 | #endif //SMEXT_ENABLE_DBMANAGER 260 | #if defined SMEXT_ENABLE_MEMUTILS 261 | extern IMemoryUtils *memutils; 262 | #endif 263 | #if defined SMEXT_ENABLE_GAMEHELPERS 264 | extern IGameHelpers *gamehelpers; 265 | #endif 266 | #if defined SMEXT_ENABLE_TIMERSYS 267 | extern ITimerSystem *timersys; 268 | #endif 269 | #if defined SMEXT_ENABLE_ADTFACTORY 270 | extern IADTFactory *adtfactory; 271 | #endif 272 | #if defined SMEXT_ENABLE_THREADER 273 | extern IThreader *threader; 274 | #endif 275 | #if defined SMEXT_ENABLE_LIBSYS 276 | extern ILibrarySys *libsys; 277 | #endif 278 | #if defined SMEXT_ENABLE_PLUGINSYS 279 | extern SourceMod::IPluginManager *plsys; 280 | #endif 281 | #if defined SMEXT_ENABLE_MENUS 282 | extern IMenuManager *menus; 283 | #endif 284 | #if defined SMEXT_ENABLE_ADMINSYS 285 | extern IAdminSystem *adminsys; 286 | #endif 287 | #if defined SMEXT_ENABLE_USERMSGS 288 | extern IUserMessages *usermsgs; 289 | #endif 290 | #if defined SMEXT_ENABLE_TRANSLATOR 291 | extern ITranslator *translator; 292 | #endif 293 | #if defined SMEXT_ENABLE_NINVOKE 294 | extern INativeInterface *ninvoke; 295 | #endif 296 | 297 | #if defined SMEXT_CONF_METAMOD 298 | PLUGIN_GLOBALVARS(); 299 | extern IVEngineServer *engine; 300 | extern IServerGameDLL *gamedll; 301 | #endif 302 | 303 | /** Creates a SourceMod interface macro pair */ 304 | #define SM_MKIFACE(name) SMINTERFACE_##name##_NAME, SMINTERFACE_##name##_VERSION 305 | /** Automates retrieving SourceMod interfaces */ 306 | #define SM_GET_IFACE(prefix, addr) \ 307 | if (!g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr)) \ 308 | { \ 309 | if (error != NULL && maxlength) \ 310 | { \ 311 | size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ 312 | if (len >= maxlength) \ 313 | { \ 314 | error[maxlength - 1] = '\0'; \ 315 | } \ 316 | } \ 317 | return false; \ 318 | } 319 | /** Automates retrieving SourceMod interfaces when needed outside of SDK_OnLoad() */ 320 | #define SM_GET_LATE_IFACE(prefix, addr) \ 321 | g_pShareSys->RequestInterface(SM_MKIFACE(prefix), myself, (SMInterface **)&addr) 322 | /** Validates a SourceMod interface pointer */ 323 | #define SM_CHECK_IFACE(prefix, addr) \ 324 | if (!addr) \ 325 | { \ 326 | if (error != NULL && maxlength) \ 327 | { \ 328 | size_t len = snprintf(error, maxlength, "Could not find interface: %s", SMINTERFACE_##prefix##_NAME); \ 329 | if (len >= maxlength) \ 330 | { \ 331 | error[maxlength - 1] = '\0'; \ 332 | } \ 333 | } \ 334 | return false; \ 335 | } 336 | 337 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_BASESDK_H_ 338 | -------------------------------------------------------------------------------- /extension/tickets.h: -------------------------------------------------------------------------------- 1 | #ifndef tickets_h__ 2 | #define tickets_h__ 3 | 4 | #define NO_CSTEAMID_STL 5 | #include "SteamTypes.h" 6 | 7 | #include "blob.h" 8 | 9 | class GCTokenSection_t 10 | { 11 | public: 12 | GCTokenSection_t( 13 | uint32 length, 14 | unsigned char unknown[8], 15 | CSteamID steamid, 16 | uint32 generation) 17 | { 18 | this->length = length; 19 | memcpy(this->unknown, unknown, 8); 20 | this->steamid = steamid; 21 | this->generation = generation; 22 | } 23 | 24 | //private: 25 | uint32 length; 26 | unsigned char unknown[8]; 27 | CSteamID steamid; 28 | uint32 generation; 29 | }; 30 | 31 | class SessionSection_t 32 | { 33 | public: 34 | SessionSection_t( 35 | uint32 length, 36 | uint32 unk1, 37 | uint32 unk2, 38 | uint32 externalip, 39 | uint32 filler, 40 | uint32 sometoken, 41 | uint32 unk4) 42 | { 43 | this->length = length; 44 | this->unk1 = unk1; 45 | this->unk2 = unk2; 46 | this->externalip = externalip; 47 | this->filler = filler; 48 | this->sometoken = sometoken; 49 | this->unk4 = unk4; 50 | 51 | } 52 | 53 | //private: 54 | uint32 length; 55 | uint32 unk1; 56 | uint32 unk2; 57 | uint32 externalip; 58 | uint32 filler; 59 | uint32 sometoken; 60 | uint32 unk4; 61 | }; 62 | 63 | class OwnershipTicket_t 64 | { 65 | public: 66 | OwnershipTicket_t( 67 | uint32 length, 68 | uint32 version, 69 | CSteamID steamid, 70 | AppId_t appid, 71 | uint32 externalip, 72 | uint32 internalip, 73 | uint32 ownershipflags, 74 | uint32 generation, 75 | uint32 expiration, 76 | uint16 numlicenses, 77 | uint32 licenses[], 78 | uint16 numdlcs, 79 | uint32 dlcs[], 80 | uint16 filler) 81 | { 82 | this->length = length; 83 | this->version = version; 84 | this->steamid = steamid; 85 | this->appid = appid; 86 | this->externalip = externalip; 87 | this->internalip = internalip; 88 | this->ownershipflags = ownershipflags; 89 | this->generation = generation; 90 | this->expiration = expiration; 91 | this->numlicenses = numlicenses; 92 | this->licenses = licenses; 93 | this->numdlcs = numdlcs; 94 | this->dlcs = dlcs; 95 | this->filler = filler; 96 | } 97 | 98 | /* 99 | ~OwnershipTicket_t() 100 | { 101 | delete this->licenses; 102 | } 103 | */ 104 | 105 | //private: 106 | uint32 length; 107 | uint32 version; 108 | CSteamID steamid; 109 | AppId_t appid; 110 | uint32 externalip; 111 | uint32 internalip; 112 | uint32 ownershipflags; 113 | uint32 generation; 114 | uint32 expiration; 115 | uint16 numlicenses; 116 | uint32 *licenses; 117 | uint16 numdlcs; 118 | uint32 *dlcs; 119 | uint32 filler; 120 | }; 121 | 122 | class OwnershipSection_t 123 | { 124 | public: 125 | OwnershipSection_t( 126 | uint32 length, 127 | OwnershipTicket_t *ticket, 128 | unsigned char signature[128]) 129 | { 130 | this->length = length; 131 | this->ticket = ticket; 132 | memcpy(this->signature, signature, 128); 133 | } 134 | 135 | /* 136 | ~OwnershipSection_t() 137 | { 138 | delete ticket; 139 | } 140 | */ 141 | 142 | //private: 143 | uint32 length; 144 | OwnershipTicket_t *ticket; 145 | unsigned char signature[128]; 146 | }; 147 | 148 | #define AUTHBLOB_READ(type, value) \ 149 | if (!authBlob.Read(&value)) \ 150 | { \ 151 | if (bError) \ 152 | *bError = true; \ 153 | return; \ 154 | } 155 | 156 | class AuthBlob_t 157 | { 158 | public: 159 | AuthBlob_t(const void *pvAuthBlob, size_t cubAuthBlob, bool *bError = NULL) 160 | { 161 | CBlob authBlob(pvAuthBlob, cubAuthBlob); 162 | 163 | uint32 sectionlength; 164 | AUTHBLOB_READ(uint32, sectionlength); 165 | 166 | if (sectionlength != 20) 167 | { 168 | if ((authBlob.GetPosition() + sectionlength) > cubAuthBlob) 169 | { 170 | if (bError) 171 | *bError = true; 172 | return; 173 | } 174 | authBlob.AdvancePosition(sectionlength); 175 | gcsection = NULL; 176 | } else { 177 | unsigned char unknown[8]; 178 | if (!authBlob.Read(unknown, 8)) 179 | { 180 | if (bError) 181 | *bError = true; 182 | return; 183 | } 184 | uint64 steamid; 185 | AUTHBLOB_READ(uint64, steamid); 186 | uint32 generation; 187 | AUTHBLOB_READ(uint32, generation); 188 | 189 | gcsection = new GCTokenSection_t( 190 | sectionlength, 191 | unknown, 192 | steamid, 193 | generation 194 | ); 195 | } 196 | 197 | uint32 sesionsectionlength; 198 | AUTHBLOB_READ(uint32, sesionsectionlength); 199 | 200 | if (sesionsectionlength != 24) 201 | { 202 | if ((authBlob.GetPosition() + sesionsectionlength) > cubAuthBlob) 203 | { 204 | if (bError) 205 | *bError = true; 206 | return; 207 | } 208 | authBlob.AdvancePosition(sesionsectionlength); 209 | session = NULL; 210 | } else { 211 | uint32 unk1; 212 | AUTHBLOB_READ(uint32, unk1); 213 | uint32 unk2; 214 | AUTHBLOB_READ(uint32, unk2); 215 | uint32 externalip; 216 | AUTHBLOB_READ(uint32, externalip); 217 | uint32 filler; 218 | AUTHBLOB_READ(uint32, filler); 219 | uint32 sometoken; 220 | AUTHBLOB_READ(uint32, sometoken); 221 | uint32 unk4; 222 | AUTHBLOB_READ(uint32, unk4); 223 | 224 | session = new SessionSection_t( 225 | sesionsectionlength, 226 | unk1, 227 | unk2, 228 | externalip, 229 | filler, 230 | sometoken, 231 | unk4 232 | ); 233 | } 234 | 235 | uint32 section2length; 236 | AUTHBLOB_READ(uint32, section2length); 237 | 238 | if (section2length == 0) 239 | { 240 | /* 241 | if ((authBlob.GetPosition() + section2length) > cubAuthBlob) 242 | { 243 | if (bError) 244 | *bError = true; 245 | return; 246 | } 247 | authBlob.AdvancePosition(section2length); 248 | */ 249 | ownership = NULL; 250 | } else { 251 | uint32 length; 252 | AUTHBLOB_READ(uint32, length); 253 | uint32 version; 254 | AUTHBLOB_READ(uint32, version); 255 | uint64 steamid; 256 | AUTHBLOB_READ(uint64, steamid); 257 | AppId_t appid; 258 | AUTHBLOB_READ(AppId_t, appid); 259 | uint32 externalip; 260 | AUTHBLOB_READ(uint32, externalip); 261 | uint32 internalip; 262 | AUTHBLOB_READ(uint32, internalip); 263 | uint32 ownershipflags; 264 | AUTHBLOB_READ(uint32, ownershipflags); 265 | uint32 generation; 266 | AUTHBLOB_READ(uint32, generation); 267 | uint32 expiration; 268 | AUTHBLOB_READ(uint32, expiration); 269 | 270 | uint16 numlicenses; 271 | AUTHBLOB_READ(uint16, numlicenses); 272 | uint32 *licenses = new uint32[numlicenses]; 273 | for (int i = 0; i < numlicenses; i++) 274 | { 275 | AUTHBLOB_READ(uint32, licenses[i]); 276 | } 277 | 278 | uint16 numdlcs; 279 | AUTHBLOB_READ(uint16, numdlcs); 280 | uint32 *dlcs = new uint32[numdlcs]; 281 | for (int i = 0; i < numdlcs; i++) 282 | { 283 | AUTHBLOB_READ(uint32, dlcs[i]); 284 | 285 | uint16 numsubs; 286 | AUTHBLOB_READ(uint16, numsubs); 287 | uint32 *subs = new uint32[numsubs]; 288 | for (int i = 0; i < numsubs; i++) 289 | { 290 | AUTHBLOB_READ(uint32, subs[i]); 291 | } 292 | } 293 | 294 | uint16 filler; 295 | AUTHBLOB_READ(uint16, filler); 296 | 297 | unsigned char signature[128]; 298 | if (!authBlob.Read(signature, 128)) 299 | { 300 | if (bError) 301 | *bError = true; 302 | return; 303 | } 304 | 305 | ownership = new OwnershipSection_t( 306 | section2length, 307 | new OwnershipTicket_t( 308 | length, 309 | version, 310 | steamid, 311 | appid, 312 | externalip, 313 | internalip, 314 | ownershipflags, 315 | generation, 316 | expiration, 317 | numlicenses, 318 | licenses, 319 | numdlcs, 320 | dlcs, 321 | filler 322 | ), 323 | signature 324 | ); 325 | } 326 | 327 | this->length = authBlob.GetPosition(); 328 | } 329 | 330 | /* 331 | ~AuthBlob_t() 332 | { 333 | delete section; 334 | delete ownership; 335 | } 336 | */ 337 | 338 | //private: 339 | uint32 length; 340 | GCTokenSection_t *gcsection; 341 | SessionSection_t *session; 342 | OwnershipSection_t *ownership; 343 | }; 344 | 345 | #endif // tickets_h__ 346 | -------------------------------------------------------------------------------- /extension/version.h: -------------------------------------------------------------------------------- 1 | #ifndef _INCLUDE_VERSION_INFORMATION_H_ 2 | #define _INCLUDE_VERSION_INFORMATION_H_ 3 | 4 | /** 5 | * @file Contains version information. 6 | * @brief This file will redirect to an autogenerated version if being compiled via 7 | * the build scripts. 8 | */ 9 | 10 | #if defined SM_GENERATED_BUILD 11 | #include "version_auto.h" 12 | #else 13 | 14 | #ifndef SM_GENERATED_BUILD 15 | #undef BINARY_NAME 16 | #define BINARY_NAME "steamtools.ext.2.ep2v.dll\0" 17 | #endif 18 | 19 | #define SM_BUILD_TAG "-manual" 20 | #define SM_BUILD_UNIQUEID "[MANUAL BUILD]" 21 | #define SM_VERSION "0.10.0" 22 | #define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG 23 | #define SM_FILE_VERSION 0,10,0,0 24 | 25 | #endif 26 | 27 | #endif /* _INCLUDE_VERSION_INFORMATION_H_ */ 28 | -------------------------------------------------------------------------------- /extension/version.rc: -------------------------------------------------------------------------------- 1 | #include "winres.h" 2 | 3 | #include 4 | 5 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 6 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 7 | #pragma code_page(1252) 8 | #endif 9 | 10 | #ifndef SM_GENERATED_BUILD 11 | #define BINARY_NAME "steamtools.ext.2.ep2v.dll\0" 12 | #endif 13 | 14 | VS_VERSION_INFO VERSIONINFO 15 | FILEVERSION SM_FILE_VERSION 16 | PRODUCTVERSION SM_FILE_VERSION 17 | FILEFLAGSMASK 0x17L 18 | #ifdef _DEBUG 19 | FILEFLAGS 0x1L 20 | #else 21 | FILEFLAGS 0x0L 22 | #endif 23 | FILEOS 0x4L 24 | FILETYPE 0x2L 25 | FILESUBTYPE 0x0L 26 | BEGIN 27 | BLOCK "StringFileInfo" 28 | BEGIN 29 | BLOCK "040904b0" 30 | BEGIN 31 | VALUE "Comments", "SteamTools Extension" 32 | VALUE "FileDescription", "SourceMod SteamTools Extension" 33 | VALUE "FileVersion", SM_BUILD_UNIQUEID 34 | VALUE "InternalName", "SteamTools" 35 | VALUE "LegalCopyright", "" 36 | VALUE "OriginalFilename", BINARY_NAME 37 | VALUE "ProductName", "SteamTools Extension" 38 | VALUE "ProductVersion", SM_FULL_VERSION 39 | END 40 | END 41 | BLOCK "VarFileInfo" 42 | BEGIN 43 | VALUE "Translation", 0x0409, 0x04B0 44 | END 45 | END 46 | -------------------------------------------------------------------------------- /plugin/steamtools.inc: -------------------------------------------------------------------------------- 1 | #if defined _steamtools_included 2 | #endinput 3 | #endif 4 | #define _steamtools_included 5 | 6 | #define USE_CUSTOM_STEAMID -1 7 | 8 | /** 9 | * Called after SteamTools has completely finished loading. 10 | * No features are available before this point. 11 | * 12 | * @noreturn 13 | */ 14 | forward Steam_FullyLoaded(); 15 | forward Steam_Shutdown(); 16 | 17 | 18 | /** 19 | * Gets the current status of VAC on the server. 20 | * 21 | * @return A bool representing the current VAC status. 22 | */ 23 | native bool:Steam_IsVACEnabled(); 24 | 25 | /** 26 | * Gets the server's external IP address, as reported by Steam. 27 | * 28 | * @param octets Reference to an array to be filled with the octets of 29 | * the IP address. 30 | * 31 | * @noreturn 32 | */ 33 | native Steam_GetPublicIP(octets[4]); 34 | 35 | 36 | /** 37 | * Is fired when the Steam master servers report that your server is 38 | * outdated 39 | * 40 | * @return Plugin_Continue to continue normal operation or Plugin_Handled 41 | * to block the regular console message. 42 | */ 43 | forward Action:Steam_RestartRequested(); 44 | 45 | 46 | /** 47 | * Requests a client's status in a Steam group. 48 | * Response is returned in Steam_GroupStatusResult forward. 49 | * 50 | * @param client Client index. 51 | * @param groupAccountID 32-bit account ID of group. 52 | * 53 | * @return A bool representing whether or not the request was sent to 54 | * Steam. 55 | */ 56 | native bool:Steam_RequestGroupStatus(client, groupAccountID); 57 | 58 | /** 59 | * Called when a response to a group status request is recieved. 60 | * This is called for all responses recieved, not just ones requested by 61 | * your plugin. 62 | * 63 | * @param client Client index. 64 | * @param groupAccountID 32-bit account ID of group. Make sure to check 65 | * this agaist the ID you are expecting. 66 | * @param groupMember Whether or not the client is a member in the 67 | * specified group. 68 | * @param groupMember Whether or not the client is an officer in the 69 | * specified group. 70 | * 71 | * @noreturn 72 | */ 73 | forward Steam_GroupStatusResult(client, groupAccountID, bool:groupMember, bool:groupOfficer); 74 | 75 | #pragma deprecated No longer operational 76 | native Steam_RequestGameplayStats(); 77 | forward Steam_GameplayStats(rank, totalConnects, totalMinutesPlayed); 78 | 79 | #pragma deprecated No longer operational 80 | native Steam_RequestServerReputation(); 81 | forward Steam_Reputation(reputationScore, bool:banned, bannedIP, bannedPort, bannedGameID, banExpires); 82 | 83 | 84 | /** 85 | * Gets the current Steam connection state, the forwards below fire 86 | * whenever this changes. 87 | * 88 | * @return Steam connection state. 89 | */ 90 | native bool:Steam_IsConnected(); 91 | 92 | /** 93 | * Fired upon a successfull connection to Steam. 94 | * Is also fired for late-loaded plugins. 95 | * 96 | * @noreturn 97 | */ 98 | forward Steam_SteamServersConnected(); 99 | 100 | /** 101 | * Fired upon disconnection from Steam. 102 | * Is also fired for late-loaded plugins. 103 | * 104 | * For plugins loaded with the server, this will normally be fired right 105 | * after Steam_FullyLoaded, closly followed by Steam_SteamServersConnected 106 | * if a successfull connection is established. 107 | * 108 | * @noreturn 109 | */ 110 | forward Steam_SteamServersDisconnected(); 111 | 112 | /** 113 | * Sets an entry in the server's list of rules. This list is used to 114 | * build the response to the A2S_RULES query and is generally known as 115 | * the list of public convars. 116 | * 117 | * @param key Name of the key to set, is created if it does not already 118 | * exist. 119 | * @param value Value of the key to set, the named key is removed if this 120 | * is blank. 121 | * 122 | * @noreturn 123 | */ 124 | native Steam_SetRule(const String:key[], const String:value[]); 125 | 126 | /** 127 | * Clears the server's list of rules. This list is used to build the 128 | * response to the A2S_RULES query and is generally known as the list of 129 | * public convars. 130 | * 131 | * @noreturn 132 | */ 133 | native Steam_ClearRules(); 134 | 135 | 136 | native Steam_ForceHeartbeat(); 137 | 138 | #pragma deprecated No longer operational 139 | native bool:Steam_AddMasterServer(const String:serverAddress[]); 140 | #pragma deprecated No longer operational 141 | native bool:Steam_RemoveMasterServer(const String:serverAddress[]); 142 | #pragma deprecated No longer operational 143 | native Steam_GetNumMasterServers(); 144 | #pragma deprecated No longer operational 145 | native Steam_GetMasterServerAddress(server, String:serverAddress[], maxlength); 146 | 147 | native Steam_SetGameDescription(String:gameDescription[]); 148 | 149 | native Steam_RequestStats(client); 150 | forward Steam_StatsReceived(client); 151 | forward Steam_StatsUnloaded(client); 152 | native Steam_GetStat(client, const String:statName[]); 153 | native Float:Steam_GetStatFloat(client, const String:statName[]); 154 | native bool:Steam_IsAchieved(client, const String:achievementName[]); 155 | 156 | 157 | native Steam_GetNumClientSubscriptions(client); 158 | native Steam_GetClientSubscription(client, index); 159 | 160 | native Steam_GetNumClientDLCs(client); 161 | native Steam_GetClientDLC(client, index); 162 | 163 | stock bool:Steam_CheckClientSubscription(client, subid) 164 | { 165 | new subCount = Steam_GetNumClientSubscriptions(client); 166 | for (new x = 0; x < subCount; x++) 167 | { 168 | if (Steam_GetClientSubscription(client, x) == subid) 169 | { 170 | return true; 171 | } 172 | } 173 | 174 | return false; 175 | } 176 | 177 | stock bool:Steam_CheckClientDLC(client, appid) 178 | { 179 | new subCount = Steam_GetNumClientDLCs(client); 180 | for (new x = 0; x < subCount; x++) 181 | { 182 | if (Steam_GetClientDLC(client, x) == appid) 183 | { 184 | return true; 185 | } 186 | } 187 | 188 | return false; 189 | } 190 | 191 | native Steam_GetCSteamIDForClient(client, String:steamID[], maxlength); 192 | 193 | native bool:Steam_SetCustomSteamID(const String:renderedID[]); 194 | native bool:Steam_GetCustomSteamID(String:renderedID[], maxlength); 195 | 196 | native Steam_RenderedIDToCSteamID(const String:renderedID[], String:steamID[], maxlength); 197 | native Steam_CSteamIDToRenderedID(const String:steamID[], String:renderedID[], maxlength); 198 | 199 | native Steam_GroupIDToCSteamID(groupID, String:steamID[], maxlength); 200 | native Steam_CSteamIDToGroupID(const String:steamID[]); 201 | 202 | enum HTTPRequestHandle 203 | { 204 | INVALID_HTTP_HANDLE = 0, 205 | }; 206 | 207 | enum HTTPMethod 208 | { 209 | HTTPMethod_Invalid = 0, 210 | HTTPMethod_GET, 211 | HTTPMethod_HEAD, 212 | HTTPMethod_POST, 213 | }; 214 | 215 | enum HTTPStatusCode 216 | { 217 | HTTPStatusCode_Invalid = 0, 218 | 219 | // Informational codes 220 | HTTPStatusCode_Continue = 100, 221 | HTTPStatusCode_SwitchingProtocols = 101, 222 | 223 | // Success codes 224 | HTTPStatusCode_OK = 200, 225 | HTTPStatusCode_Created = 201, 226 | HTTPStatusCode_Accepted = 202, 227 | HTTPStatusCode_NonAuthoritative = 203, 228 | HTTPStatusCode_NoContent = 204, 229 | HTTPStatusCode_ResetContent = 205, 230 | HTTPStatusCode_PartialContent = 206, 231 | 232 | // Redirection codes 233 | HTTPStatusCode_MultipleChoices = 300, 234 | HTTPStatusCode_MovedPermanently = 301, 235 | HTTPStatusCode_Found = 302, 236 | HTTPStatusCode_SeeOther = 303, 237 | HTTPStatusCode_NotModified = 304, 238 | HTTPStatusCode_UseProxy = 305, 239 | HTTPStatusCode_TemporaryRedirect = 307, 240 | 241 | // Error codes 242 | HTTPStatusCode_BadRequest = 400, 243 | HTTPStatusCode_Unauthorized = 401, 244 | HTTPStatusCode_PaymentRequired = 402, 245 | HTTPStatusCode_Forbidden = 403, 246 | HTTPStatusCode_NotFound = 404, 247 | HTTPStatusCode_MethodNotAllowed = 405, 248 | HTTPStatusCode_NotAcceptable = 406, 249 | HTTPStatusCode_ProxyAuthRequired = 407, 250 | HTTPStatusCode_RequestTimeout = 408, 251 | HTTPStatusCode_Conflict = 409, 252 | HTTPStatusCode_Gone = 410, 253 | HTTPStatusCode_LengthRequired = 411, 254 | HTTPStatusCode_PreconditionFailed = 412, 255 | HTTPStatusCode_RequestEntityTooLarge = 413, 256 | HTTPStatusCode_RequestURITooLong = 414, 257 | HTTPStatusCode_UnsupportedMediaType = 415, 258 | HTTPStatusCode_RequestedRangeNotSatisfiable = 416, 259 | HTTPStatusCode_ExpectationFailed = 417, 260 | 261 | // Server error codes 262 | HTTPStatusCode_InternalServerError = 500, 263 | HTTPStatusCode_NotImplemented = 501, 264 | HTTPStatusCode_BadGateway = 502, 265 | HTTPStatusCode_ServiceUnavailable = 503, 266 | HTTPStatusCode_GatewayTimeout = 504, 267 | HTTPStatusCode_HTTPVersionNotSupported = 505, 268 | }; 269 | 270 | funcenum HTTPRequestComplete 271 | { 272 | public(HTTPRequestHandle:HTTPRequest, bool:requestSuccessful, HTTPStatusCode:statusCode), 273 | public(HTTPRequestHandle:HTTPRequest, bool:requestSuccessful, HTTPStatusCode:statusCode, any:contextData), 274 | }; 275 | 276 | native HTTPRequestHandle:Steam_CreateHTTPRequest(HTTPMethod:HTTPRequestMethod, const String:absoluteURL[]); 277 | native Steam_SetHTTPRequestNetworkActivityTimeout(HTTPRequestHandle:HTTPRequest, timeoutSeconds); 278 | native Steam_SetHTTPRequestHeaderValue(HTTPRequestHandle:HTTPRequest, const String:headerName[], const String:headerValue[]); 279 | native Steam_SetHTTPRequestGetOrPostParameter(HTTPRequestHandle:HTTPRequest, const String:paramName[], const String:paramValue[]); 280 | native bool:Steam_SendHTTPRequest(HTTPRequestHandle:HTTPRequest, HTTPRequestComplete:callbackFunction, any:contextData = 0); 281 | native Steam_DeferHTTPRequest(HTTPRequestHandle:HTTPRequest); 282 | native Steam_PrioritizeHTTPRequest(HTTPRequestHandle:HTTPRequest); 283 | native Steam_GetHTTPResponseHeaderSize(HTTPRequestHandle:HTTPRequest, const String:headerName[]); 284 | native Steam_GetHTTPResponseHeaderValue(HTTPRequestHandle:HTTPRequest, const String:headerName[], String:headerValueBuffer[], bufferSize); 285 | native Steam_GetHTTPResponseBodySize(HTTPRequestHandle:HTTPRequest); 286 | native Steam_GetHTTPResponseBodyData(HTTPRequestHandle:HTTPRequest, String:bodyDataBuffer[], bufferSize); 287 | native Steam_WriteHTTPResponseBody(HTTPRequestHandle:HTTPRequest, const String:filePath[]); 288 | native Steam_ReleaseHTTPRequest(HTTPRequestHandle:HTTPRequest); 289 | native Float:Steam_GetHTTPDownloadProgressPercent(HTTPRequestHandle:HTTPRequest); 290 | native bool:Steam_SetHTTPRequestRawPostBody(HTTPRequestHandle:HTTPRequest, const String:data[], dataLength, const String:contentType[]="text/plain"); 291 | native bool:Steam_SetHTTPRequestRawPostBodyFile(HTTPRequestHandle:HTTPRequest, const String:filePath[], const String:contentType[]="text/plain"); 292 | 293 | #if !defined REQUIRE_EXTENSIONS 294 | public __ext_SteamTools_SetNTVOptional() 295 | { 296 | MarkNativeAsOptional("Steam_IsVACEnabled"); 297 | MarkNativeAsOptional("Steam_GetPublicIP"); 298 | MarkNativeAsOptional("Steam_RequestGroupStatus"); 299 | MarkNativeAsOptional("Steam_IsConnected"); 300 | MarkNativeAsOptional("Steam_SetRule"); 301 | MarkNativeAsOptional("Steam_ClearRules"); 302 | MarkNativeAsOptional("Steam_ForceHeartbeat"); 303 | MarkNativeAsOptional("Steam_SetGameDescription"); 304 | MarkNativeAsOptional("Steam_RequestStats"); 305 | MarkNativeAsOptional("Steam_GetStat"); 306 | MarkNativeAsOptional("Steam_GetStatFloat"); 307 | MarkNativeAsOptional("Steam_IsAchieved"); 308 | MarkNativeAsOptional("Steam_GetNumClientSubscriptions"); 309 | MarkNativeAsOptional("Steam_GetClientSubscription"); 310 | MarkNativeAsOptional("Steam_GetNumClientDLCs"); 311 | MarkNativeAsOptional("Steam_GetClientDLC"); 312 | MarkNativeAsOptional("Steam_GetCSteamIDForClient"); 313 | MarkNativeAsOptional("Steam_SetCustomSteamID"); 314 | MarkNativeAsOptional("Steam_GetCustomSteamID"); 315 | MarkNativeAsOptional("Steam_RenderedIDToCSteamID"); 316 | MarkNativeAsOptional("Steam_CSteamIDToRenderedID"); 317 | MarkNativeAsOptional("Steam_GroupIDToCSteamID"); 318 | MarkNativeAsOptional("Steam_CSteamIDToGroupID"); 319 | MarkNativeAsOptional("Steam_CreateHTTPRequest"); 320 | MarkNativeAsOptional("Steam_SetHTTPRequestNetworkActivityTimeout"); 321 | MarkNativeAsOptional("Steam_SetHTTPRequestHeaderValue"); 322 | MarkNativeAsOptional("Steam_SetHTTPRequestGetOrPostParameter"); 323 | MarkNativeAsOptional("Steam_SendHTTPRequest"); 324 | MarkNativeAsOptional("Steam_DeferHTTPRequest"); 325 | MarkNativeAsOptional("Steam_PrioritizeHTTPRequest"); 326 | MarkNativeAsOptional("Steam_GetHTTPResponseHeaderSize"); 327 | MarkNativeAsOptional("Steam_GetHTTPResponseHeaderValue"); 328 | MarkNativeAsOptional("Steam_GetHTTPResponseBodySize"); 329 | MarkNativeAsOptional("Steam_GetHTTPResponseBodyData"); 330 | MarkNativeAsOptional("Steam_WriteHTTPResponseBody"); 331 | MarkNativeAsOptional("Steam_ReleaseHTTPRequest"); 332 | MarkNativeAsOptional("Steam_GetHTTPDownloadProgressPercent"); 333 | MarkNativeAsOptional("Steam_SetHTTPRequestRawPostBody"); 334 | MarkNativeAsOptional("Steam_SetHTTPRequestRawPostBodyFile"); 335 | } 336 | #endif 337 | 338 | public Extension:__ext_SteamTools = 339 | { 340 | name = "SteamTools", 341 | file = "steamtools.ext", 342 | #if defined AUTOLOAD_EXTENSIONS 343 | autoload = 1, 344 | #else 345 | autoload = 0, 346 | #endif 347 | #if defined REQUIRE_EXTENSIONS 348 | required = 1, 349 | #else 350 | required = 0, 351 | #endif 352 | } 353 | -------------------------------------------------------------------------------- /plugin/steamtools.sp: -------------------------------------------------------------------------------- 1 | #pragma semicolon 1 2 | 3 | #include 4 | 5 | #define AUTOLOAD_EXTENSIONS 6 | #define REQUIRE_EXTENSIONS 7 | #include 8 | 9 | #define PLUGIN_VERSION "0.8.3" 10 | 11 | public Plugin:myinfo = { 12 | name = "SteamTools Tester", 13 | author = "Asher Baker (asherkin)", 14 | description = "Plugin for testing the SteamTools extension.", 15 | version = PLUGIN_VERSION, 16 | url = "http://limetech.org/" 17 | }; 18 | 19 | new ReplySource:Async_GroupStatus_Reply; 20 | new ReplySource:Async_ServerReputation_Reply; 21 | 22 | new Async_GroupStatus_Client; 23 | new Async_ServerReputation_Client; 24 | 25 | new bool:HaveStats[MAXPLAYERS+1]; 26 | 27 | public OnPluginStart() 28 | { 29 | LoadTranslations("common.phrases"); 30 | 31 | RegAdminCmd("sm_groupstatus", Command_GroupStatus, ADMFLAG_ROOT, "Requests a client's membership status in a Steam Community Group."); 32 | RegAdminCmd("sm_printserverreputation", Command_ServerReputation, ADMFLAG_ROOT, "Requests a server's reputation from the Steam Master Servers."); 33 | RegAdminCmd("sm_forceheartbeat", Command_Heartbeat, ADMFLAG_ROOT, "Sends a heartbeat to the Steam Master Servers."); 34 | RegAdminCmd("sm_printvacstatus", Command_VACStatus, ADMFLAG_ROOT, "Shows the current VAC status."); 35 | RegAdminCmd("sm_printconnectionstatus", Command_ConnectionStatus, ADMFLAG_ROOT, "Shows the current Steam connection status."); 36 | RegAdminCmd("sm_printip", Command_PrintIP, ADMFLAG_ROOT, "Shows the server's current external IP address."); 37 | 38 | RegAdminCmd("sm_setrule", Command_SetRule, ADMFLAG_ROOT, "Sets (and adds if missing) the value of an entry in the Master Server Rules response."); 39 | RegAdminCmd("sm_clearrules", Command_ClearRules, ADMFLAG_ROOT, "Removes all the entries in the Master Server Rules response."); 40 | 41 | RegAdminCmd("sm_setgamedescription", Command_SetGameDescription, ADMFLAG_ROOT, "Sets the game description shown in the server browser."); 42 | 43 | RegAdminCmd("sm_printstat", Command_PrintStat, ADMFLAG_ROOT, "Prints the value of a stat for a client."); 44 | RegAdminCmd("sm_printachievement", Command_PrintAchievement, ADMFLAG_ROOT, "Prints whether or not a client has earned an achievement."); 45 | 46 | RegAdminCmd("sm_printsubscription", Command_PrintSubscription, ADMFLAG_ROOT, "Shows the Subscription ID of the Steam Subscription that contains the client's game."); 47 | RegAdminCmd("sm_printdlc", Command_PrintDLC, ADMFLAG_ROOT, "Shows the App IDs of the DLCs that are associated with the client's game."); 48 | } 49 | 50 | public OnClientAuthorized(client, const String:auth[]) 51 | { 52 | if (!IsFakeClient(client)) 53 | { 54 | Steam_RequestStats(client); 55 | } 56 | } 57 | 58 | public OnClientDisconnect(client) 59 | { 60 | HaveStats[client] = false; 61 | } 62 | 63 | public Steam_StatsReceived(client) 64 | { 65 | HaveStats[client] = true; 66 | return; 67 | } 68 | 69 | public Steam_StatsUnloaded(client) 70 | { 71 | if (client == -1) // We'll get a Steam_StatsUnloaded after a client has left. 72 | return; 73 | 74 | HaveStats[client] = false; 75 | return; 76 | } 77 | 78 | public Action:Command_GroupStatus(client, args) 79 | { 80 | if (args != 2) 81 | { 82 | ReplyToCommand(client, "[SM] Usage: sm_groupstatus "); 83 | return Plugin_Handled; 84 | } 85 | 86 | new String:arg1[32]; 87 | new String:arg2[32]; 88 | 89 | GetCmdArg(1, arg1, sizeof(arg1)); 90 | GetCmdArg(2, arg2, sizeof(arg2)); 91 | 92 | new String:target_name[MAX_TARGET_LENGTH]; 93 | new target_list[MAXPLAYERS], target_count; 94 | new bool:tn_is_ml; 95 | 96 | if ((target_count = ProcessTargetString( 97 | arg1, 98 | client, 99 | target_list, 100 | MAXPLAYERS, 101 | COMMAND_FILTER_NO_IMMUNITY, 102 | target_name, 103 | sizeof(target_name), 104 | tn_is_ml)) <= 0) 105 | { 106 | ReplyToTargetError(client, target_count); 107 | return Plugin_Handled; 108 | } 109 | 110 | new bool:didLastRequestWork = false; 111 | 112 | for (new i = 0; i < target_count; i++) 113 | { 114 | didLastRequestWork = Steam_RequestGroupStatus(target_list[i], StringToInt(arg2)); 115 | } 116 | 117 | ReplyToCommand(client, "[SM] %s.", didLastRequestWork?"Group status requested":"Error in requesting group status, not connected to Steam"); 118 | 119 | Async_GroupStatus_Client = client; 120 | Async_GroupStatus_Reply = GetCmdReplySource(); 121 | 122 | return Plugin_Handled; 123 | } 124 | 125 | public Action:Command_ServerReputation(client, args) 126 | { 127 | Steam_RequestServerReputation(); 128 | ReplyToCommand(client, "[SM] Server Reputation Requested."); 129 | 130 | Async_ServerReputation_Client = client; 131 | Async_ServerReputation_Reply = GetCmdReplySource(); 132 | 133 | return Plugin_Handled; 134 | } 135 | 136 | public Action:Command_Heartbeat(client, args) 137 | { 138 | Steam_ForceHeartbeat(); 139 | ReplyToCommand(client, "[SM] Heartbeat Sent."); 140 | return Plugin_Handled; 141 | } 142 | 143 | public Action:Command_VACStatus(client, args) 144 | { 145 | ReplyToCommand(client, "[SM] VAC is %s.", Steam_IsVACEnabled()?"active":"not active"); 146 | return Plugin_Handled; 147 | } 148 | 149 | public Action:Command_ConnectionStatus(client, args) 150 | { 151 | ReplyToCommand(client, "[SM] %s to Steam servers.", Steam_IsConnected()?"Connected":"Not connected"); 152 | return Plugin_Handled; 153 | } 154 | 155 | public Action:Command_PrintIP(client, args) 156 | { 157 | new octets[4]; 158 | Steam_GetPublicIP(octets); 159 | ReplyToCommand(client, "[SM] Server IP Address: %d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); 160 | return Plugin_Handled; 161 | } 162 | 163 | public Action:Command_SetRule(client, args) 164 | { 165 | if (args != 2) 166 | { 167 | ReplyToCommand(client, "[SM] Usage: sm_setrule "); 168 | return Plugin_Handled; 169 | } 170 | 171 | new String:arg1[32]; 172 | new String:arg2[32]; 173 | 174 | GetCmdArg(1, arg1, sizeof(arg1)); 175 | GetCmdArg(2, arg2, sizeof(arg2)); 176 | 177 | Steam_SetRule(arg1, arg2); 178 | ReplyToCommand(client, "[SM] Rule Set."); 179 | 180 | return Plugin_Handled; 181 | } 182 | 183 | public Action:Command_ClearRules(client, args) 184 | { 185 | Steam_ClearRules(); 186 | ReplyToCommand(client, "[SM] Rules Cleared."); 187 | 188 | return Plugin_Handled; 189 | } 190 | 191 | public Action:Command_SetGameDescription(client, args) 192 | { 193 | if (args != 1) 194 | { 195 | ReplyToCommand(client, "[SM] Usage: sm_setgamedescription "); 196 | return Plugin_Handled; 197 | } 198 | 199 | new String:arg1[32]; 200 | 201 | GetCmdArg(1, arg1, sizeof(arg1)); 202 | 203 | Steam_SetGameDescription(arg1); 204 | ReplyToCommand(client, "[SM] Game Description Set."); 205 | 206 | return Plugin_Handled; 207 | } 208 | 209 | public Action:Command_PrintStat(client, args) 210 | { 211 | if (args != 2) 212 | { 213 | ReplyToCommand(client, "[SM] Usage: sm_printstat "); 214 | return Plugin_Handled; 215 | } 216 | 217 | new String:arg1[32]; 218 | new String:arg2[32]; 219 | 220 | GetCmdArg(1, arg1, sizeof(arg1)); 221 | GetCmdArg(2, arg2, sizeof(arg2)); 222 | 223 | new String:target_name[MAX_TARGET_LENGTH]; 224 | new target_list[MAXPLAYERS], target_count; 225 | new bool:tn_is_ml; 226 | 227 | if ((target_count = ProcessTargetString( 228 | arg1, 229 | client, 230 | target_list, 231 | MAXPLAYERS, 232 | COMMAND_FILTER_NO_IMMUNITY, 233 | target_name, 234 | sizeof(target_name), 235 | tn_is_ml)) <= 0) 236 | { 237 | ReplyToTargetError(client, target_count); 238 | return Plugin_Handled; 239 | } 240 | 241 | for (new i = 0; i < target_count; i++) 242 | { 243 | if (HaveStats[client]) 244 | ReplyToCommand(client, "[SM] Stat '%s' = %d for %N.", arg2, Steam_GetStat(target_list[i], arg2), target_list[i]); 245 | else 246 | ReplyToCommand(client, "[SM] Stats for %N not received yet.", target_list[i]); 247 | } 248 | 249 | return Plugin_Handled; 250 | } 251 | 252 | public Action:Command_PrintAchievement(client, args) 253 | { 254 | if (args != 2) 255 | { 256 | ReplyToCommand(client, "[SM] Usage: sm_printachievement "); 257 | return Plugin_Handled; 258 | } 259 | 260 | new String:arg1[32]; 261 | new String:arg2[32]; 262 | 263 | GetCmdArg(1, arg1, sizeof(arg1)); 264 | GetCmdArg(2, arg2, sizeof(arg2)); 265 | 266 | new String:target_name[MAX_TARGET_LENGTH]; 267 | new target_list[MAXPLAYERS], target_count; 268 | new bool:tn_is_ml; 269 | 270 | if ((target_count = ProcessTargetString( 271 | arg1, 272 | client, 273 | target_list, 274 | MAXPLAYERS, 275 | COMMAND_FILTER_NO_IMMUNITY, 276 | target_name, 277 | sizeof(target_name), 278 | tn_is_ml)) <= 0) 279 | { 280 | ReplyToTargetError(client, target_count); 281 | return Plugin_Handled; 282 | } 283 | 284 | for (new i = 0; i < target_count; i++) 285 | { 286 | if (HaveStats[client]) 287 | ReplyToCommand(client, "[SM] %N %s earned achievement %s.", target_list[i], Steam_IsAchieved(target_list[i], arg2)?"has":"has not", arg2); 288 | else 289 | ReplyToCommand(client, "[SM] Stats for %N not received yet.", target_list[i]); 290 | } 291 | 292 | return Plugin_Handled; 293 | } 294 | 295 | public Action:Command_PrintSubscription(client, args) 296 | { 297 | if (args != 1) 298 | { 299 | ReplyToCommand(client, "[SM] Usage: sm_printsubscription "); 300 | return Plugin_Handled; 301 | } 302 | 303 | new String:arg1[32]; 304 | new String:arg2[32]; 305 | 306 | GetCmdArg(1, arg1, sizeof(arg1)); 307 | GetCmdArg(2, arg2, sizeof(arg2)); 308 | 309 | new String:target_name[MAX_TARGET_LENGTH]; 310 | new target_list[MAXPLAYERS], target_count; 311 | new bool:tn_is_ml; 312 | 313 | if ((target_count = ProcessTargetString( 314 | arg1, 315 | client, 316 | target_list, 317 | MAXPLAYERS, 318 | COMMAND_FILTER_NO_IMMUNITY, 319 | target_name, 320 | sizeof(target_name), 321 | tn_is_ml)) <= 0) 322 | { 323 | ReplyToTargetError(client, target_count); 324 | return Plugin_Handled; 325 | } 326 | 327 | for (new i = 0; i < target_count; i++) 328 | { 329 | new subCount = Steam_GetNumClientSubscriptions(target_list[i]); 330 | for (new x = 0; x < subCount; x++) 331 | { 332 | ReplyToCommand(client, "[SM] Client purchased this game as part of subscription %d.", Steam_GetClientSubscription(target_list[i], x)); 333 | } 334 | } 335 | 336 | return Plugin_Handled; 337 | } 338 | 339 | public Action:Command_PrintDLC(client, args) 340 | { 341 | if (args != 1) 342 | { 343 | ReplyToCommand(client, "[SM] Usage: sm_printdlc "); 344 | return Plugin_Handled; 345 | } 346 | 347 | new String:arg1[32]; 348 | new String:arg2[32]; 349 | 350 | GetCmdArg(1, arg1, sizeof(arg1)); 351 | GetCmdArg(2, arg2, sizeof(arg2)); 352 | 353 | new String:target_name[MAX_TARGET_LENGTH]; 354 | new target_list[MAXPLAYERS], target_count; 355 | new bool:tn_is_ml; 356 | 357 | if ((target_count = ProcessTargetString( 358 | arg1, 359 | client, 360 | target_list, 361 | MAXPLAYERS, 362 | COMMAND_FILTER_NO_IMMUNITY, 363 | target_name, 364 | sizeof(target_name), 365 | tn_is_ml)) <= 0) 366 | { 367 | ReplyToTargetError(client, target_count); 368 | return Plugin_Handled; 369 | } 370 | 371 | for (new i = 0; i < target_count; i++) 372 | { 373 | new subCount = Steam_GetNumClientDLCs(target_list[i]); 374 | for (new x = 0; x < subCount; x++) 375 | { 376 | ReplyToCommand(client, "[SM] Client has DLC %d.", Steam_GetClientDLC(target_list[i], x)); 377 | } 378 | } 379 | 380 | return Plugin_Handled; 381 | } 382 | 383 | public Steam_GroupStatusResult(client, groupAccountID, bool:groupMember, bool:groupOfficer) 384 | { 385 | SetCmdReplySource(Async_GroupStatus_Reply); 386 | ReplyToCommand(Async_GroupStatus_Client, "[SM] %N is %s in group %d.", client, groupMember?(groupOfficer?"an officer":"a member"):"not a member", groupAccountID); 387 | Async_GroupStatus_Reply = SM_REPLY_TO_CONSOLE; 388 | Async_GroupStatus_Client = 0; 389 | return; 390 | } 391 | 392 | public Steam_Reputation(reputationScore, bool:banned, bannedIP, bannedPort, bannedGameID, banExpires) 393 | { 394 | SetCmdReplySource(Async_ServerReputation_Reply); 395 | ReplyToCommand(Async_ServerReputation_Client, "[SM] Reputation Score: %d. Banned: %s.", reputationScore, banned?"true":"false"); 396 | Async_ServerReputation_Reply = SM_REPLY_TO_CONSOLE; 397 | Async_ServerReputation_Client = 0; 398 | return; 399 | } 400 | 401 | public Action:Steam_RestartRequested() 402 | { 403 | PrintToServer("[SM] Server needs to be restarted due to an update."); 404 | return Plugin_Continue; 405 | } 406 | 407 | public Steam_SteamServersConnected() 408 | { 409 | PrintToChatAll("[SM] Connection to Steam servers established."); 410 | return; 411 | } 412 | 413 | public Steam_SteamServersDisconnected() 414 | { 415 | PrintToChatAll("[SM] Lost connection to Steam servers."); 416 | return; 417 | } 418 | -------------------------------------------------------------------------------- /product.version: -------------------------------------------------------------------------------- 1 | 0.10.0 2 | -------------------------------------------------------------------------------- /upload.py: -------------------------------------------------------------------------------- 1 | import re, os, sys 2 | import subprocess 3 | 4 | import zipfile 5 | import ftplib 6 | 7 | platform = 'unknown' 8 | if sys.platform.startswith('linux'): 9 | platform = 'linux' 10 | elif sys.platform.startswith('win32'): 11 | platform = 'windows' 12 | elif sys.platform.startswith('darwin'): 13 | platform = 'mac' 14 | 15 | def GITHash(): 16 | p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) 17 | (stdout, stderr) = p.communicate() 18 | stdout = stdout.decode('UTF-8') 19 | return stdout.rstrip('\r\n') 20 | 21 | def GITVersion(): 22 | p = subprocess.Popen(['git', 'rev-list', '--count', '--first-parent', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE) 23 | (stdout, stderr) = p.communicate() 24 | stdout = stdout.decode('UTF-8') 25 | return stdout.rstrip('\r\n') 26 | 27 | def ReleaseVersion(): 28 | productFile = open('product.version', 'r') 29 | productContents = productFile.read() 30 | productFile.close() 31 | 32 | m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents) 33 | if m == None: 34 | raise Exception('Could not detremine product version') 35 | 36 | major, minor, release, tag = m.groups() 37 | return '.'.join([major, minor, release]) 38 | 39 | filename = '-'.join(['steamtools', ReleaseVersion(), 'git' + GITVersion(), GITHash(), platform]) 40 | 41 | debug_build = os.environ.get('is_debug_build', False) == "1" 42 | 43 | if debug_build: 44 | filename += '-debug' 45 | 46 | filename += '.zip' 47 | 48 | zip = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED) 49 | 50 | for base, dirs, files in os.walk('package'): 51 | for file in files: 52 | fn = os.path.join(base, file) 53 | fns = fn[(len('package') + 1):] 54 | 55 | zip.write(fn, fns) 56 | 57 | print("%-33s %-10s %21s %12s" % ("File Name", "CRC32", "Modified ", "Size")) 58 | for zinfo in zip.infolist(): 59 | date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6] 60 | print("%-33s %-10d %21s %12d" % (zinfo.filename, zinfo.CRC, date, zinfo.file_size)) 61 | 62 | zip.close() 63 | 64 | if 'ftp_hostname' in os.environ: 65 | print('') 66 | 67 | ftp = ftplib.FTP(os.environ['ftp_hostname'], os.environ['ftp_username'], os.environ['ftp_password']) 68 | print('Connected to server, uploading build...') 69 | ftp.cwd(os.environ['ftp_directory']) 70 | print(ftp.storbinary('STOR ' + filename, open(filename, 'rb'))) 71 | ftp.quit() 72 | 73 | print('Uploaded as \'' + filename + '\'') 74 | 75 | os.unlink(filename) 76 | 77 | --------------------------------------------------------------------------------