├── .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 |
--------------------------------------------------------------------------------