diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c2bab2..d812caf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,81 +1,147 @@ -name: CI +name: Extension builder -on: [push, pull_request, workflow_dispatch] +on: + push: + branches: + - master + tags: + - "*" + pull_request: + branches: + - master jobs: build: - name: Build - runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: os: [ubuntu-20.04] - sourcemod-version: [1.11-dev] include: - os: ubuntu-20.04 - target-archs: x86,x86_64 - sdks: sdk2013,csgo + cc: clang-10 + cxx: clang++-10 + fail-fast: false + name: ${{ matrix.os }} - ${{ matrix.cc }} + runs-on: ${{ matrix.os }} + + env: + PROJECT: 'a2sqcache' + SDKS: 'css' + MMSOURCE_VERSION: '1.12' + SOURCEMOD_VERSION: '1.12' + CACHE_PATH: ${{ github.workspace }}/cache steps: - - name: Install Linux packages - if: runner.os == 'Linux' - run: | - sudo apt update - sudo apt install -yq --no-install-recommends g++-multilib - - - name: Set up Python - uses: actions/setup-python@v5 - - - name: Checkout - uses: actions/checkout@v4 - with: - path: extension - - - name: Checkout SourceMod - uses: actions/checkout@v4 - with: - repository: alliedmodders/sourcemod - ref: ${{ matrix.sourcemod-version }} - path: sourcemod - submodules: recursive - - - name: Checkout AMBuild - uses: actions/checkout@v4 - with: - repository: alliedmodders/ambuild - path: ambuild - - - name: Checkout sm-ext-common - uses: actions/checkout@v4 - with: - repository: srcdslab/sm-ext-common - path: sourcemod/extensions/sm-ext-common - - - name: Install sourcemod dependencies - run: | - bash sourcemod/tools/checkout-deps.sh -m -s ${{ matrix.sdks }} - - - name: Install AMBuild - uses: BSFishy/pip-action@v1 - with: - packages: ./ambuild - - - name: Build - working-directory: extension + - name: Concatenate SDK Names shell: bash - env: - BREAKPAD_SYMBOL_SERVER: ${{ secrets.BREAKPAD_SYMBOL_SERVER }} run: | - mkdir build && cd build - python ../configure.py --enable-optimize --targets=${{ matrix.target-archs }} --sdks=${{ matrix.sdks }} + # Paranoia + SDKS_VAR="${{env.SDKS}}" + # This will be used in our cache key + echo "SDKS_KEY=${SDKS_VAR//[[:blank:]]/}" >> $GITHUB_ENV + + - name: Linux dependencies + if: startsWith(runner.os, 'Linux') + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \ + libc6-dev libc6-dev-i386 linux-libc-dev \ + linux-libc-dev:i386 lib32z1-dev ${{ matrix.cc }} + + - uses: actions/setup-python@v4 + name: Setup Python 3.9 + with: + python-version: 3.9 + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip setuptools wheel + + - uses: actions/checkout@v3 + name: Repository checkout + with: + fetch-depth: 0 + path: extension + + - uses: actions/cache@v3 + name: Cache dependencies + if: ${{ ! startsWith(github.ref, 'refs/tags/') }} + env: + cache-name: cache + with: + path: ${{ env.CACHE_PATH }} + key: ${{ runner.os }}-build-${{ env.cache-name }}-sm${{ env.SOURCEMOD_VERSION }}-mmsource${{ env.MMSOURCE_VERSION }}-${{ env.SDKS_KEY }} + + - shell: bash + name: Install dependencies + run: | + mkdir -p "${{ env.CACHE_PATH }}" + cd "${{ env.CACHE_PATH }}" + shallow_checkout () { + # Param 1 is origin + # Param 2 is branch + # Param 3 is name + if [ ! -d "$3" ]; then + git clone "$1" --depth 1 --branch "$2" "$3" + fi + cd "$3" + git remote set-url origin "$1" + git fetch --depth 1 origin "$2" + git checkout --force --recurse-submodules FETCH_HEAD + git submodule init + git submodule update --depth 1 + cd .. + } + # We are aware of what we are doing! + git config --global advice.detachedHead false + # Verify github cache, and see if we don't have the sdks already cloned and update them + for sdk in ${{ env.SDKS }} + do + shallow_checkout "https://github.com/alliedmodders/hl2sdk" "${sdk}" "hl2sdk-${sdk}" + done + shallow_checkout "https://github.com/alliedmodders/ambuild" "master" "ambuild" + shallow_checkout "https://github.com/alliedmodders/sourcemod" "${{env.SOURCEMOD_VERSION}}-dev" "sourcemod" + shallow_checkout "https://github.com/alliedmodders/metamod-source/" "${{env.MMSOURCE_VERSION}}-dev" "metamod-source" + shallow_checkout "https://github.com/srcdslab/sm-ext-common" "master" "sourcemod/public/sm-ext-common" + # But maybe others aren't (also probably unnecessary because git actions but paranoia) + git config --global advice.detachedHead true + + - name: Setup AMBuild + shell: bash + run: | + cd "${{ env.CACHE_PATH }}" + python -m pip install ./ambuild + + - name: Select clang compiler + if: startsWith(runner.os, 'Linux') + run: | + echo "CC=${{ matrix.cc }}" >> $GITHUB_ENV + echo "CXX=${{ matrix.cxx }}" >> $GITHUB_ENV + ${{ matrix.cc }} --version + ${{ matrix.cxx }} --version + + - name: Build + shell: bash + working-directory: extension + run: | + mkdir build + cd build + python ../configure.py --enable-optimize --sdks="${{ env.SDKS }}" --mms-path="${{ env.CACHE_PATH }}/metamod-source" --hl2sdk-root="${{ env.CACHE_PATH }}" --sm-path="${{ env.CACHE_PATH }}/sourcemod" --targets=x86,x86_64 ambuild - - name: Upload artifact + - name: Upload a Build Artifact uses: actions/upload-artifact@v4 with: + # Artifact name name: ${{ runner.os }} + # optional, default is artifact + # A file, directory or wildcard pattern that describes what to upload path: extension/build/package - + # The desired behavior if no files are found using the provided path. + if-no-files-found: error + # Duration after which artifact will expire in days. 0 means using default retention. + retention-days: 14 release: name: Release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8b58b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build +Containerfile +.venv +safetyhook \ No newline at end of file diff --git a/AMBuildScript b/AMBuildScript index 6829f74..88fe901 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -1,6 +1,5 @@ # vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: -import os, sys -import traceback +import os, sys, shutil class SDK(object): def __init__(self, sdk, ext, aDef, name, platform, dir): @@ -27,13 +26,49 @@ class SDK(object): return True return False -WinOnly = ['windows'] WinLinux = ['windows', 'linux'] -WinLinuxMac = ['windows', 'linux', 'mac'] +CSS = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86', 'x86_64'] +} +HL2DM = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86', 'x86_64'] +} +DODS = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86', 'x86_64'] +} +TF2 = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86', 'x86_64'] +} +CSGO = { + 'windows': ['x86'], + 'linux': ['x86', 'x86_64'] +} PossibleSDKs = { - 'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'), - 'csgo': SDK('HL2SDKCSGO', '2.csgo', '21', 'CSGO', WinLinuxMac, 'csgo'), + # 'episode1': SDK('HL2SDK', '1.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'), + # 'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'), + # 'orangebox': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'), + # 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'), + # 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'), + 'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', CSS, 'css'), + # 'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', HL2DM, 'hl2dm'), + # 'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', DODS, 'dods'), + # 'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinux, 'sdk2013'), + # 'bms': SDK('HL2SDKBMS', '2.bms', '11', 'BMS', WinLinux, 'bms'), + # 'tf2': SDK('HL2SDKTF2', '2.tf2', '12', 'TF2', TF2, 'tf2'), + # 'l4d': SDK('HL2SDKL4D', '2.l4d', '13', 'LEFT4DEAD', WinLinux, 'l4d'), + # 'nucleardawn': SDK('HL2SDKND', '2.nd', '14', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'), + # 'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '15', 'CONTAGION', WinOnly, 'contagion'), + # 'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '16', 'LEFT4DEAD2', WinLinux, 'l4d2'), + # 'swarm': SDK('HL2SDK-SWARM', '2.swarm', '17', 'ALIENSWARM', WinOnly, 'swarm'), + # 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '18', 'PORTAL2', [], 'portal2'), + # 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'), + # 'blade': SDK('HL2SDKBLADE', '2.blade', '21', 'BLADE', WinLinux, 'blade'), + 'csgo': SDK('HL2SDKCSGO', '2.csgo', '23', 'CSGO', CSGO, 'csgo'), } def ResolveEnvPath(env, folder): @@ -56,7 +91,7 @@ def ResolveEnvPath(env, folder): def Normalize(path): return os.path.abspath(os.path.normpath(path)) - + def SetArchFlags(compiler): if compiler.behavior == 'gcc': if compiler.target.arch == 'x86_64': @@ -69,19 +104,13 @@ class ExtensionConfig(object): def __init__(self): self.sdks = {} self.binaries = [] - self.spvm = [] self.extensions = [] self.generated_headers = None - self.sm_root = None self.mms_root = None - self.mysql_root = {} - self.spcomp = None - self.spcomp_bins = None - self.smx_files = {} - self.versionlib = None + self.sm_root = None self.all_targets = [] self.target_archs = set() - + self.libsafetyhook = {} if builder.options.targets: target_archs = builder.options.targets.split(',') @@ -105,6 +134,26 @@ class ExtensionConfig(object): if not self.all_targets: raise Exception('No suitable C/C++ compiler was found.') + def use_auto_versioning(self): + return not getattr(builder.options, 'disable_auto_versioning', True) + + def AddVersioning(self, binary): + if binary.compiler.target.platform == 'windows': + binary.sources += ['version.rc'] + binary.compiler.rcdefines += [ + 'BINARY_NAME="{0}"'.format(binary.outputFile), + 'RC_COMPILE', + ] + elif binary.compiler.target.platform == 'mac': + if binary.type == 'library': + binary.compiler.postlink += [ + '-compatibility_version', '1.0.0', + '-current_version', self.productVersion + ] + if self.use_auto_versioning(): + binary.compiler.sourcedeps += self.generated_headers + return binary + @property def tag(self): if builder.options.debug == '1': @@ -118,7 +167,7 @@ class ExtensionConfig(object): import re with open(os.path.join(builder.sourcePath, 'product.version'), 'r') as fp: productContents = fp.read() - m = re.match('(\d+)\.(\d+)\.(\d+).*', productContents) + m = re.match('(\\d+)\.(\\d+)\.(\\d+).*', productContents) if m == None: self.productVersion = '1.0.0' else: @@ -126,7 +175,7 @@ class ExtensionConfig(object): self.productVersion = '{0}.{1}.{2}'.format(major, minor, release) def detectSDKs(self): - sdk_list = builder.options.sdks.split(',') + sdk_list = builder.options.sdks.split(' ') use_none = sdk_list[0] == 'none' use_all = sdk_list[0] == 'all' use_present = sdk_list[0] == 'present' @@ -152,28 +201,24 @@ class ExtensionConfig(object): if builder.options.sm_path: self.sm_root = builder.options.sm_path else: - self.sm_root = ResolveEnvPath('SOURCEMOD18', 'sourcemod-1.10') - if not self.sm_root: - self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod-source') - if not self.sm_root: - self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central') + self.sm_root = ResolveEnvPath('SOURCEMOD112', 'sourcemod-1.12') if not self.sm_root: self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod') + if not self.sm_root: + self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central') if not self.sm_root or not os.path.isdir(self.sm_root): - raise Exception('Could not find a source copy of Sourcemod') + raise Exception('Could not find a source copy of SourceMod') self.sm_root = Normalize(self.sm_root) if builder.options.mms_path: self.mms_root = builder.options.mms_path else: - self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10') + self.mms_root = ResolveEnvPath('MMSOURCE112', 'mmsource-1.12') if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod-source') if not self.mms_root: self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'mmsource-central') - if not self.mms_root: - self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'metamod') if not self.mms_root or not os.path.isdir(self.mms_root): raise Exception('Could not find a source copy of Metamod:Source') @@ -188,14 +233,14 @@ class ExtensionConfig(object): def configure_cxx(self, cxx): if cxx.family == 'msvc': - if cxx.version < 1900: - raise Exception('Only MSVC 2015 and later are supported, c++14 support is required.') - if cxx.family == 'gcc': - if cxx.version < 'gcc-4.9': - raise Exception('Only GCC versions 4.9 or greater are supported, c++14 support is required.') - if cxx.family == 'clang': - if cxx.version < 'clang-3.4': - raise Exception('Only clang versions 3.4 or greater are supported, c++14 support is required.') + if cxx.version < 1914 and builder.options.generator != 'vs': + raise Exception(f'Only MSVC 2017 15.7 and later are supported, full C++17 support is required. ({str(cxx.version)} < 1914)') + elif cxx.family == 'gcc': + if cxx.version < 'gcc-9': + raise Exception('Only GCC versions 9 or later are supported, full C++17 support is required.') + elif cxx.family == 'clang': + if cxx.version < 'clang-5': + raise Exception('Only clang versions 5 or later are supported, full C++17 support is required.') if cxx.like('gcc'): self.configure_gcc(cxx) @@ -223,6 +268,12 @@ class ExtensionConfig(object): os.path.join(self.sm_root, 'public'), ] + if self.use_auto_versioning(): + cxx.defines += ['SM_GENERATED_BUILD'] + cxx.includes += [ + os.path.join(builder.buildPath, 'includes') + ] + def configure_gcc(self, cxx): cxx.defines += [ 'stricmp=strcasecmp', @@ -240,19 +291,19 @@ class ExtensionConfig(object): '-Wno-unused', '-Wno-switch', '-Wno-array-bounds', - '-msse', + '-Wno-unknown-pragmas', + '-Wno-dangling-else', '-fvisibility=hidden', ] - - if cxx.version == 'apple-clang-6.0' or cxx.version == 'clang-3.4': - cxx.cxxflags += ['-std=c++1y'] - else: - cxx.cxxflags += ['-std=c++14'] + if cxx.target.arch in ['x86', 'x86_64']: + cxx.cflags += ['-msse'] cxx.cxxflags += [ + '-std=c++17', '-fno-threadsafe-statics', '-Wno-non-virtual-dtor', '-Wno-overloaded-virtual', + '-Wno-register', '-fvisibility-inlines-hidden', ] @@ -283,26 +334,18 @@ class ExtensionConfig(object): cxx.cflags += ['-Wno-sometimes-uninitialized'] # Work around SDK warnings. - if cxx.version >= 'clang-10.0': - cxx.cflags += [ - '-Wno-implicit-int-float-conversion', - '-Wno-tautological-overlap-compare', - ] + if cxx.version >= 'clang-10.0' or cxx.version >= 'apple-clang-12.0': + cxx.cflags += [ + '-Wno-implicit-int-float-conversion', + '-Wno-tautological-overlap-compare', + ] if have_gcc: cxx.cflags += ['-mfpmath=sse'] cxx.cflags += ['-Wno-maybe-uninitialized'] if builder.options.opt == '1': - cxx.cflags += [ - '-O3', - '-fexperimental-new-pass-manager', - '-mllvm', - '-inline-threshold=1000', - '-mllvm', - '-vectorize-loops', - '-ftree-vectorize', - ] + cxx.cflags += ['-O3'] # Don't omit the frame pointer. cxx.cflags += ['-fno-omit-frame-pointer'] @@ -326,6 +369,7 @@ class ExtensionConfig(object): '/EHsc', '/GR-', '/TP', + '/std:c++17', ] cxx.linkflags += [ 'kernel32.lib', @@ -354,13 +398,12 @@ class ExtensionConfig(object): cxx.cflags += ['/Oy-'] def configure_linux(self, cxx): - cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] + cxx.defines += ['LINUX', '_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] cxx.linkflags += ['-lm'] if cxx.family == 'gcc': cxx.linkflags += ['-static-libgcc'] elif cxx.family == 'clang': cxx.linkflags += ['-lgcc_eh'] - cxx.linkflags += ['-static-libstdc++'] def configure_mac(self, cxx): cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL'] @@ -373,43 +416,16 @@ class ExtensionConfig(object): cxx.cxxflags += ['-stdlib=libc++'] def configure_windows(self, cxx): - cxx.defines += ['WIN32', '_WINDOWS'] - - def add_libamtl(self): - # Add libamtl. - self.libamtl = {} - for cxx in self.all_targets: - def get_configure_fn(cxx): - return lambda builder, name: self.StaticLibrary(builder, cxx, name) - extra_vars = {'Configure': get_configure_fn(cxx)} - libamtl = builder.Build('public/amtl/amtl/AMBuilder', extra_vars) - self.libamtl[cxx.target.arch] = libamtl.binary - - def AddVersioning(self, binary): - if binary.compiler.target.platform == 'windows': - binary.sources += ['version.rc'] - binary.compiler.rcdefines += [ - 'BINARY_NAME="{0}"'.format(binary.outputFile), - 'RC_COMPILE', - ] - elif binary.compiler.target.platform == 'mac': - if binary.type == 'library': - binary.compiler.postlink += [ - '-compatibility_version', '1.0.0', - '-current_version', self.productVersion - ] - return binary + cxx.defines += ['WIN32', '_WINDOWS', 'PLATFORM_WINDOWS_PC'] def LibraryBuilder(self, compiler, name): binary = compiler.Library(name) - self.AddVersioning(binary) if binary.compiler.like('msvc'): binary.compiler.linkflags += ['/SUBSYSTEM:WINDOWS'] return binary def ProgramBuilder(self, compiler, name): binary = compiler.Program(name) - self.AddVersioning(binary) if '-static-libgcc' in binary.compiler.linkflags: binary.compiler.linkflags.remove('-static-libgcc') if '-lgcc_eh' in binary.compiler.linkflags: @@ -444,10 +460,10 @@ class ExtensionConfig(object): os.path.join(context.currentSourcePath, 'sdk'), os.path.join(self.sm_root, 'public'), os.path.join(self.sm_root, 'public', 'extensions'), - os.path.join(self.sm_root, 'sourcepawn', 'include'), os.path.join(self.sm_root, 'public', 'amtl', 'amtl'), os.path.join(self.sm_root, 'public', 'amtl'), - os.path.join(self.sm_root, 'extensions', 'sm-ext-common', 'include'), + os.path.join(self.sm_root, 'sourcepawn', 'include'), + os.path.join(self.sm_root, 'public', 'sm-ext-common', 'include'), ] return compiler @@ -466,7 +482,8 @@ class ExtensionConfig(object): os.path.join(self.mms_root, 'core', 'sourcehook'), ] - defines = ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs] + defines = ['RAD_TELEMETRY_DISABLED'] + defines += ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs] compiler.defines += defines paths = [ @@ -475,8 +492,7 @@ class ExtensionConfig(object): ['public', 'mathlib'], ['public', 'vstdlib'], ['public', 'tier0'], - ['public', 'tier1'], - ['public', 'appframework'] + ['public', 'tier1'] ] if sdk.name == 'episode1' or sdk.name == 'darkm': paths.append(['public', 'dlls']) @@ -489,7 +505,7 @@ class ExtensionConfig(object): compiler.defines += ['SOURCE_ENGINE=' + sdk.code] - if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'): + if sdk.name in ['sdk2013', 'bms', 'css', 'tf2', 'dods', 'hl2dm'] and compiler.like('gcc'): # The 2013 SDK already has these in public/tier0/basetypes.h compiler.defines.remove('stricmp=strcasecmp') compiler.defines.remove('_stricmp=strcasecmp') @@ -520,7 +536,6 @@ class ExtensionConfig(object): if compiler.target.platform == 'linux': if sdk.name in ['csgo', 'blade']: - compiler.linkflags.remove('-static-libstdc++') compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0'] for path in paths: @@ -532,16 +547,16 @@ class ExtensionConfig(object): elif sdk.name in ['sdk2013', 'bms']: lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32') elif compiler.target.arch == 'x86_64': - lib_folder = os.path.join(sdk.path, 'lib', 'linux64') + lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux64') else: - lib_folder = os.path.join(sdk.path, 'lib', 'linux') + lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux') elif compiler.target.platform == 'mac': if sdk.name in ['sdk2013', 'bms']: lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32') elif compiler.target.arch == 'x86_64': - lib_folder = os.path.join(sdk.path, 'lib', 'osx64') + lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx64') else: - lib_folder = os.path.join(sdk.path, 'lib', 'mac') + lib_folder = os.path.join(sdk.path, 'lib', 'public', 'mac') if compiler.target.platform in ['linux', 'mac']: if sdk.name in ['sdk2013', 'bms'] or compiler.target.arch == 'x86_64': @@ -580,9 +595,9 @@ class ExtensionConfig(object): libs.append('interfaces') for lib in libs: if compiler.target.arch == 'x86': - lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib' + lib_path = os.path.join(sdk.path, 'lib', 'public', 'x86', lib) + '.lib' elif compiler.target.arch == 'x86_64': - lib_path = os.path.join(sdk.path, 'lib', 'public', 'win64', lib) + '.lib' + lib_path = os.path.join(sdk.path, 'lib', 'public', 'x64', lib) + '.lib' compiler.linkflags.append(lib_path) for library in dynamic_libs: @@ -598,16 +613,41 @@ class ExtensionConfig(object): return binary + def AddCDetour(self, binary): + sm_public_path = os.path.join(self.sm_root, 'public') + + if os.path.exists(os.path.join(sm_public_path, 'safetyhook')): + binary.sources += [ os.path.join(sm_public_path, 'CDetour', 'detours.cpp') ] + binary.compiler.cxxincludes += [ os.path.join(builder.sourcePath, 'safetyhook', 'include') ] + + for task in self.libsafetyhook: + if task.target.arch == binary.compiler.target.arch: + binary.compiler.linkflags += [task.binary] + return + raise Exception('No suitable build of safetyhook was found.') + else: + binary.sources += [ + os.path.join(sm_public_path, 'CDetour', 'detours.cpp'), + os.path.join(sm_public_path, 'asm', 'asm.c'), + ] + # sm1.10+ + libudis_folder = os.path.join(sm_public_path, 'libudis86') + if os.path.isdir(libudis_folder): + binary.compiler.defines += ['HAVE_STRING_H'] + binary.sources += [ + os.path.join(libudis_folder, 'decode.c'), + os.path.join(libudis_folder, 'itab.c'), + os.path.join(libudis_folder, 'syn-att.c'), + os.path.join(libudis_folder, 'syn-intel.c'), + os.path.join(libudis_folder, 'syn.c'), + os.path.join(libudis_folder, 'udis86.c'), + ] + def HL2Library(self, context, compiler, name, sdk): binary = self.Library(context, compiler, name) self.ConfigureForExtension(context, binary.compiler) return self.ConfigureForHL2(context, binary, sdk) - def HL2Project(self, context, compiler, name): - project = context.LibraryProject(name) - self.ConfigureForExtension(context, compiler) - return project - def HL2Config(self, project, context, compiler, name, sdk): binary = project.Configure(compiler, name, '{0} - {1} {2}'.format(self.tag, sdk.name, compiler.target.arch)) @@ -622,24 +662,43 @@ class ExtensionConfig(object): self.ConfigureForExtension(context, binary.compiler) return binary +class SafetyHookShim(object): + def __init__(self): + self.all_targets = {} + self.libsafetyhook = {} + if getattr(builder, 'target', None) is not None: - sys.stderr.write("Your output folder was configured for AMBuild 2.1, and SourceMod is now\n") - sys.stderr.write("configured to use AMBuild 2.2. Please remove your output folder and\n") - sys.stderr.write("reconfigure to continue.\n") + sys.stderr.write("Your output folder was configured for AMBuild 2.1.\n") + sys.stderr.write("Please remove your output folder and reconfigure to continue.\n") os._exit(1) -SM = ExtensionConfig() -SM.detectSDKs() -SM.configure() +Extension = ExtensionConfig() +Extension.detectProductVersion() +Extension.detectSDKs() +Extension.configure() + +if os.path.exists(os.path.join(Extension.sm_root, 'public', 'safetyhook')): + # we need to pull safetyhook in locally because ambuild does not take kindly to outside relpaths + safetyhook_dest = Normalize(builder.sourcePath + '/safetyhook/') + shutil.copytree(os.path.join(Extension.sm_root, 'public', 'safetyhook'), safetyhook_dest, dirs_exist_ok=True) + + SafetyHook = SafetyHookShim() + SafetyHook.all_targets = Extension.all_targets + builder.Build('safetyhook/AMBuilder', {'SafetyHook': SafetyHook }) + Extension.libsafetyhook = SafetyHook.libsafetyhook # This will clone the list and each cxx object as we recurse, preventing child # scripts from messing up global state. -builder.targets = builder.CloneableList(SM.all_targets) +builder.targets = builder.CloneableList(Extension.all_targets) + +# Add additional buildscripts here +BuildScripts = [ + 'AMBuilder', +] if builder.backend == 'amb2': - BuildScripts = [ - 'AMBuilder', - 'PackageScript', - ] + BuildScripts += [ + 'PackageScript', + ] -builder.Build(BuildScripts, { 'SM': SM}) +builder.Build(BuildScripts, { 'Extension': Extension }) diff --git a/AMBuilder b/AMBuilder index 27e07a2..1efe270 100644 --- a/AMBuilder +++ b/AMBuilder @@ -1,48 +1,22 @@ # vim: set sts=2 ts=8 sw=2 tw=99 et ft=python: -import os +import os, sys -projectName = 'a2sqcache' +projectname = 'a2sqcache' -for cxx in builder.targets: - for sdk_name in SM.sdks: - sdk = SM.sdks[sdk_name] +project = builder.LibraryProject(projectname + '.ext') +project.sources = [ + 'extension.cpp', + os.path.join(Extension.sm_root, 'public', 'smsdk_ext.cpp'), +] +for sdk_name in Extension.sdks: + sdk = Extension.sdks[sdk_name] + + for cxx in builder.targets: if not cxx.target.arch in sdk.platformSpec[cxx.target.platform]: continue + + binary = Extension.HL2ExtConfig(project, builder, cxx, projectname + '.ext.' + sdk.ext, sdk) + Extension.AddCDetour(binary) - sdk.ext = "." + sdk.ext - if "sdk2013" in sdk.ext: - sdk.ext = "" - - binary = SM.HL2Library(builder, cxx, projectName + ".ext" + sdk.ext, sdk) - - binary.compiler.defines += [ - 'HAVE_STRING_H', - ] - - if cxx.target.platform == 'linux': - cxx.postlink += ['-lpthread', '-lrt'] - elif cxx.target.platform == 'mac': - cxx.cflags += ['-Wno-deprecated-declarations'] - cxx.postlink += ['-framework', 'CoreServices'] - - if cxx.family == 'gcc' or cxx.family == 'clang': - cxx.cxxflags += ['-fno-rtti'] - elif cxx.family == 'msvc': - cxx.cxxflags += ['/GR-'] - - binary.sources += [ - 'extension.cpp', - os.path.join(SM.sm_root, 'extensions', 'sm-ext-common', 'mathstubs.c'), - os.path.join(SM.sm_root, 'public', 'smsdk_ext.cpp'), - os.path.join(SM.sm_root, 'public', 'CDetour', 'detours.cpp'), - os.path.join(SM.sm_root, 'public', 'asm', 'asm.c'), - os.path.join(SM.sm_root, 'public', 'libudis86', 'decode.c'), - os.path.join(SM.sm_root, 'public', 'libudis86', 'itab.c'), - os.path.join(SM.sm_root, 'public', 'libudis86', 'syn-att.c'), - os.path.join(SM.sm_root, 'public', 'libudis86', 'syn-intel.c'), - os.path.join(SM.sm_root, 'public', 'libudis86', 'syn.c'), - os.path.join(SM.sm_root, 'public', 'libudis86', 'udis86.c'), - ] - - SM.extensions += [builder.Add(binary)] +Extension.extensions = builder.Add(project) \ No newline at end of file diff --git a/PackageScript b/PackageScript index f1e5898..7a4757a 100644 --- a/PackageScript +++ b/PackageScript @@ -1,57 +1,47 @@ -# vim: set ts=8 sts=2 sw=2 tw=99 et ft=python: +# vim: set ts=2 sw=2 tw=99 noet ft=python: import os +import shutil +import ambuild.osutil as osutil +from ambuild.command import Command -# This is where the files will be output to -# package is the default builder.SetBuildFolder('package') -# Add any folders you need to this list -folder_list = [ - 'addons/sourcemod/extensions', - 'addons/sourcemod/scripting/include', - 'addons/sourcemod/gamedata' -] +def CreateFolders(folders): + dict = {} + for folder in folders: + path = os.path.normpath(folder) + dict[folder] = builder.AddFolder(path) + return dict -if 'x86_64' in SM.target_archs: - folder_list.extend([ - 'addons/sourcemod/extensions/x64', - ]) +def CopyFiles(src, dest, filter_ext=None): + source_path = os.path.join(builder.sourcePath, src) + if os.path.isfile(source_path): + builder.AddCopy(source_path, dest) + return + for entry in os.listdir(source_path): + entry_path = os.path.join(source_path, entry) + if not os.path.isfile(entry_path): + continue + if filter_ext: + _, ext = os.path.splitext(entry) + if filter_ext != ext: + continue + builder.AddCopy(entry_path, dest) -# Create the distribution folder hierarchy. -folder_map = {} -for folder in folder_list: - norm_folder = os.path.normpath(folder) - folder_map[folder] = builder.AddFolder(norm_folder) -# Copy binaries. -for cxx_task in SM.extensions: +folders = CreateFolders(['addons/sourcemod/extensions', 'addons/sourcemod/extensions/x64', 'addons/sourcemod/gamedata', 'addons/sourcemod/scripting', 'addons/sourcemod/scripting/include']) + +pdblog = open(os.path.join(builder.buildPath, 'pdblog.txt'), 'wt') +for cxx_task in Extension.extensions: if cxx_task.target.arch == 'x86_64': - builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions/x64']) + builder.AddCopy(cxx_task.binary, folders['addons/sourcemod/extensions/x64']) else: - builder.AddCopy(cxx_task.binary, folder_map['addons/sourcemod/extensions']) + builder.AddCopy(cxx_task.binary, folders['addons/sourcemod/extensions']) + pdblog.write(cxx_task.debug.path + '\n') +pdblog.close() -# Do all straight-up file copies from the source tree. -def CopyFiles(src, dest, files): - if not dest: - dest = src - dest_entry = folder_map[dest] - for source_file in files: - source_path = os.path.join(builder.sourcePath, src, source_file) - dest_path = os.path.join(dest_entry.path, source_file) - if not os.path.isfile(str(dest_path)): - builder.AddCopy(source_path, dest_entry) +CopyFiles('addons/sourcemod/gamedata/a2sqcache.games.txt', folders['addons/sourcemod/gamedata']) +CopyFiles('addons/sourcemod/scripting/include/a2sqcache.inc', folders['addons/sourcemod/scripting/include']) +CopyFiles('addons/sourcemod/extensions/a2sqcache.autoload', folders['addons/sourcemod/extensions']) -# Gamedata (custom so updater doesn't replace it) -CopyFiles('addons/sourcemod/gamedata', 'addons/sourcemod/gamedata', - [ 'a2sqcache.games.txt' ] -) - -# Include file -CopyFiles('addons/sourcemod/scripting/include', 'addons/sourcemod/scripting/include', - [ 'a2sqcache.inc' ] -) - -# Autoload extension -CopyFiles('addons/sourcemod/extensions', 'addons/sourcemod/extensions', - [ 'a2sqcache.autoload' ] -) +debug_info = [] \ No newline at end of file diff --git a/addons/sourcemod/gamedata/a2sqcache.games.txt b/addons/sourcemod/gamedata/a2sqcache.games.txt index 8045001..ed5cc46 100644 --- a/addons/sourcemod/gamedata/a2sqcache.games.txt +++ b/addons/sourcemod/gamedata/a2sqcache.games.txt @@ -37,6 +37,19 @@ "Signatures" { + "Steam3Server" + { + "library" "engine" + "linux" "@_Z12Steam3Serverv" + "linux64" "@_Z12Steam3Serverv" + } + + "CBaseServer__CheckMasterServerRequestRestart" + { + "library" "engine" + "windows" "\xE8\x2A\x2A\x2A\x2A\x83\x78\x04\x00\x74\x2A\xE8\x2A\x2A\x2A\x2A\x8B\x48\x04\x8B\x01\x8B\x40\x2C\xFF\xD0\x84\xC0\x74\x2A\x56" + } + "net_sockets" { "library" "engine" @@ -75,6 +88,11 @@ "windows" "1" "linux" "2" } + + "CheckMasterServerRequestRestart_Steam3ServerFuncOffset" + { + "windows" "1" + } } } diff --git a/extension.cpp b/extension.cpp index 80ecfd7..fd7c928 100644 --- a/extension.cpp +++ b/extension.cpp @@ -52,6 +52,19 @@ #include #include +enum +{ + NS_CLIENT = 0, // client socket + NS_SERVER, // server socket + NS_HLTV, + NS_MATCHMAKING, + NS_SYSTEMLINK, +#ifdef LINUX + NS_SVLAN, // LAN udp port for Linux. See NET_OpenSockets for info. +#endif + MAX_SOCKETS +}; + size_t strlcpy(char *dst, const char *src, size_t dsize) { @@ -156,6 +169,32 @@ typedef struct #endif } netsocket_t; +class CSteam3Server +{ +public: + void *m_pSteamClient; + ISteamGameServer *m_pSteamGameServer; + void *m_pSteamGameServerUtils; + void *m_pSteamGameServerNetworking; + void *m_pSteamGameServerStats; + void *m_pSteamHTTP; + void *m_pSteamInventory; + void *m_pSteamUGC; + void *m_pSteamApps; +} *g_pSteam3Server; + +typedef CSteam3Server *(*Steam3ServerFunc)(); + +Steam3ServerFunc g_pSteam3ServerFunc = NULL; + +CSteam3Server *Steam3Server() +{ + if (!g_pSteam3ServerFunc) + return NULL; + + return g_pSteam3ServerFunc(); +} + CUtlVector *net_sockets; int g_ServerUDPSocket = 0; @@ -256,7 +295,7 @@ void UpdateQueryCache() #endif info.nPassword = iserver->GetPassword() ? 1 : 0; - info.bIsSecure = true; + info.bIsSecure = g_pSteam3Server->m_pSteamGameServer->BSecure() ? 1 : 0; if(!(info.nNewFlags & S2A_EXTRA_DATA_HAS_STEAMID) && engine->GetGameServerSteamID()) { @@ -276,7 +315,7 @@ void UpdateQueryCache() IServer *ihltvserver = hltv->GetBaseServer(); if(ihltvserver) { - info.iHLTVUDPPort = ihltvserver->GetUDPPort(); + info.iHLTVUDPPort = ihltvserver->GetLocalUDPPort(); info.aHLTVNameLen = strlcpy(info.aHLTVName, ihltvserver->GetName(), sizeof(info.aHLTVName)); info.nNewFlags |= S2A_EXTRA_DATA_HAS_SPECTATOR_DATA; } @@ -416,10 +455,8 @@ void SendA2S_Info(netpacket_t * packet) // Password? buf.PutUnsignedChar( iserver->GetPassword() ? 1 : 0 ); - // buf.PutUnsignedChar( Steam3Server().BSecure() ? 1 : 0 ); - // Secure? - buf.PutUnsignedChar( 1 ); + buf.PutUnsignedChar( g_pSteam3Server->m_pSteamGameServer->BSecure() ? 1 : 0 ); buf.PutString( g_QueryCache.info.aVersion ); @@ -432,7 +469,7 @@ void SendA2S_Info(netpacket_t * packet) // Write the rest of the data. if ( g_QueryCache.info.nNewFlags & S2A_EXTRA_DATA_HAS_GAME_PORT ) { - buf.PutShort( LittleWord( iserver->GetUDPPort() ) ); + buf.PutShort( LittleWord( iserver->GetLocalUDPPort() ) ); } #if SOURCE_ENGINE <= SE_CSGO @@ -604,6 +641,48 @@ bool A2SQCache::SDK_OnLoad(char *error, size_t maxlen, bool late) } #endif +#ifndef WIN32 + if (!g_pGameConf->GetMemSig("Steam3Server", (void **)(&g_pSteam3ServerFunc)) || !g_pSteam3ServerFunc) + { + snprintf(error, maxlen, "Failed to find Steam3Server function.\n"); + return false; + } +#else + void *address; + if (!g_pGameConf->GetMemSig("CBaseServer__CheckMasterServerRequestRestart", &address) || !address) + { + snprintf(error, maxlen, "Failed to find CBaseServer__CheckMasterServerRequestRestart function.\n"); + return false; + } + + int steam3ServerFuncOffset = 0; + if (!g_pGameConf->GetOffset("CheckMasterServerRequestRestart_Steam3ServerFuncOffset", &steam3ServerFuncOffset) || steam3ServerFuncOffset == 0) + { + snprintf(error, maxlen, "Failed to find CheckMasterServerRequestRestart_Steam3ServerFuncOffset offset.\n"); + return false; + } + + //META_CONPRINTF("CheckMasterServerRequestRestart: %p\n", address); + address = (void *)((intptr_t)address + steam3ServerFuncOffset); + intptr_t offset = (intptr_t)(*(void **)address); // Get offset + + g_pSteam3ServerFunc = (Steam3ServerFunc)((intptr_t)address + offset + sizeof(intptr_t)); + //META_CONPRINTF("Steam3Server: %p\n", g_pSteam3ServerFunc); +#endif + + g_pSteam3Server = Steam3Server(); + if (!g_pSteam3Server) + { + snprintf(error, maxlen, "Unable to get Steam3Server singleton.\n"); + return false; + } + + if (!g_pSteam3Server->m_pSteamGameServer) + { + snprintf(error, maxlen, "Unable to get Steam Game Server.\n"); + return false; + } + CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf); g_Detour_CBaseServer__InactivateClients = DETOUR_CREATE_MEMBER(CBaseServer__InactivateClients, "CBaseServer__InactivateClients"); @@ -680,8 +759,7 @@ void A2SQCache::SDK_OnAllLoaded() return; } - int socknum = 1; // NS_SERVER - g_ServerUDPSocket = (*net_sockets)[socknum].hUDP; + g_ServerUDPSocket = (*net_sockets)[NS_SERVER].hUDP; if(!g_ServerUDPSocket) { @@ -715,7 +793,7 @@ void A2SQCache::SDK_OnAllLoaded() info.aVersionLen = snprintf(info.aVersion, sizeof(info.aVersion), "%d", engine->GetServerVersion()); #endif - info.iUDPPort = iserver->GetUDPPort(); + info.iUDPPort = iserver->GetLocalUDPPort(); info.nNewFlags |= S2A_EXTRA_DATA_HAS_GAME_PORT; info.iGameID = info.iSteamAppID; diff --git a/product.version b/product.version new file mode 100644 index 0000000..524cb55 --- /dev/null +++ b/product.version @@ -0,0 +1 @@ +1.1.1 diff --git a/smsdk_config.h b/smsdk_config.h index 32f93b0..c002ba7 100644 --- a/smsdk_config.h +++ b/smsdk_config.h @@ -40,7 +40,7 @@ /* Basic information exposed publicly */ #define SMEXT_CONF_NAME "A2SQCache" #define SMEXT_CONF_DESCRIPTION "A2S_INFO + A2S_PLAYER cache" -#define SMEXT_CONF_VERSION "1.1" +#define SMEXT_CONF_VERSION "1.1.1" #define SMEXT_CONF_AUTHOR "BotoX, maxime1907" #define SMEXT_CONF_URL "https://git.botox.bz/CSSZombieEscape/sm-ext-A2SQCache" #define SMEXT_CONF_LOGTAG "A2SQCACHE"