diff --git a/AMBuildScript b/AMBuildScript index 2e1e40f..c67c800 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -8,13 +8,30 @@ class SDK(object): self.ext = ext self.code = aDef self.define = name - self.platform = platform self.name = dir self.path = None # Actual path + self.platformSpec = platform + + # By default, nothing supports x64. + if type(platform) is list: + self.platformSpec = {p: ['x86'] for p in platform} + else: + self.platformSpec = platform + + def shouldBuild(self, target): + if target.platform not in self.platformSpec: + return False + if target.arch not in self.platformSpec[target.platform]: + return False + return True WinOnly = ['windows'] WinLinux = ['windows', 'linux'] WinLinuxMac = ['windows', 'linux', 'mac'] +Source2 = { + 'windows': ['x86', 'x86_64'], + 'linux': ['x86_64'], +} PossibleSDKs = { 'episode1': SDK('HL2SDK', '2.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'), @@ -32,7 +49,7 @@ PossibleSDKs = { 'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'), 'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'), 'csgo': SDK('HL2SDKCSGO', '2.csgo', '20', 'CSGO', WinLinuxMac, 'csgo'), - 'dota': SDK('HL2SDKDOTA', '2.dota', '21', 'DOTA', [], 'dota'), + 'dota': SDK('HL2SDKDOTA', '2.dota', '21', 'DOTA', Source2, 'dota'), 'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'), 'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinLinux, 'blade'), 'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'), @@ -92,7 +109,7 @@ class MMSConfig(object): for sdk_name in PossibleSDKs: sdk = PossibleSDKs[sdk_name] - if builder.target_platform in sdk.platform: + if sdk.shouldBuild(builder.target): if builder.options.hl2sdk_root: sdk_path = os.path.join(builder.options.hl2sdk_root, sdk.folder) else: @@ -106,16 +123,19 @@ class MMSConfig(object): self.sdks[sdk_name] = sdk if len(self.sdks) < 1 and len(sdk_list): - raise Exception('At least one SDK must be available.') + raise Exception('No SDKs were found that build on {0}-{1}, nothing to do.'.format( + builder.target.platform, builder.target.arch)) def configure(self): builder.AddConfigureFile('pushbuild.txt') - cfg = builder.DetectCompilers() - cxx = cfg.cxx + if builder.target.arch not in ['x86', 'x86_64']: + raise Exception('Unknown target architecture: {0}'.format(builder.target.arch)) + + cxx = builder.DetectCxx() if cxx.behavior == 'gcc': - cfg.defines += [ + cxx.defines += [ 'stricmp=strcasecmp', '_stricmp=strcasecmp', '_snprintf=snprintf', @@ -123,7 +143,7 @@ class MMSConfig(object): 'HAVE_STDINT_H', 'GNUC', ] - cfg.cflags += [ + cxx.cflags += [ '-pipe', '-fno-strict-aliasing', '-Wall', @@ -132,52 +152,63 @@ class MMSConfig(object): '-Wno-unused', '-Wno-switch', '-msse', - '-m32', ] - cfg.cxxflags += [ '-std=c++11' ] - if (cxx.name == 'gcc' and cxx.majorVersion >= 4) or cxx.name == 'clang': - cfg.cflags += ['-fvisibility=hidden'] - cfg.cxxflags += ['-fvisibility-inlines-hidden'] - cfg.linkflags += ['-m32'] - cfg.cxxflags += [ + + if builder.target.arch == 'x86': + cxx.cflags += ['-m32'] + cxx.linkflags += ['-m32'] + elif builder.target.arch == 'x86_64': + cxx.cflags += ['-m64', '-fPIC'] + cxx.linkflags += ['-m64'] + + cxx.cxxflags += [ '-std=c++11' ] + if (cxx.version >= 'gcc-4.0') or cxx.family == 'clang': + cxx.cflags += ['-fvisibility=hidden'] + cxx.cxxflags += ['-fvisibility-inlines-hidden'] + cxx.cxxflags += [ '-fno-exceptions', '-fno-rtti', '-fno-threadsafe-statics', '-Wno-non-virtual-dtor', '-Wno-overloaded-virtual', ] - if (cxx.name == 'gcc' and cfg.version >= 'gcc-4.7') or \ - (cxx.name == 'clang' and cxx.majorVersion >= 3): - cfg.cxxflags += ['-Wno-delete-non-virtual-dtor'] - if cxx.name == 'gcc': - cfg.cflags += ['-mfpmath=sse'] - if cxx.name == 'clang': - if cfg.version >= 'clang-3.6': - cfg.cxxflags += ['-Wno-inconsistent-missing-override'] - cfg.cxxflags += ['-Wno-implicit-exception-spec-mismatch'] - if cfg.version >= 'apple-clang-5.1' or cfg.version >= 'clang-3.4': - cfg.cxxflags += ['-Wno-deprecated-register'] + if (cxx.version >= 'gcc-4.7' or cxx.version >= 'clang-3.0'): + cxx.cxxflags += ['-Wno-delete-non-virtual-dtor'] + if cxx.family == 'gcc': + cxx.cflags += ['-mfpmath=sse'] + if cxx.family == 'clang': + cxx.cxxflags += [ + '-Wno-implicit-exception-spec-mismatch', + '-Wno-inconsistent-missing-override', + ] + if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4': + cxx.cxxflags += ['-Wno-deprecated-register'] else: - cfg.cxxflags += ['-Wno-deprecated'] + cxx.cxxflags += ['-Wno-deprecated'] - elif cxx.name == 'msvc': + elif cxx.like('msvc'): if builder.options.debug == '1': - cfg.cflags += ['/MTd'] - cfg.linkflags += ['/NODEFAULTLIB:libcmt'] + cxx.cflags += ['/MTd'] + cxx.linkflags += ['/NODEFAULTLIB:libcmt'] else: - cfg.cflags += ['/MT'] - cfg.defines += [ + cxx.cflags += ['/MT'] + cxx.defines += [ '_CRT_SECURE_NO_DEPRECATE', '_CRT_SECURE_NO_WARNINGS', '_CRT_NONSTDC_NO_DEPRECATE', ] - cfg.cflags += [ + cxx.cflags += [ '/W3', '/Zi', ] - cfg.cxxflags += ['/TP'] - cfg.linkflags += [ - '/MACHINE:X86', + cxx.cxxflags += ['/TP'] + + if builder.target.arch == 'x86': + cxx.linkflags += ['/MACHINE:X86'] + elif builder.target.arch == 'x86_64': + cxx.linkflags += ['/MACHINE:X64'] + + cxx.linkflags += [ '/SUBSYSTEM:WINDOWS', 'kernel32.lib', 'user32.lib', @@ -191,62 +222,62 @@ class MMSConfig(object): 'uuid.lib', 'odbc32.lib', 'odbccp32.lib', - ] + ] # Optimization if builder.options.opt == '1': - cfg.defines += ['NDEBUG'] + cxx.defines += ['NDEBUG'] if cxx.behavior == 'gcc': - cfg.cflags += ['-O3'] + cxx.cflags += ['-O3'] elif cxx.behavior == 'msvc': - cfg.cflags += ['/Ox', '/Zo'] - cfg.linkflags += ['/OPT:ICF', '/OPT:REF'] + cxx.cflags += ['/Ox', '/Zo'] + cxx.linkflags += ['/OPT:ICF', '/OPT:REF'] # Debugging if builder.options.debug == '1': - cfg.defines += ['DEBUG', '_DEBUG'] + cxx.defines += ['DEBUG', '_DEBUG'] if cxx.behavior == 'gcc': - cfg.cflags += ['-g3'] + cxx.cflags += ['-g3'] elif cxx.behavior == 'msvc': - cfg.cflags += ['/Od', '/RTC1'] + cxx.cflags += ['/Od', '/RTC1'] # This needs to be after our optimization flags which could otherwise disable it. - if cxx.name == 'msvc': + if cxx.family == 'msvc': # Don't omit the frame pointer. - cfg.cflags += ['/Oy-'] + cxx.cflags += ['/Oy-'] # Platform-specifics - if builder.target_platform == 'linux': - cfg.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] - if cxx.name == 'gcc': - cfg.linkflags += ['-static-libgcc'] - elif cxx.name == 'clang': - cfg.linkflags += ['-lgcc_eh'] - elif builder.target_platform == 'mac': - cfg.defines += ['OSX', '_OSX', 'POSIX'] - cfg.cflags += ['-mmacosx-version-min=10.5'] - cfg.linkflags += [ + if builder.target.platform == 'linux': + cxx.defines += ['_LINUX', 'POSIX', '_FILE_OFFSET_BITS=64'] + if cxx.family == 'gcc': + cxx.linkflags += ['-static-libgcc'] + elif cxx.family == 'clang': + cxx.linkflags += ['-lgcc_eh'] + elif builder.target.platform == 'mac': + cxx.defines += ['OSX', '_OSX', 'POSIX'] + cxx.cflags += ['-mmacosx-version-min=10.5'] + cxx.linkflags += [ '-mmacosx-version-min=10.5', '-arch', 'i386', '-lstdc++', ] - elif builder.target_platform == 'windows': - cfg.defines += ['WIN32', '_WINDOWS'] + elif builder.target.platform == 'windows': + cxx.defines += ['WIN32', '_WINDOWS'] # Finish up. - cfg.defines += [ 'MMS_USE_VERSIONLIB' ] - cfg.includes += [ + cxx.defines += [ 'MMS_USE_VERSIONLIB' ] + cxx.includes += [ os.path.join(builder.sourcePath, 'public'), ] if self.use_auto_versioning(): - cfg.defines += ['MMS_GENERATED_BUILD'] - cfg.includes += [ + cxx.defines += ['MMS_GENERATED_BUILD'] + cxx.includes += [ os.path.join(builder.buildPath, 'includes'), os.path.join(builder.sourcePath, 'versionlib'), ] def HL2Compiler(self, context, sdk): - compiler = context.compiler.clone() + compiler = context.cxx.clone() compiler.cxxincludes += [ os.path.join(context.currentSourcePath), os.path.join(context.currentSourcePath, 'sourcehook'), @@ -270,25 +301,32 @@ class MMSConfig(object): compiler.defines += ['SOURCE_ENGINE=' + sdk.code] - if sdk.name in ['sdk2013', 'bms'] and compiler.cxx.behavior == 'gcc': + if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'): # The 2013 SDK already has these in public/tier0/basetypes.h compiler.defines.remove('stricmp=strcasecmp') compiler.defines.remove('_stricmp=strcasecmp') compiler.defines.remove('_snprintf=snprintf') compiler.defines.remove('_vsnprintf=vsnprintf') - if compiler.cc.behavior == 'msvc': - compiler.defines += ['COMPILER_MSVC', 'COMPILER_MSVC32'] + if compiler.family == 'msvc': + compiler.defines += ['COMPILER_MSVC'] + if builder.target.arch == 'x86': + compiler.defines += ['COMPILER_MSVC32'] + elif builder.target.arch == 'x86_64': + compiler.defines += ['COMPILER_MSVC64'] else: compiler.defines += ['COMPILER_GCC'] + if sdk.name == 'dota' and builder.target.arch == 'x86_64': + compiler.defines += ['X64BITS', 'PLATFORM_64BITS'] + if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2', 'dota']: - if builder.target_platform in ['linux', 'mac']: + if builder.target.platform in ['linux', 'mac']: compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE'] - - if sdk.name == 'csgo' and builder.target_platform == 'linux': + + if sdk.name == 'csgo' and builder.target.platform == 'linux': compiler.linkflags += ['-lstdc++'] - + for path in paths: compiler.cxxincludes += [os.path.join(sdk.path, *path)] @@ -296,13 +334,13 @@ class MMSConfig(object): return compiler def AddVersioning(self, binary): - if builder.target_platform == 'windows': + if builder.target.platform == 'windows': binary.sources += ['version.rc'] binary.compiler.rcdefines += [ 'BINARY_NAME="{0}"'.format(binary.outputFile), 'RC_COMPILE' ] - elif builder.target_platform == 'mac': + elif builder.target.platform == 'mac': binary.compiler.postlink += [ '-compatibility_version', '1.0.0', '-current_version', self.productVersion @@ -329,30 +367,30 @@ class MMSConfig(object): return binary def Library(self, context, name): - compiler = context.compiler.clone() + compiler = context.cxx.clone() return self.LibraryBuilder(compiler, name) def Program(self, context, name): - compiler = context.compiler.clone() + compiler = context.cxx.clone() return self.ProgramBuilder(compiler, name) def HL2Library(self, context, name, sdk): compiler = self.HL2Compiler(context, sdk) - if builder.target_platform == 'linux': + if builder.target.platform == 'linux': if sdk.name == 'episode1': lib_folder = os.path.join(sdk.path, 'linux_sdk') elif sdk.name in ['sdk2013', 'bms']: lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32') else: lib_folder = os.path.join(sdk.path, 'lib', 'linux') - elif builder.target_platform == 'mac': + elif builder.target.platform == 'mac': if sdk.name in ['sdk2013', 'bms']: lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32') else: lib_folder = os.path.join(sdk.path, 'lib', 'mac') - if builder.target_platform in ['linux', 'mac']: + if builder.target.platform in ['linux', 'mac']: if sdk.name in ['sdk2013', 'bms']: compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'tier1.a'))] else: @@ -364,7 +402,7 @@ class MMSConfig(object): binary = self.LibraryBuilder(compiler, name) dynamic_libs = [] - if builder.target_platform == 'linux': + if builder.target.platform == 'linux': compiler.linkflags[0:0] = ['-lm'] if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency']: dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so'] @@ -372,15 +410,18 @@ class MMSConfig(object): dynamic_libs = ['libtier0.so', 'libvstdlib.so'] else: dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so'] - elif builder.target_platform == 'mac': + elif builder.target.platform == 'mac': binary.compiler.linkflags.append('-liconv') dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib'] - elif builder.target_platform == 'windows': + elif builder.target.platform == 'windows': libs = ['tier0', 'tier1', 'vstdlib'] if sdk.name in ['swarm', 'blade', 'insurgency', 'csgo', 'dota']: libs.append('interfaces') for lib in libs: - lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib' + if builder.target.arch == 'x86': + lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib' + elif builder.target.arch == 'x86_64': + lib_path = os.path.join(sdk.path, 'lib', 'public', 'win64', lib) + '.lib' binary.compiler.linkflags.append(binary.Dep(lib_path)) for library in dynamic_libs: @@ -404,18 +445,18 @@ MMS.detectSDKs() MMS.configure() if MMS.use_auto_versioning(): - MMS.generated_headers = builder.RunScript( + MMS.generated_headers = builder.Build( 'support/buildbot/Versioning', { 'MMS': MMS } ) - MMS.versionlib = builder.RunScript( + MMS.versionlib = builder.Build( 'versionlib/AMBuildScript', { 'MMS': MMS } ) BuildScripts = [ - 'loader/AMBuilder', - 'core/AMBuilder', + 'loader/AMBuilder', + 'core/AMBuilder', ] if getattr(builder.options, 'enable_tests', False): BuildScripts += [ @@ -427,7 +468,7 @@ if builder.backend == 'amb2': 'support/buildbot/PackageScript', ] -builder.RunBuildScripts(BuildScripts, { 'MMS': MMS }) +builder.Build(BuildScripts, { 'MMS': MMS }) if builder.options.breakpad_dump: - builder.RunScript('support/buildbot/BreakpadSymbols', { 'MMS': MMS }) + builder.RunScript('support/buildbot/BreakpadSymbols', { 'MMS': MMS }) \ No newline at end of file diff --git a/configure.py b/configure.py index 90c1406..7c67d44 100644 --- a/configure.py +++ b/configure.py @@ -12,19 +12,23 @@ except: sys.stderr.write('http://www.alliedmods.net/ambuild\n') sys.exit(1) -run = run.PrepareBuild(sourcePath=sys.path[0]) -run.default_build_folder = 'obj-' + run.target_platform -run.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, +def make_objdir_name(p): + return 'obj-linux-' + p.target_arch + +parser = run.BuildParser(sourcePath=sys.path[0], api='2.1') +parser.default_arch = 'x86' +parser.default_build_folder = make_objdir_name +parser.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None, help='Root search folder for HL2SDKs') -run.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', +parser.options.add_option('--enable-debug', action='store_const', const='1', dest='debug', help='Enable debugging symbols') -run.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', +parser.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt', help='Enable optimization') -run.options.add_option('-s', '--sdks', default='all', dest='sdks', +parser.options.add_option('-s', '--sdks', default='all', dest='sdks', help='Build against specified SDKs; valid args are "all", "present", or ' 'comma-delimited list of engine names (default: %default)') -run.options.add_option('--enable-tests', default=False, dest='enable_tests', action='store_true', +parser.options.add_option('--enable-tests', default=False, dest='enable_tests', action='store_true', help='Build tests.') -run.options.add_option('--breakpad-dump', action='store_true', dest='breakpad_dump', +parser.options.add_option('--breakpad-dump', action='store_true', dest='breakpad_dump', default=False, help='Dump and upload breakpad symbols') -run.Configure() +parser.Configure() diff --git a/core/AMBuilder b/core/AMBuilder index 8952e7d..83edb80 100644 --- a/core/AMBuilder +++ b/core/AMBuilder @@ -16,7 +16,6 @@ for sdk_name in MMS.sdks: 'provider/console.cpp', 'provider/provider_ep2.cpp', 'sourcehook/sourcehook.cpp', - 'sourcehook/sourcehook_hookmangen.cpp', 'sourcehook/sourcehook_impl_chookidman.cpp', 'sourcehook/sourcehook_impl_chookmaninfo.cpp', 'sourcehook/sourcehook_impl_cproto.cpp', @@ -24,5 +23,9 @@ for sdk_name in MMS.sdks: 'gamedll_bridge.cpp', 'vsp_bridge.cpp' ] + + # Source2 hack. TODO: check this more deterministically, "are we doing an x64 build?" + if builder.target.arch == 'x86': + binary.sources += ['sourcehook/sourcehook_hookmangen.cpp'] nodes = builder.Add(binary) MMS.binaries += [nodes] diff --git a/core/ISmmAPI.h b/core/ISmmAPI.h index 5b134cf..0788e08 100644 --- a/core/ISmmAPI.h +++ b/core/ISmmAPI.h @@ -39,7 +39,6 @@ #if defined META_NO_HL2SDK class CGlobalVars; -class IServerPluginCallbacks; struct edict_t; class ConCommandBase; #else @@ -56,6 +55,8 @@ class ConCommandBase; typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); +class IServerPluginCallbacks; + namespace SourceMM { class ISmmPlugin; diff --git a/core/ISmmPlugin.h b/core/ISmmPlugin.h index e77e2c5..a6fb061 100644 --- a/core/ISmmPlugin.h +++ b/core/ISmmPlugin.h @@ -39,6 +39,8 @@ #include #include +class IServerPluginCallbacks; + // Interface return status, binary-compatible with HL2SDK's IFACE_OK and IFACE_FAILED. enum { diff --git a/core/gamedll_bridge.cpp b/core/gamedll_bridge.cpp index 21d730f..9c8c34b 100644 --- a/core/gamedll_bridge.cpp +++ b/core/gamedll_bridge.cpp @@ -63,7 +63,11 @@ public: SourceHook::MemFuncInfo mfi; mfi.isVirtual = false; +#if SOURCE_ENGINE == SE_DOTA + SourceHook::GetFuncInfo(&IServerGameDLL::Shutdown, mfi); +#else SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfi); +#endif assert(mfi.isVirtual); assert(mfi.vtbloffs == 0); assert(mfi.thisptroffs == 0); diff --git a/core/metamod.cpp b/core/metamod.cpp index 9d9fc0b..e8508fe 100644 --- a/core/metamod.cpp +++ b/core/metamod.cpp @@ -37,6 +37,9 @@ #if defined __linux__ #include #endif +#if SOURCE_ENGINE == SE_DOTA +#include +#endif using namespace SourceMM; using namespace SourceHook; @@ -47,6 +50,28 @@ using namespace SourceHook::Impl; * @file sourcemm.cpp */ +#if SOURCE_ENGINE == SE_DOTA +// Hack to make hook decl compile when only having forward decl in header. +// (we have class structure but it requires protobuf which we don't want to include here) +class GameSessionConfiguration_t { }; + +SH_DECL_MANUALHOOK3_void(SGD_StartupServer, 0, 0, 0, const GameSessionConfiguration_t &, ISource2WorldSession *, const char *); +SH_DECL_MANUALHOOK2_void(SGD_Init, 0, 0, 0, GameSessionConfiguration_t *, const char *); +SH_DECL_MANUALHOOK3(SGD_StartChangeLevel, 0, 0, 0, CUtlVector *, const char *, const char *, void *); +SH_DECL_MANUALHOOK5_void(SGD_SwitchToLoop, 0, 0, 0, const char *, KeyValues *, uint32, const char *, bool); + +static void +Handler_SwitchToLoop(const char *, KeyValues *, uint32, const char *, bool); + +static void +Handler_StartupServer_Post(const GameSessionConfiguration_t &, ISource2WorldSession *, const char *); + +static void +Handler_Init(GameSessionConfiguration_t *, const char *); + +static CUtlVector * +Handler_StartChangeLevel(const char *, const char *, void *); +#else SH_DECL_MANUALHOOK0(SGD_GameInit, 0, 0, 0, bool); SH_DECL_MANUALHOOK6(SGD_LevelInit, 0, 0, 0, bool, const char *, const char *, const char *, const char *, bool, bool); SH_DECL_MANUALHOOK0_void(SGD_LevelShutdown, 0, 0, 0); @@ -64,6 +89,7 @@ Handler_LevelInit(char const *pMapName, static bool Handler_GameInit(); +#endif static void InitializeVSP(); @@ -98,8 +124,9 @@ static ConVar *mm_basedir = NULL; static CreateInterfaceFn engine_factory = NULL; static CreateInterfaceFn physics_factory = NULL; static CreateInterfaceFn filesystem_factory = NULL; -static CGlobalVars *gpGlobals = NULL; +#if !defined( __amd64__ ) static CHookManagerAutoGen g_SH_HookManagerAutoGen(&g_SourceHook); +#endif static META_RES last_meta_res; static IServerPluginCallbacks *vsp_callbacks = NULL; static bool were_plugins_loaded = false; @@ -157,6 +184,23 @@ mm_InitializeForLoad() */ in_first_level = true; +#if SOURCE_ENGINE == SE_DOTA + SourceHook::MemFuncInfo info; + + if (!provider->GetHookInfo(ProvidedHook_StartupServer, &info)) + { + provider->DisplayError("Metamod:Source could not find a valid hook for INetworkServerService::StartupServer"); + } + SH_MANUALHOOK_RECONFIGURE(SGD_StartupServer, info.vtblindex, info.vtbloffs, info.thisptroffs); + SH_ADD_MANUALHOOK(SGD_StartupServer, netservice, SH_STATIC(Handler_StartupServer_Post), true); + + if (!provider->GetHookInfo(ProvidedHook_SwitchToLoop, &info)) + { + provider->DisplayError("Metamod:Source could not find a valid hook for IEngineServiceMgr::SwitchToLoop"); + } + SH_MANUALHOOK_RECONFIGURE(SGD_SwitchToLoop, info.vtblindex, info.vtbloffs, info.thisptroffs); + SH_ADD_MANUALHOOK(SGD_SwitchToLoop, enginesvcmgr, SH_STATIC(Handler_SwitchToLoop), false); +#else SourceHook::MemFuncInfo info; if (!provider->GetHookInfo(ProvidedHook_GameInit, &info)) @@ -179,6 +223,7 @@ mm_InitializeForLoad() } SH_MANUALHOOK_RECONFIGURE(SGD_LevelShutdown, info.vtblindex, info.vtbloffs, info.thisptroffs); SH_ADD_MANUALHOOK_STATICFUNC(SGD_LevelShutdown, server, Handler_LevelShutdown, true); +#endif } bool @@ -469,27 +514,6 @@ mm_InitializeGlobals(CreateInterfaceFn engineFactory, provider->Notify_DLLInit_Pre(engineFactory, gamedll_info.factory); } -static bool -Handler_GameInit() -{ - if (is_game_init) - return true; - - if (vsp_load_requested) - InitializeVSP(); - - if (g_bIsVspBridged && !were_plugins_loaded) - { - DoInitialPluginLoads(); - g_PluginMngr.SetAllLoaded(); - were_plugins_loaded = true; - } - - is_game_init = true; - - RETURN_META_VALUE(MRES_IGNORED, true); -} - void mm_UnloadMetamod() { @@ -502,8 +526,35 @@ mm_UnloadMetamod() } static void -Handler_LevelShutdown(void) +mm_HandleGameInit() { + if (is_game_init) + return; + +#if SOURCE_ENGINE == SE_DOTA + DevMsg("MMS: GameInit\n"); +#endif + + if (vsp_load_requested) + InitializeVSP(); + + if (g_bIsVspBridged && !were_plugins_loaded) + { + DoInitialPluginLoads(); + g_PluginMngr.SetAllLoaded(); + were_plugins_loaded = true; + } + + is_game_init = true; +} + +static void +mm_HandleLevelShutdown() +{ +#if SOURCE_ENGINE == SE_DOTA + DevMsg("MMS: LevelShutdown\n"); +#endif + if (g_bIsVspBridged && !were_plugins_loaded) { DoInitialPluginLoads(); @@ -534,6 +585,96 @@ Handler_LevelShutdown(void) } ITER_EVENT(OnLevelShutdown, ()); +} + +static void +mm_HandleLevelInit(char const *pMapName, +char const *pMapEntities, +char const *pOldLevel, +char const *pLandmarkName, +bool loadGame, +bool background) +{ +#if SOURCE_ENGINE == SE_DOTA + DevMsg("MMS: LevelInit\n"); +#endif + + ITER_EVENT(OnLevelInit, (pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background)); +} +#include +#if SOURCE_ENGINE == SE_DOTA +static void +Handler_SwitchToLoop(const char *pszLoopName, KeyValues *pKV, uint32 nId, const char *pszUnk, bool bUnk) +{ + if (strcmp(pszLoopName, "levelload") == 0) + { + mm_HandleGameInit(); + } + + RETURN_META(MRES_IGNORED); +} + +static void +Handler_StartupServer_Post(const GameSessionConfiguration_t &config, ISource2WorldSession *, const char *) +{ + static bool bGameServerHooked = false; + if (!bGameServerHooked) + { + INetworkGameServer *netserver = (META_IFACEPTR(INetworkServerService))->GetIGameServer(); + + SourceHook::MemFuncInfo info; + if (!provider->GetHookInfo(ProvidedHook_Init, &info)) + { + provider->DisplayError("Metamod:Source could not find a valid hook for INetworkGameServer::Init"); + } + SH_MANUALHOOK_RECONFIGURE(SGD_Init, info.vtblindex, info.vtbloffs, info.thisptroffs); + SH_ADD_MANUALVPHOOK(SGD_Init, netserver, SH_STATIC(Handler_Init), false); + + if (!provider->GetHookInfo(ProvidedHook_StartChangeLevel, &info)) + { + provider->DisplayError("Metamod:Source could not find a valid hook for INetworkGameServer::StartChangeLevel"); + } + SH_MANUALHOOK_RECONFIGURE(SGD_StartChangeLevel, info.vtblindex, info.vtbloffs, info.thisptroffs); + SH_ADD_MANUALVPHOOK(SGD_StartChangeLevel, netserver, SH_STATIC(Handler_StartChangeLevel), false); + + bGameServerHooked = true; + } + + RETURN_META(MRES_IGNORED); +} + +static void +Handler_Init(GameSessionConfiguration_t *pConfig, const char *pszMapName) +{ + static char szLastMap[260] = ""; + mm_HandleLevelInit(pszMapName, "", szLastMap, "", false, false); + UTIL_Format(szLastMap, sizeof(szLastMap), "%s", pszMapName); + + RETURN_META(MRES_IGNORED); +} + +static CUtlVector * +Handler_StartChangeLevel(const char *, const char *, void *) +{ + mm_HandleLevelShutdown(); + + RETURN_META_VALUE(MRES_IGNORED, nullptr); +} + +#else + +static bool +Handler_GameInit() +{ + mm_HandleGameInit(); + + RETURN_META_VALUE(MRES_IGNORED, true); +} + +static void +Handler_LevelShutdown(void) +{ + mm_HandleLevelShutdown(); RETURN_META(MRES_IGNORED); } @@ -550,6 +691,7 @@ Handler_LevelInit(char const *pMapName, RETURN_META_VALUE(MRES_IGNORED, false); } +#endif void MetamodSource::LogMsg(ISmmPlugin *pl, const char *msg, ...) { @@ -855,11 +997,19 @@ void *MetamodSource::MetaFactory(const char *iface, int *ret, PluginId *id) } else if (strcmp(iface, MMIFACE_SH_HOOKMANAUTOGEN) == 0) { +#if defined( __amd64__ ) + if (ret) + { + *ret = META_IFACE_FAILED; + } + return nullptr; +#else if (ret) { *ret = META_IFACE_OK; } return static_cast(static_cast(&g_SH_HookManagerAutoGen)); +#endif } CPluginManager::CPlugin *pl; diff --git a/core/metamod_oslink.h b/core/metamod_oslink.h index 78c6e20..a2b579c 100644 --- a/core/metamod_oslink.h +++ b/core/metamod_oslink.h @@ -100,10 +100,6 @@ bool GetFileOfAddress(void *pAddr, char *buffer, size_t maxlength); typedef unsigned __int32 uint32_t; #elif defined __GNUC__ #include -#if !__GLIBC_HAVE_LONG_LONG - typedef long long int64_t; - typedef unsigned long long uint64_t; -#endif #endif #if !defined __linux__ && !defined __APPLE__ diff --git a/core/metamod_provider.h b/core/metamod_provider.h index 4aec228..2451625 100644 --- a/core/metamod_provider.h +++ b/core/metamod_provider.h @@ -37,9 +37,16 @@ namespace SourceMM enum ProvidedHooks { +#if SOURCE_ENGINE == SE_DOTA + ProvidedHook_StartChangeLevel = 0, + ProvidedHook_Init = 1, + ProvidedHook_StartupServer = 2, + ProvidedHook_SwitchToLoop = 3, +#else ProvidedHook_LevelInit = 0, /**< IServerGameDLL::LevelInit */ ProvidedHook_LevelShutdown = 1, /**< IServerGameDLL::LevelShutdown */ ProvidedHook_GameInit = 4, /**< IServerGameDLL::GameInit */ +#endif }; /** diff --git a/core/provider/provider_ep2.cpp b/core/provider/provider_ep2.cpp index 563652a..54b447c 100644 --- a/core/provider/provider_ep2.cpp +++ b/core/provider/provider_ep2.cpp @@ -38,6 +38,18 @@ #include "metamod_console.h" #include #include "metamod.h" +#include +#if SOURCE_ENGINE == SE_DOTA +#include +#endif + +#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 ) +SH_DECL_HOOK1(ISource2ServerConfig, AllowDedicatedServers, const, 0, bool, EUniverse); +bool BaseProvider::AllowDedicatedServers(EUniverse universe) const +{ + RETURN_META_VALUE(MRES_SUPERCEDE, true); +} +#endif /* Types */ typedef void (*CONPRINTF_FUNC)(const char *, ...); @@ -63,14 +75,18 @@ DLL_IMPORT ICommandLine *CommandLine(); void CacheUserMessages(); bool KVLoadFromFile(KeyValues *kv, IBaseFileSystem *filesystem, const char *resourceName, const char *pathID = NULL); void Detour_Error(const tchar *pMsg, ...); + #if SOURCE_ENGINE == SE_DOTA void ClientCommand(CEntityIndex index, const CCommand &args); -void LocalCommand_Meta(const CCommandContext &context, const CCommand &args); #elif SOURCE_ENGINE >= SE_ORANGEBOX void ClientCommand(edict_t *pEdict, const CCommand &args); -void LocalCommand_Meta(const CCommand &args); #else void ClientCommand(edict_t *pEdict); +#endif + +#if SOURCE_ENGINE >= SE_ORANGEBOX +void LocalCommand_Meta(const CCommand &args); +#else void LocalCommand_Meta(); #endif @@ -85,8 +101,14 @@ static bool g_bOriginalEngine = false; ICvar *icvar = NULL; IFileSystem *baseFs = NULL; IServerGameDLL *server = NULL; +#if SOURCE_ENGINE == SE_DOTA +static ISource2ServerConfig *serverconfig = NULL; +INetworkServerService *netservice = NULL; +IEngineServiceMgr *enginesvcmgr = NULL; +#endif IVEngineServer *engine = NULL; IServerGameClients *gameclients = NULL; +CGlobalVars *gpGlobals = NULL; IMetamodSourceProvider *provider = &g_Ep1Provider; ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options"); @@ -129,6 +151,12 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, DisplayError("Could not find IVEngineServer! Metamod cannot load."); return; } +#if SOURCE_ENGINE == SE_DOTA + gpGlobals = engine->GetServerGlobals(); + serverconfig = (ISource2ServerConfig *) ((serverFactory) (INTERFACEVERSION_SERVERCONFIG, NULL)); + netservice = (INetworkServerService *) ((engineFactory) (NETWORKSERVERSERVICE_INTERFACE_VERSION, NULL)); + enginesvcmgr = (IEngineServiceMgr *) ((engineFactory) (ENGINESERVICEMGR_INTERFACE_VERSION, NULL)); +#endif #if SOURCE_ENGINE >= SE_ORANGEBOX icvar = (ICvar *)((engineFactory)(CVAR_INTERFACE_VERSION, NULL)); #else @@ -140,12 +168,15 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, return; } - +#if SOURCE_ENGINE == SE_DOTA + gameclients = (IServerGameClients *)(serverFactory(INTERFACEVERSION_SERVERGAMECLIENTS, NULL)); +#else if ((gameclients = (IServerGameClients *)(serverFactory("ServerGameClients003", NULL))) == NULL) { gameclients = (IServerGameClients *)(serverFactory("ServerGameClients004", NULL)); } +#endif baseFs = (IFileSystem *)((engineFactory)(FILESYSTEM_INTERFACE_VERSION, NULL)); if (baseFs == NULL) @@ -153,6 +184,41 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, mm_LogMessage("Unable to find \"%s\": .vdf files will not be parsed", FILESYSTEM_INTERFACE_VERSION); } +#if SOURCE_ENGINE == SE_DOTA + // Since we have to be added as a Game path (cannot add GameBin directly), we + // automatically get added to other paths as well, including having the MM:S + // dir become the default write path for logs and more. We can fix some of these. + + char searchPath[260]; + baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, sizeof(searchPath)); + for (size_t i = 0; i < sizeof(searchPath); ++i) + { + if (searchPath[i] == ';') + { + searchPath[i] = '\0'; + break; + } + } + baseFs->RemoveSearchPath(searchPath, "GAME"); + + // TODO: figure out why these calls get ignored and path remains + //baseFs->RemoveSearchPath(searchPath, "CONTENT"); + //baseFs->RemoveSearchPath(searchPath, "SHADER_SOURCE"); + //baseFs->RemoveSearchPath(searchPath, "SHADER_SOURCE_MOD"); + + baseFs->RemoveSearchPaths("DEFAULT_WRITE_PATH"); + baseFs->GetSearchPath("GAME", (GetSearchPathTypes_t)0, searchPath, sizeof(searchPath)); + for (size_t i = 0; i < sizeof(searchPath); ++i) + { + if (searchPath[i] == ';') + { + searchPath[i] = '\0'; + break; + } + } + baseFs->AddSearchPath(searchPath, "DEFAULT_WRITE_PATH"); +#endif + #if SOURCE_ENGINE >= SE_ORANGEBOX g_pCVar = icvar; #endif @@ -179,6 +245,10 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, { SH_ADD_HOOK_STATICFUNC(IServerGameClients, ClientCommand, gameclients, ClientCommand, false); } + +#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 ) + SH_ADD_VPHOOK(ISource2ServerConfig, AllowDedicatedServers, serverconfig, SH_MEMBER(this, &BaseProvider::AllowDedicatedServers), false); +#endif } void BaseProvider::Notify_DLLShutdown_Pre() @@ -201,7 +271,7 @@ bool BaseProvider::IsRemotePrintingAvailable() void BaseProvider::ClientConsolePrint(edict_t *pEdict, const char *message) { #if SOURCE_ENGINE == SE_DOTA - int client = (int)(pEdict - g_Metamod.GetCGlobals()->pEdicts); + int client = (int)(pEdict - gpGlobals->pEdicts); engine->ClientPrintf(client, message); #else engine->ClientPrintf(pEdict, message); @@ -284,6 +354,31 @@ bool BaseProvider::LogMessage(const char *buffer) bool BaseProvider::GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInfo) { +#if SOURCE_ENGINE == SE_DOTA + SourceHook::MemFuncInfo mfi = {true, -1, 0, 0}; + + switch (hook) + { + case ProvidedHook_StartupServer: + SourceHook::GetFuncInfo(&INetworkServerService::StartupServer, mfi); + break; + case ProvidedHook_StartChangeLevel: + SourceHook::GetFuncInfo(&INetworkGameServer::StartChangeLevel, mfi); + break; + case ProvidedHook_Init: + SourceHook::GetFuncInfo(&INetworkGameServer::Init, mfi); + break; + case ProvidedHook_SwitchToLoop: + SourceHook::GetFuncInfo(&IEngineServiceMgr::SwitchToLoop, mfi); + break; + default: + return false; + } + + *pInfo = mfi; + + return (mfi.thisptroffs >= 0); +#else SourceHook::MemFuncInfo mfi = {true, -1, 0, 0}; if (hook == ProvidedHook_LevelInit) @@ -302,6 +397,7 @@ bool BaseProvider::GetHookInfo(ProvidedHooks hook, SourceHook::MemFuncInfo *pInf *pInfo = mfi; return (mfi.thisptroffs >= 0); +#endif } void BaseProvider::DisplayError(const char *fmt, ...) @@ -391,7 +487,11 @@ void BaseProvider::GetGamePath(char *pszBuffer, int len) const char *BaseProvider::GetGameDescription() { +#if SOURCE_ENGINE == SE_DOTA + return serverconfig->GetGameDescription(); +#else return server->GetGameDescription(); +#endif } int BaseProvider::DetermineSourceEngine() @@ -618,11 +718,7 @@ public: }; #endif -#if SOURCE_ENGINE == SE_DOTA -void LocalCommand_Meta(const CCommandContext &context, const CCommand &args) -{ - GlobCommand cmd(&args); -#elif SOURCE_ENGINE >= SE_ORANGEBOX +#if SOURCE_ENGINE >= SE_ORANGEBOX void LocalCommand_Meta(const CCommand &args) { GlobCommand cmd(&args); diff --git a/core/provider/provider_ep2.h b/core/provider/provider_ep2.h index 0c576de..59f79d2 100644 --- a/core/provider/provider_ep2.h +++ b/core/provider/provider_ep2.h @@ -32,7 +32,9 @@ #endif #include #include +#if SOURCE_ENGINE != SE_DOTA #include +#endif #include "ISmmAPI.h" #include "metamod_provider.h" #include "metamod_oslink.h" @@ -44,6 +46,8 @@ using namespace SourceMM; using namespace SourceHook; +class INetworkGameServer; + class BaseProvider : public IMetamodSourceProvider { public: @@ -78,12 +82,20 @@ public: virtual int DetermineSourceEngine(); virtual bool ProcessVDF(const char *file, char path[], size_t path_len, char alias[], size_t alias_len); virtual const char *GetEngineDescription() const; +#if SOURCE_ENGINE == SE_DOTA && defined( _WIN32 ) + bool AllowDedicatedServers(EUniverse universe) const; +#endif }; extern IVEngineServer *engine; extern IServerGameDLL *server; extern IServerGameClients *gameclients; extern ICvar *icvar; +extern CGlobalVars *gpGlobals; +#if SOURCE_ENGINE == SE_DOTA +extern INetworkServerService *netservice; +extern IEngineServiceMgr *enginesvcmgr; +#endif #endif //_INCLUDE_METAMOD_SOURCE_BASE_PROVIDER_H_ diff --git a/core/sourcehook/sh_memfuncinfo.h b/core/sourcehook/sh_memfuncinfo.h index 47a7dae..f83bf69 100644 --- a/core/sourcehook/sh_memfuncinfo.h +++ b/core/sourcehook/sh_memfuncinfo.h @@ -125,6 +125,11 @@ namespace SourceHook addr += 2; ok = true; } + else if (addr[0] == 0x48 && addr[1] == 0x8B && addr[2] == 0x01) + { + addr += 3; + ok = true; + } if (!ok) return -1; @@ -132,11 +137,11 @@ namespace SourceHook { if (*addr == 0x60) { - return *++addr / 4; + return *++addr / SH_PTRSIZE; } else if (*addr == 0xA0) { - return *((unsigned int*)++addr) / 4; + return *((unsigned int*)++addr) / SH_PTRSIZE; } else if (*addr == 0x20) return 0; diff --git a/core/sourcehook/test/main.cpp b/core/sourcehook/test/main.cpp index f6c7e2f..2db137c 100644 --- a/core/sourcehook/test/main.cpp +++ b/core/sourcehook/test/main.cpp @@ -18,6 +18,11 @@ using namespace std; bool g_Verbose; +struct Unloader : public SourceHook::Impl::UnloadListener +{ + void ReadyToUnload(SourceHook::Plugin) { } +} g_UnloadListener; + #define DECL_TEST(x) bool Test##x(std::string &error); #define DO_TEST(x) \ @@ -73,7 +78,9 @@ int main(int argc, char *argv[]) DO_TEST(RefRet); DO_TEST(VPHooks); DO_TEST(CPageAlloc); +#if !defined( _M_AMD64 ) && !defined( __amd64__ ) // TODO: Fix for 64-bit DO_TEST(HookManGen); +#endif DO_TEST(OddThunks); cout << endl << "----" << endl << "Passed: " << passed << endl << "Failed: " << failed << endl; diff --git a/core/sourcehook/test/msvc12/test.sln b/core/sourcehook/test/msvc12/test.sln new file mode 100644 index 0000000..8c9edab --- /dev/null +++ b/core/sourcehook/test/msvc12/test.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{456BBA64-FF14-4292-8443-3BA79E4D84CC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug_NoDebugRuntimeLib|Win32 = Debug_NoDebugRuntimeLib|Win32 + Debug_NoDebugRuntimeLib|x64 = Debug_NoDebugRuntimeLib|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + DebugOpt|Win32 = DebugOpt|Win32 + DebugOpt|x64 = DebugOpt|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug_NoDebugRuntimeLib|Win32.ActiveCfg = Debug_NoDebugRuntimeLib|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug_NoDebugRuntimeLib|Win32.Build.0 = Debug_NoDebugRuntimeLib|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug_NoDebugRuntimeLib|x64.ActiveCfg = Debug_NoDebugRuntimeLib|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug_NoDebugRuntimeLib|x64.Build.0 = Debug_NoDebugRuntimeLib|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug|Win32.ActiveCfg = Debug|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug|Win32.Build.0 = Debug|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug|x64.ActiveCfg = Debug|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Debug|x64.Build.0 = Debug|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.DebugOpt|Win32.ActiveCfg = DebugOpt|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.DebugOpt|Win32.Build.0 = DebugOpt|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.DebugOpt|x64.ActiveCfg = DebugOpt|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.DebugOpt|x64.Build.0 = DebugOpt|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Release|Win32.ActiveCfg = Release|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Release|Win32.Build.0 = Release|Win32 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Release|x64.ActiveCfg = Release|x64 + {456BBA64-FF14-4292-8443-3BA79E4D84CC}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/core/sourcehook/test/msvc12/test.vcxproj b/core/sourcehook/test/msvc12/test.vcxproj new file mode 100644 index 0000000..f86fa5c --- /dev/null +++ b/core/sourcehook/test/msvc12/test.vcxproj @@ -0,0 +1,461 @@ + + + + + DebugOpt + Win32 + + + DebugOpt + x64 + + + Debug_NoDebugRuntimeLib + Win32 + + + Debug_NoDebugRuntimeLib + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {456BBA64-FF14-4292-8443-3BA79E4D84CC} + test + Win32Proj + + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + Application + v120 + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>12.0.30501.0 + + + Debug\ + Debug\ + true + + + true + + + Release\ + Release\ + false + + + false + + + DebugOpt\ + DebugOpt\ + true + + + true + + + Debug_NoDebugRuntimeLib\ + Debug_NoDebugRuntimeLib\ + true + + + true + + + + Disabled + Neither + ../..;..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;SH_DEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + true + EnableFastChecks + MultiThreadedDebug + true + true + + Level3 + EditAndContinue + + + $(OutDir)test.exe + true + $(OutDir)test.pdb + Console + false + + MachineX86 + + + + + Disabled + Neither + ../..;..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;SH_DEBUG;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + true + true + + + Level3 + ProgramDatabase + + + $(OutDir)test.exe + true + $(OutDir)test.pdb + Console + false + + + + + + + Full + OnlyExplicitInline + Size + true + ../..;..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + true + + + Level3 + true + ProgramDatabase + + + $(OutDir)test.exe + true + true + true + Console + true + true + false + + MachineX86 + + + + + Full + OnlyExplicitInline + Size + true + ../..;..;%(AdditionalIncludeDirectories) + NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + MultiThreaded + true + true + + + + + Level3 + true + ProgramDatabase + + + $(OutDir)test.exe + true + true + true + Console + true + true + false + + + + + + + Full + OnlyExplicitInline + false + Size + true + ..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + false + Default + MultiThreaded + true + true + + Level3 + ProgramDatabase + + + $(OutDir)test.exe + true + $(OutDir)test.pdb + Console + false + + MachineX86 + + + + + Full + OnlyExplicitInline + false + Size + true + ..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + false + Default + MultiThreaded + true + true + + + Level3 + ProgramDatabase + + + $(OutDir)test.exe + true + $(OutDir)test.pdb + Console + false + + + + + + + Disabled + Neither + ../..;..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + true + Sync + EnableFastChecks + MultiThreaded + true + true + + Level3 + EditAndContinue + + + $(OutDir)test.exe + true + $(OutDir)test.pdb + Console + false + + MachineX86 + + + + + Disabled + Neither + ../..;..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + Sync + EnableFastChecks + MultiThreaded + true + true + + + Level3 + ProgramDatabase + + + $(OutDir)test.exe + true + $(OutDir)test.pdb + Console + false + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + + + + + + + + + + + + + + echo on +pushd ..\..\generate +shworker.exe iter sourcehook.hxx sourcehook.h 16 +copy sourcehook.h ..\sourcehook.h +popd + + echo on +pushd ..\..\generate +shworker.exe iter sourcehook.hxx sourcehook.h 16 +copy sourcehook.h ..\sourcehook.h +popd + + ..\..\sourcehook.h;%(Outputs) + ..\..\sourcehook.h;%(Outputs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/sourcehook/test/msvc12/test.vcxproj.filters b/core/sourcehook/test/msvc12/test.vcxproj.filters new file mode 100644 index 0000000..74a54b8 --- /dev/null +++ b/core/sourcehook/test/msvc12/test.vcxproj.filters @@ -0,0 +1,191 @@ + + + + + {097d3bc9-e1f2-4054-93f3-b12d28409a01} + cpp;c;cxx;def;odl;idl;hpj;bat;asm + + + {c1c8bd55-c7ae-46fc-a115-e490b5068b5c} + + + {17326c78-c7e4-456b-b9df-019e07ca478f} + + + {c15408a4-1aa9-485e-916b-c4eeaddeeee2} + h;hpp;hxx;hm;inl;inc + + + {be31706d-65c6-4945-b7be-84dcbbcde2e7} + + + {75eb6b5b-331a-4eba-b0b2-687c273e2746} + + + {1efd71b2-ab96-4217-ab65-dbc369ec623f} + + + {d4f35fc6-e668-474b-82af-0771f32cde7f} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + + + Source Files\SourceHook + + + Source Files\SourceHook + + + Source Files\SourceHook + + + Source Files\SourceHook + + + Source Files\SourceHook + + + Source Files\SourceHook + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + Source Files\TestTools + + + + + Header Files\TestTools + + + Header Files\TestTools + + + Header Files\TestTools + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook + + + Header Files\SourceHook\generate + + + Header Files\SourceHook\generate + + + Header Files\SourceHook\generate + + + + + Header Files\SourceHook\generate + + + \ No newline at end of file diff --git a/core/sourcehook/test/testoddthunks.cpp b/core/sourcehook/test/testoddthunks.cpp index ab0624a..379cdfc 100644 --- a/core/sourcehook/test/testoddthunks.cpp +++ b/core/sourcehook/test/testoddthunks.cpp @@ -98,13 +98,30 @@ namespace // Now generate the jump code g_ThunkAllocator.SetRW(g_OddThunkMemory); - *(base + 0) = 0xE9; // offset jump, immediate operand - ptrdiff_t *offsetAddr = reinterpret_cast(base + 1); - // destination = src + offset + 5 // <=> offset = destination - src - 5 - *offsetAddr = - (reinterpret_cast(origEntry) - base) - 5; + ptrdiff_t offset = reinterpret_cast(origEntry) - base - 5; + + if (offset >= INT_MIN && offset <= INT_MAX) + { + *(base + 0) = 0xE9; // offset jump, immediate operand + ptrdiff_t *offsetAddr = reinterpret_cast(base + 1); + + *offsetAddr = offset; + } + else + { + // mov rax, origEntry + *(base + 0) = 0x48; + *(base + 1) = 0xB8; + void **offsetAddr = reinterpret_cast(reinterpret_cast(base) + 2); + + *offsetAddr = origEntry; + + // jmp rax + *(base + 10) = 0xFF; + *(base + 11) = 0xE0; + } g_ThunkAllocator.SetRE(g_OddThunkMemory); diff --git a/core/vsp_bridge.cpp b/core/vsp_bridge.cpp index 496e630..ab24824 100644 --- a/core/vsp_bridge.cpp +++ b/core/vsp_bridge.cpp @@ -34,7 +34,7 @@ #include #include "provider/provider_ep2.h" -#if SOURCE_ENGINE == SE_DOTA || SOURCE_ENGINE == SE_SOURCE2 +#if SOURCE_ENGINE == SE_DOTA SH_DECL_HOOK2_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommandContext &, const CCommand &); #elif SOURCE_ENGINE >= SE_ORANGEBOX SH_DECL_HOOK1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); @@ -164,11 +164,14 @@ public: virtual void Unload() { + // Source2 doesn't have the Error function (nor VSP support). +#if SOURCE_ENGINE != SE_DOTA if (g_bIsTryingToUnload) { Error("Metamod:Source cannot be unloaded from VSP mode. Use \"meta unload\" to unload specific plugins.\n"); return; } +#endif if (g_plugin_unload != NULL) { SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_plugin_unload, InterceptPluginUnloads, false); diff --git a/loader/AMBuilder b/loader/AMBuilder index 0207359..c2f30aa 100644 --- a/loader/AMBuilder +++ b/loader/AMBuilder @@ -11,12 +11,17 @@ def configure_library(name, linux_defines): 'utility.cpp', ] - if builder.target_platform == 'linux': + if builder.target.platform == 'linux': binary.compiler.defines += linux_defines nodes = builder.Add(binary) MMS.binaries += [nodes] -configure_library('server', ['LIB_PREFIX="lib"', 'LIB_SUFFIX=".so"']) -if builder.target_platform == 'linux': +libname = 'server' +if builder.target.platform == 'linux' and builder.target.arch == 'x86_64': + libname = 'libserver' + +configure_library(libname, ['LIB_PREFIX="lib"', 'LIB_SUFFIX=".so"']) + +if builder.target.platform == 'linux' and builder.target.arch == 'x86': configure_library('server_i486', ['LIB_PREFIX=""', 'LIB_SUFFIX="_i486.so"']) diff --git a/loader/gamedll.cpp b/loader/gamedll.cpp index 1d46c9f..b110a12 100644 --- a/loader/gamedll.cpp +++ b/loader/gamedll.cpp @@ -2,7 +2,7 @@ * vim: set ts=4 sw=4 tw=99 noet : * ====================================================== * Metamod:Source - * Copyright (C) 2004-2009 AlliedModders LLC and authors. + * Copyright (C) 2004-2015 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * @@ -36,6 +36,7 @@ #include "gamedll.h" class IServerGameDLL; +class ISource2ServerConfig; #define MAX_GAMEDLL_PATHS 10 @@ -47,17 +48,40 @@ static void *gamedll_libs[MAX_GAMEDLL_PATHS]; static unsigned int gamedll_path_count = 0; static void *gamedll_lib = NULL; static IServerGameDLL *gamedll_iface = NULL; +static ISource2ServerConfig *config_iface = NULL; static QueryValveInterface gamedll_qvi = NULL; static int gamedll_version = 0; static int isgd_shutdown_index = -1; +#if defined _WIN32 +static int is2sc_allowdedi_index = 21; +#endif static char mm_path[PLATFORM_MAX_PATH]; +static bool g_is_source2 = false; #if defined _WIN32 #define SERVER_NAME "server.dll" +#if defined _WIN64 +#define PLATFORM_NAME "win64" +#else +#define PLATFORM_NAME "win32" +#endif #elif defined __APPLE__ #define SERVER_NAME "server.dylib" +#if defined __amd64__ +#define PLATFORM_NAME "osx64" +#else +#define PLATFORM_NAME "osx32" +#endif #elif defined __linux__ -#define SERVER_NAME "server" LIB_SUFFIX +#if defined __amd64__ +// hackhack - source2 uses libserver as name on POSIX, but source1 x64 does not +// (but source1 x64 is also client-only right now so what-ev) +#define SERVER_NAME "libserver" LIB_SUFFIX +#define PLATFORM_NAME "linuxsteamrt64" +#else +#define SERVER_NAME "server" LIB_SUFFIX +#define PLATFORM_NAME "linuxsteamrt32" +#endif #endif static bool @@ -78,7 +102,7 @@ mm_DetectGameInformation() return false; } - if (!mm_ResolvePath(game_name, game_path, sizeof(game_path))) + if (!mm_ResolvePath(game_name, game_path, sizeof(game_path), g_is_source2)) { mm_LogFatal("Could not resolve path: %s", game_name); return false; @@ -87,11 +111,21 @@ mm_DetectGameInformation() FILE *fp; char gameinfo_path[PLATFORM_MAX_PATH]; + bool is_source2 = false; mm_PathFormat(gameinfo_path, sizeof(gameinfo_path), "%s/gameinfo.txt", game_path); if ((fp = fopen(gameinfo_path, "rt")) == NULL) { - mm_LogFatal("Could not read file: %s", gameinfo_path); - return false; + // Try Source2 gameinfo + mm_PathFormat(gameinfo_path, sizeof(gameinfo_path), "%s/gameinfo.gi", game_path); + if ((fp = fopen(gameinfo_path, "rt")) == NULL) + { + mm_LogFatal("Could not read file: %s", gameinfo_path); + return false; + } + else + { + is_source2 = true; + } } char temp_path[PLATFORM_MAX_PATH]; @@ -131,12 +165,14 @@ mm_DetectGameInformation() lptr = cur_path; } + const char *pRelPath = is_source2 ? "../../" : ""; + const char *pOSDir = is_source2 ? PLATFORM_NAME "/" : ""; if (stricmp(key, "GameBin") == 0) - mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s/" SERVER_NAME, lptr, ptr); + mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s%s/%s" SERVER_NAME, lptr, pRelPath, ptr, pOSDir); else if (!ptr[0]) - mm_PathFormat(temp_path, sizeof(temp_path), "%s/bin/" SERVER_NAME, lptr); + mm_PathFormat(temp_path, sizeof(temp_path), "%s/%sbin/%s" SERVER_NAME, lptr, pRelPath, pOSDir); else - mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s/bin/" SERVER_NAME, lptr, ptr); + mm_PathFormat(temp_path, sizeof(temp_path), "%s/%s%s/bin/%s" SERVER_NAME, lptr, pRelPath, ptr, pOSDir); if (mm_PathCmp(mm_path, temp_path)) continue; @@ -174,7 +210,7 @@ mm_DetectGameInformation() if (gamedll_path_count == 0) { - mm_LogFatal("Could not detect any valid game paths in gameinfo.txt"); + mm_LogFatal("Could not detect any valid game paths in gameinfo file"); return false; } @@ -198,13 +234,216 @@ mm_PatchDllInit(bool patch); static void mm_PatchDllShutdown(); +#if defined _WIN32 +static void +mm_PatchAllowDedicated(bool patch); +#endif + +static void +mm_PatchConnect(bool patch); + static void *isgd_orig_init = NULL; static void *isgd_orig_shutdown = NULL; +#if defined _WIN32 +static void *is2sc_orig_allowdedi = NULL; +#endif +static void *is2sc_orig_connect = NULL; class VEmptyClass { }; +gamedll_bridge_info g_bridge_info; + +// Source2 - Rough start order +// CreateInterfaceFn (IS2SC) - hook Connect and AllowDedicatedServer +// IS2SC::Connect - save factory pointer. return orig. remove hook. +// IS2SC::AllowDedicatedServer - return true. remove hook. +// CreateInterfaceFn (IS2S) - hook Init and Shutdown +// IS2S::Init - do same as old ISGD::DLLInit, including core load. return orig. remove hook. +// IS2S::Shutdown - <-- this + +enum InitReturnVal_t +{ + INIT_FAILED = 0, + INIT_OK, + + INIT_LAST_VAL, +}; + +class ISource2ServerConfig +{ +public: + virtual bool Connect(QueryValveInterface factory) + { + g_bridge_info.engineFactory = factory; + g_bridge_info.fsFactory = factory; + g_bridge_info.physicsFactory = factory; + + + /* Call the original */ + bool result; + { + union + { + bool(VEmptyClass::*mfpnew)(QueryValveInterface factory); +#if defined _WIN32 + void *addr; + } u; + u.addr = is2sc_orig_connect; +#else + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + u.s.addr = is2sc_orig_connect; + u.s.adjustor = 0; +#endif + result = (((VEmptyClass *) config_iface)->*u.mfpnew)(factory); + } + + mm_PatchConnect(false); + + return result; + } +#if defined _WIN32 + virtual bool AllowDedicatedServers(int universe) const + { + mm_PatchAllowDedicated(false); + return true; + } +#endif +}; + +class ISource2Server +{ +public: + virtual bool Connect(QueryValveInterface factory) { return true; } + virtual void Disconnect() {} + virtual void *QueryInterface(const char *pInterfaceName) { return nullptr; } + + virtual InitReturnVal_t Init() + { + mm_backend = MMBackend_DOTA; + + char error[255]; + if (!mm_LoadMetamodLibrary(mm_backend, error, sizeof(error))) + { + mm_LogFatal("Detected engine %d but could not load: %s", mm_backend, error); + } + else + { + typedef IGameDllBridge *(*GetGameDllBridge)(); + GetGameDllBridge get_bridge = (GetGameDllBridge)mm_GetProcAddress("GetGameDllBridge"); + if (get_bridge == NULL) + { + mm_UnloadMetamodLibrary(); + mm_LogFatal("Detected engine %d but could not find GetGameDllBridge callback", mm_backend); + } + else + { + gamedll_bridge = get_bridge(); + } + } + + if (gamedll_bridge) + { + g_bridge_info.pGlobals = nullptr;// pGlobals; + g_bridge_info.dllVersion = gamedll_version; + g_bridge_info.isgd = gamedll_iface; + g_bridge_info.gsFactory = gamedll_qvi; + g_bridge_info.vsp_listener_path = mm_path; + + strcpy(error, "Unknown error"); + if (!gamedll_bridge->DLLInit_Pre(&g_bridge_info, error, sizeof(error))) + { + gamedll_bridge = NULL; + mm_UnloadMetamodLibrary(); + mm_LogFatal("Unknown error loading Metamod for engine %d: %s", mm_backend, error); + } + } + + /* Call the original */ + InitReturnVal_t result; + { + union + { + InitReturnVal_t(VEmptyClass::*mfpnew)(); +#if defined _WIN32 + void *addr; + } u; + u.addr = isgd_orig_init; +#else + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + u.s.addr = isgd_orig_init; + u.s.adjustor = 0; +#endif + result = (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(); + } + + /** + * :TODO: possible logic hole here, what happens if the gamedll REALLY returns false? + * I'm pretty sure we'll die horribly. + */ + + if (!result) + { + gamedll_bridge->Unload(); + mm_UnloadMetamodLibrary(); + gamedll_bridge = NULL; + } + else if (gamedll_bridge != NULL) + { + gamedll_bridge->DLLInit_Post(&isgd_shutdown_index); + assert(isgd_shutdown_index != -1); + mm_PatchDllShutdown(); + } + + mm_PatchDllInit(false); + + return result; + } + + virtual void Shutdown() + { + gamedll_bridge->Unload(); + gamedll_bridge = NULL; + mm_UnloadMetamodLibrary(); + + /* Call original function */ + { + union + { + void (VEmptyClass::*mfpnew)(); +#if defined _WIN32 + void *addr; + } u; + u.addr = isgd_orig_shutdown; +#else + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + u.s.addr = isgd_orig_shutdown; + u.s.adjustor = 0; +#endif + (((VEmptyClass *)gamedll_iface)->*u.mfpnew)(); + } + + mm_UnloadLibrary(gamedll_lib); + gamedll_lib = NULL; + } +}; + class IServerGameDLL { public: @@ -244,19 +483,17 @@ public: if (gamedll_bridge) { - gamedll_bridge_info info; - - info.engineFactory = (QueryValveInterface)engineFactory; - info.physicsFactory = (QueryValveInterface)physicsFactory; - info.fsFactory = (QueryValveInterface)fileSystemFactory; - info.pGlobals = pGlobals; - info.dllVersion = gamedll_version; - info.isgd = gamedll_iface; - info.gsFactory = gamedll_qvi; - info.vsp_listener_path = mm_path; + g_bridge_info.engineFactory = (QueryValveInterface)engineFactory; + g_bridge_info.physicsFactory = (QueryValveInterface)physicsFactory; + g_bridge_info.fsFactory = (QueryValveInterface)fileSystemFactory; + g_bridge_info.pGlobals = pGlobals; + g_bridge_info.dllVersion = gamedll_version; + g_bridge_info.isgd = gamedll_iface; + g_bridge_info.gsFactory = gamedll_qvi; + g_bridge_info.vsp_listener_path = mm_path; strcpy(error, "Unknown error"); - if (!gamedll_bridge->DLLInit_Pre(&info, error, sizeof(error))) + if (!gamedll_bridge->DLLInit_Pre(&g_bridge_info, error, sizeof(error))) { gamedll_bridge = NULL; mm_UnloadMetamodLibrary(); @@ -350,6 +587,8 @@ public: }; static IServerGameDLL isgd_thunk; +static ISource2Server is2s_thunk; +static ISource2ServerConfig is2sc_thunk; static void mm_PatchDllInit(bool patch) @@ -358,13 +597,27 @@ mm_PatchDllInit(bool patch) void **vtable_dest; SourceHook::MemFuncInfo mfp; - SourceHook::GetFuncInfo(&IServerGameDLL::DLLInit, mfp); + if (g_is_source2) + { + SourceHook::GetFuncInfo(&ISource2Server::Init, mfp); + } + else + { + SourceHook::GetFuncInfo(&IServerGameDLL::DLLInit, mfp); + } assert(mfp.isVirtual); assert(mfp.thisptroffs == 0); assert(mfp.vtbloffs == 0); - vtable_src = (void **)*(void **)&isgd_thunk; + if (g_is_source2) + { + vtable_src = (void **)*(void **)&is2s_thunk; + } + else + { + vtable_src = (void **)*(void **)&isgd_thunk; + } vtable_dest = (void **)*(void **)gamedll_iface; SourceHook::SetMemAccess(&vtable_dest[mfp.vtblindex], @@ -393,33 +646,169 @@ mm_PatchDllShutdown() SourceHook::MemFuncInfo mfp; mfp.isVirtual = false; - SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfp); + if (g_is_source2) + { + SourceHook::GetFuncInfo(&ISource2Server::Shutdown, mfp); + } + else + { + SourceHook::GetFuncInfo(&IServerGameDLL::DLLShutdown, mfp); + } assert(mfp.isVirtual); assert(mfp.thisptroffs == 0); assert(mfp.vtbloffs == 0); - vtable_src = (void **)*(void **)&isgd_thunk; + if (g_is_source2) + { + vtable_src = (void **)*(void **)&is2s_thunk; + } + else + { + vtable_src = (void **)*(void **)&isgd_thunk; + } vtable_dest = (void **)*(void **)gamedll_iface; isgd_orig_shutdown = vtable_dest[isgd_shutdown_index]; vtable_dest[isgd_shutdown_index] = vtable_src[mfp.vtblindex]; } +#if defined _WIN32 static void -mm_PrepForGameLoad() +mm_PatchAllowDedicated(bool patch) { - mm_PatchDllInit(true); + void **vtable_src; + void **vtable_dest; + SourceHook::MemFuncInfo mfp; + + SourceHook::GetFuncInfo(&ISource2ServerConfig::AllowDedicatedServers, mfp); + + assert(mfp.isVirtual); + assert(mfp.thisptroffs == 0); + assert(mfp.vtbloffs == 0); + + vtable_src = (void **) *(void **) &is2sc_thunk; + vtable_dest = (void **) *(void **) config_iface; + + SourceHook::SetMemAccess(&vtable_dest[is2sc_allowdedi_index], + sizeof(void*), + SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC); + + if (patch) + { + assert(is2sc_orig_allowdedi == NULL); + is2sc_orig_allowdedi = vtable_dest[is2sc_allowdedi_index]; + vtable_dest[is2sc_allowdedi_index] = vtable_src[mfp.vtblindex]; + } + else + { + assert(is2sc_orig_allowdedi != NULL); + vtable_dest[is2sc_allowdedi_index] = is2sc_orig_allowdedi; + is2sc_orig_allowdedi = NULL; + } } +#endif + +static void +mm_PatchConnect(bool patch) +{ + void **vtable_src; + void **vtable_dest; + SourceHook::MemFuncInfo mfp; + + SourceHook::GetFuncInfo(&ISource2ServerConfig::Connect, mfp); + + assert(mfp.isVirtual); + assert(mfp.thisptroffs == 0); + assert(mfp.vtbloffs == 0); + + vtable_src = (void **) *(void **) &is2sc_thunk; + vtable_dest = (void **) *(void **) config_iface; + + SourceHook::SetMemAccess(&vtable_dest[mfp.vtblindex], + sizeof(void*), + SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC); + + if (patch) + { + assert(is2sc_orig_connect == NULL); + is2sc_orig_connect = vtable_dest[mfp.vtblindex]; + vtable_dest[mfp.vtblindex] = vtable_src[mfp.vtblindex]; + } + else + { + assert(is2sc_orig_connect != NULL); + vtable_dest[mfp.vtblindex] = is2sc_orig_connect; + is2sc_orig_connect = NULL; + } +} + void * mm_GameDllRequest(const char *name, int *ret) { - if (gamedll_lib != NULL && gamedll_bridge == NULL) + if (strncmp(name, "Source2ServerConfig", 19) == 0) { - return gamedll_qvi(name, ret); - } + g_is_source2 = true; + if (!mm_DetectGameInformation()) + { + if (ret != NULL) + *ret = 1; + return NULL; + } - if (strncmp(name, "ServerGameDLL", 13) == 0) + void *lib; + char error[255]; + void *ptr = NULL; + QueryValveInterface qvi; + for (unsigned int i = 0; i < gamedll_path_count; i++) + { + if (gamedll_libs[i] == NULL) + { + lib = mm_LoadLibrary(gamedll_paths[i], error, sizeof(error)); + if (lib == NULL) + continue; + gamedll_libs[i] = lib; + } + lib = gamedll_libs[i]; + qvi = (QueryValveInterface)mm_GetLibAddress(lib, "CreateInterface"); + if (qvi == NULL) + continue; + ptr = qvi(name, ret); + if (ptr != NULL) + { + gamedll_libs[i] = NULL; + break; + } + } + + if (ptr != NULL) + { + mm_FreeCachedLibraries(); + gamedll_lib = lib; + config_iface = (ISource2ServerConfig *) ptr; + gamedll_qvi = qvi; + + mm_PatchConnect(true); +#if defined _WIN32 + mm_PatchAllowDedicated(true); +#endif + + if (ret != NULL) + *ret = 0; + return ptr; + } + } + else if (strncmp(name, "Source2Server0", 14) == 0) + { + gamedll_iface = (IServerGameDLL *)gamedll_qvi(name, ret); + gamedll_version = atoi(&name[13]); + mm_PatchDllInit(true); + + if (ret != NULL) + *ret = 0; + return gamedll_iface; + } + else if (strncmp(name, "ServerGameDLL", 13) == 0) { if (!mm_DetectGameInformation()) { @@ -460,13 +849,17 @@ mm_GameDllRequest(const char *name, int *ret) gamedll_iface = (IServerGameDLL *)ptr; gamedll_qvi = qvi; gamedll_version = atoi(&name[13]); - mm_PrepForGameLoad(); + mm_PatchDllInit(true); if (ret != NULL) *ret = 0; return ptr; } } + else if (gamedll_lib != NULL && gamedll_bridge == NULL) + { + return gamedll_qvi(name, ret); + } else if (game_info_detected == 0) { mm_LogFatal("Received interface request too early: %s", name); diff --git a/loader/loader.cpp b/loader/loader.cpp index 601faff..47cffe4 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -2,7 +2,7 @@ * vim: set ts=4 sw=4 tw=99 noet : * ====================================================== * Metamod:Source - * Copyright (C) 2004-2010 AlliedModders LLC and authors. + * Copyright (C) 2004-2015 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * @@ -114,7 +114,12 @@ mm_LoadMetamodLibrary(MetamodBackend backend, char *buffer, size_t maxlength) return false; len = strlen(mm_path); - temp_len = strlen("server" LIBRARY_EXT); + + const char *pLastSlash = strrchr(mm_path, PATH_SEP_CHAR); + if (!pLastSlash) + return false; + + temp_len = strlen(&pLastSlash[1]); if (len < temp_len) return false; @@ -193,6 +198,7 @@ void mm_GetGameName(char *buffer, size_t size) { buffer[0] = '\0'; + bool bHasDedicated = false; #if defined _WIN32 static char game[128]; @@ -202,15 +208,18 @@ mm_GetGameName(char *buffer, size_t size) LPWSTR *wargv = CommandLineToArgvW(pCmdLine, &argc); for (int i = 0; i < argc; ++i) { - if (wcscmp(wargv[i], L"-game") != 0) - continue; + if (wcscmp(wargv[i], L"-game") == 0) + { + if (++i >= argc) + break; - if (++i >= argc) - break; - - wcstombs(buffer, wargv[i], size); - buffer[size-1] = '\0'; - break; + wcstombs(buffer, wargv[i], size); + buffer[size-1] = '\0'; + } + else if (wcscmp(wargv[i], L"-dedicated") == 0) + { + bHasDedicated = true; + } } LocalFree(wargv); @@ -220,15 +229,18 @@ mm_GetGameName(char *buffer, size_t size) char **argv = *_NSGetArgv(); for (int i = 0; i < argc; ++i) { - if (strcmp(argv[i], "-game") != 0) - continue; + if (strcmp(argv[i], "-game") == 0) + { + if (++i >= argc) + break; - if (++i >= argc) - break; - - strncpy(buffer, argv[i], size); - buffer[size-1] = '\0'; - break; + strncpy(buffer, argv[i], size); + buffer[size-1] = '\0'; + } + else if (strcmp(argv[i], "-dedicated") == 0) + { + bHasDedicated = true; + } } #elif defined __linux__ @@ -245,13 +257,17 @@ mm_GetGameName(char *buffer, size_t size) { strncpy(buffer, arg, size); buffer[size-1] = '\0'; - break; + bNextIsGame = false; } if (strcmp(arg, "-game") == 0) { bNextIsGame = true; } + else if (strcmp(arg, "-dedicated") == 0) + { + bHasDedicated = true; + } } free(arg); @@ -263,18 +279,26 @@ mm_GetGameName(char *buffer, size_t size) if (buffer[0] == 0) { - strncpy(buffer, ".", size); + // HackHackHack - Different engines have different defaults if -game isn't specified + // we only use this for game detection, and not even in all cases. Old behavior was to + // give back ".", which was only really accurate for Dark Messiah. We'll add a special + // case for Source2 / Dota as well, since it only supports gameinfo loading, which relies + // on accuracy here more than VSP loading. + if (bHasDedicated) + { + strncpy(buffer, "dota", size); + } + else + { + strncpy(buffer, ".", size); + } } } MetamodBackend mm_DetermineBackend(QueryValveInterface engineFactory, QueryValveInterface serverFactory, const char *game_name) { - if (engineFactory("VEngineServer024", NULL) != NULL) - { - return MMBackend_DOTA; - } - else if (engineFactory("VEngineServer023", NULL) != NULL) + if (engineFactory("VEngineServer023", NULL) != NULL) { if (engineFactory("EngineTraceServer004", NULL) == NULL) { diff --git a/loader/loader.h b/loader/loader.h index 9c11681..9134217 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -2,7 +2,7 @@ * vim: set ts=4 sw=4 tw=99 noet : * ====================================================== * Metamod:Source - * Copyright (C) 2004-2009 AlliedModders LLC and authors. + * Copyright (C) 2004-2015 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * diff --git a/loader/utility.cpp b/loader/utility.cpp index 5de2716..c2af968 100644 --- a/loader/utility.cpp +++ b/loader/utility.cpp @@ -2,7 +2,7 @@ * vim: set ts=4 : * ====================================================== * Metamod:Source - * Copyright (C) 2004-2008 AlliedModders LLC and authors. + * Copyright (C) 2004-2015 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * @@ -275,8 +275,14 @@ mm_PathCmp(const char *path1, const char *path2) } bool -mm_ResolvePath(const char *path, char *buffer, size_t maxlength) +mm_ResolvePath(const char *path, char *buffer, size_t maxlength, bool bSource2) { + char tmp[PLATFORM_MAX_PATH]; + if (bSource2) + { + mm_Format(tmp, sizeof(tmp), "../../%s", path); + path = tmp; + } #if defined _WIN32 return _fullpath(buffer, path, maxlength) != NULL; #elif defined __linux__ || defined __APPLE__ diff --git a/loader/utility.h b/loader/utility.h index c586d1b..25be3e6 100644 --- a/loader/utility.h +++ b/loader/utility.h @@ -2,7 +2,7 @@ * vim: set ts=4 : * ====================================================== * Metamod:Source - * Copyright (C) 2004-2008 AlliedModders LLC and authors. + * Copyright (C) 2004-2015 AlliedModders LLC and authors. * All rights reserved. * ====================================================== * @@ -43,7 +43,7 @@ extern void mm_UnloadLibrary(void *lib); extern bool -mm_ResolvePath(const char *path, char *buffer, size_t maxlength); +mm_ResolvePath(const char *path, char *buffer, size_t maxlength, bool bSource2); extern size_t mm_PathFormat(char *buffer, size_t len, const char *fmt, ...); diff --git a/versionlib/AMBuildScript b/versionlib/AMBuildScript index d46d66e..fb45d8e 100644 --- a/versionlib/AMBuildScript +++ b/versionlib/AMBuildScript @@ -1,11 +1,12 @@ # vim: sts=2 ts=8 sw=2 tw=99 et ft=python: -lib = builder.compiler.StaticLibrary("version") +lib = builder.cxx.StaticLibrary("version") lib.compiler.defines.remove('MMS_USE_VERSIONLIB') lib.compiler.sourcedeps += MMS.generated_headers lib.sources += [ 'versionlib.cpp' ] + cmd = builder.Add(lib) rvalue = cmd.binary