diff --git a/core/ISmmPluginExt.h b/core/ISmmPluginExt.h index 88428df..1ae01b1 100644 --- a/core/ISmmPluginExt.h +++ b/core/ISmmPluginExt.h @@ -42,6 +42,7 @@ #define SOURCE_ENGINE_EPISODEONE 2 /**< Episode 1 Source Engine (second major SDK) */ #define SOURCE_ENGINE_ORANGEBOX 3 /**< Orange Box Source Engine (third major SDK) */ #define SOURCE_ENGINE_LEFT4DEAD 4 /**< Left 4 Dead */ +#define SOURCE_ENGINE_DARKMESSIAH 5 /**< Dark Messiah Multiplayer (based on original engine) */ #define METAMOD_PLAPI_VERSION 14 /**< Version of this header file */ #define METAMOD_PLAPI_NAME "ISmmPlugin" /**< Name of the plugin interface */ diff --git a/core/metamod.cpp b/core/metamod.cpp index 2733e56..c16e554 100644 --- a/core/metamod.cpp +++ b/core/metamod.cpp @@ -194,13 +194,22 @@ mm_DetectGameInformation() metamod_path.assign(mm_path); /* Get value of -game from command line, defaulting to hl2 as engine seems to do */ - const char *game_dir = provider->GetCommandLineValue("-game", "hl2"); + const char *game_dir = provider->GetCommandLineValue("-game"); + + if (game_dir) + { + /* Get absolute path */ + abspath(game_path, game_dir); + } + else + { + /* Get absolute path for current directory */ + abspath(game_path, "."); + } - /* Get absolute path */ - abspath(game_path, game_dir); mod_path.assign(game_path); - engine_build = provider->DetermineSourceEngine(game_dir);; + engine_build = provider->DetermineSourceEngine(game_dir); return true; } diff --git a/core/metamod_console.cpp b/core/metamod_console.cpp index 5aed7dd..65dd567 100644 --- a/core/metamod_console.cpp +++ b/core/metamod_console.cpp @@ -113,10 +113,12 @@ bool Command_Meta(IMetamodSourceCommandInfo *info) } #endif -#if SOURCE_ENGINE == SE_ORANGEBOX +#if SOURCE_ENGINE == SE_LEFT4DEAD + CONMSG(" Engine: Left 4 Dead (2008)\n"); +#elif SOURCE_ENGINE == SE_ORANGEBOX CONMSG(" Engine: Episode 2 (Orange Box, 2007)\n"); #else - CONMSG(" Engine: Left 4 Dead (2008)\n"); + CONMSG(" Engine: Dark Messiah (2006)\n"); #endif // Display user messages diff --git a/core/metamod_provider.h b/core/metamod_provider.h index d548462..c5f8504 100644 --- a/core/metamod_provider.h +++ b/core/metamod_provider.h @@ -115,7 +115,7 @@ namespace SourceMM * @param val Default string to return if none found. * @return Parameter value. */ - virtual const char *GetCommandLineValue(const char *key, const char *defval) =0; + virtual const char *GetCommandLineValue(const char *key, const char *defval=NULL) =0; /** * @brief Prints a string to the remote server console. diff --git a/core/metamod_util.cpp b/core/metamod_util.cpp index 9bf834e..ef9940f 100644 --- a/core/metamod_util.cpp +++ b/core/metamod_util.cpp @@ -341,3 +341,19 @@ bool UTIL_Relatize(char buffer[], return true; } + +bool UTIL_VerifySignature(const void *addr, const char *sig, size_t len) +{ + unsigned char *addr1 = (unsigned char *) addr; + unsigned char *addr2 = (unsigned char *) sig; + + for (size_t i = 0; i < len; i++) + { + if (addr2[i] == '*') + continue; + if (addr1[i] != addr2[i]) + return false; + } + + return true; +} diff --git a/core/metamod_util.h b/core/metamod_util.h index c2b02bb..57b456b 100644 --- a/core/metamod_util.h +++ b/core/metamod_util.h @@ -85,5 +85,15 @@ bool UTIL_Relatize(char buffer[], const char *relTo, const char *relFrom); +/** + * @brief Compares memory address against a signature. + * + * @param addr Memory address to check. + * @param sig Signature used to check against memory address. Accept 0x2A as wildcard. + * @param len Length of signature. + * @return True if signature was verified, false otherwise. + */ +bool UTIL_VerifySignature(const void *addr, const char *sig, size_t len); + #endif //_INCLUDE_UTIL_H diff --git a/core/msvc9/mm_core.sln b/core/msvc9/mm_core.sln index e0fcb9a..aeab1d3 100644 --- a/core/msvc9/mm_core.sln +++ b/core/msvc9/mm_core.sln @@ -5,16 +5,22 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mm_core", "mm_core.vcproj", EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - Dark Messiah|Win32 = Debug - Dark Messiah|Win32 Debug - Left 4 Dead|Win32 = Debug - Left 4 Dead|Win32 Debug - Orange Box|Win32 = Debug - Orange Box|Win32 + Release - Dark Messiah|Win32 = Release - Dark Messiah|Win32 Release - Left 4 Dead|Win32 = Release - Left 4 Dead|Win32 Release - Orange Box|Win32 = Release - Orange Box|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Debug - Dark Messiah|Win32.ActiveCfg = Debug - Dark Messiah|Win32 + {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Debug - Dark Messiah|Win32.Build.0 = Debug - Dark Messiah|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Debug - Left 4 Dead|Win32.ActiveCfg = Debug - Left 4 Dead|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Debug - Left 4 Dead|Win32.Build.0 = Debug - Left 4 Dead|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32 + {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Release - Dark Messiah|Win32.ActiveCfg = Release - Dark Messiah|Win32 + {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Release - Dark Messiah|Win32.Build.0 = Release - Dark Messiah|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Release - Left 4 Dead|Win32.ActiveCfg = Release - Left 4 Dead|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Release - Left 4 Dead|Win32.Build.0 = Release - Left 4 Dead|Win32 {F7D47743-73B3-49B5-9D76-2333C5DFD565}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32 diff --git a/core/msvc9/mm_core.vcproj b/core/msvc9/mm_core.vcproj index bb64853..a21611a 100644 --- a/core/msvc9/mm_core.vcproj +++ b/core/msvc9/mm_core.vcproj @@ -40,9 +40,10 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/provider/console.cpp b/core/provider/console.cpp index ed81334..2a70924 100644 --- a/core/provider/console.cpp +++ b/core/provider/console.cpp @@ -27,13 +27,20 @@ #include "console.h" #include "provider_ep2.h" +#include "metamod_util.h" using namespace SourceHook; SMConVarAccessor g_SMConVarAccessor; +#if SOURCE_ENGINE >= SE_ORANGEBOX +#else +#define RegisterConCommand RegisterConCommandBase +#endif + bool SMConVarAccessor::RegisterConCommandBase(ConCommandBase *pCommand) { + m_RegisteredCommands.push_back(pCommand); pCommand->SetNext(NULL); icvar->RegisterConCommand(pCommand); @@ -48,8 +55,128 @@ bool SMConVarAccessor::Register(ConCommandBase *pCommand) return true; } -void SMConVarAccessor::Unregister(ConCommandBase *pCommand) +void SMConVarAccessor::RemoveMetamodCommands() { - icvar->UnregisterConCommand(pCommand); + List::iterator iter; + + for (iter = m_RegisteredCommands.begin(); iter != m_RegisteredCommands.end(); iter++) + { + Unregister(*iter); + } +} + +#if SOURCE_ENGINE == SE_DARKMESSIAH +/* Signature for ICvar::GetCommands() in vstdlib for Win32 and Linux. + * + * 20226EE0 A1 50 5C 5A 20 mov eax,dword ptr ds:[205A5C50h] <-- What we want + * 20226EE5 C3 ret + */ +#define CMDLIST_SIG "\xA1\x2A\x2A\x2A\x2A\xC3" +#define CMDLIST_SIGLEN 6 + +/* Linux symbol name of ConCommandBase list in vstdlib */ +#define CMDLIST_SYMBOL "_ZN14ConCommandBase18s_pConCommandBasesE" + +/* This function retrieves the address of the var that holds the top of the ConCommandBase list. + * Having this allows us to change the beginning of this list with ease. + * + * This craziness eliminates the need for the eternal command/cvar used previously which + * could have caused a crash as a result of registering commands/cvars more than once. + */ +bool SMConVarAccessor::InitConCommandBaseList() +{ + char *vfunc = (char *)SH_GET_ORIG_VFNPTR_ENTRY(icvar, &ICvar::GetCommands); + + if (*vfunc == '\xE9') + { + /* Get address from displacement... + * + * Add 5 because it's relative to next instruction: + * Opcode <1 byte> + 32-bit displacement <4 bytes> + */ + vfunc += *reinterpret_cast(vfunc + 1) + 5; + } + + if (!vfunc) + { + return false; + } + +#ifdef OS_WIN32 + if (UTIL_VerifySignature(vfunc, CMDLIST_SIG, CMDLIST_SIGLEN)) + { + /* Skip past 0xA1 and get addr of ConCommandBase list var */ + m_TopConCommandBase = *reinterpret_cast(vfunc + 1); + return true; + } +#elif defined OS_LINUX + /* Try dlsym first */ + char path[PATH_SIZE]; + if (GetFileOfAddress((void *)icvar, path, sizeof(path))) + { + void *handle = dlopen(path, RTLD_NOW); + if (handle) + { + m_TopConCommandBase = reinterpret_cast(dlsym(handle, CMDLIST_SYMBOL)); + dlclose(handle); + return true; + } + } + + /* If dlsym failed, then verify signature of function */ + if (!m_TopConCommandBase && UTIL_VerifySignature(vfunc, CMDLIST_SIG, CMDLIST_SIGLEN)) + { + /* Skip past 0xA1 and get addr of ConCommandBase list var */ + m_TopConCommandBase = *reinterpret_cast(vfunc + 1); + return true; + } +#endif + + return false; +} +#endif + +void SMConVarAccessor::Unregister(ConCommandBase *pCommand) +{ +#if SOURCE_ENGINE >= SE_ORANGEBOX + icvar->UnregisterConCommand(pCommand); +#else + ConCommandBase *pCur = NULL; + ConCommandBase *pPrev = NULL; + + if (!pCommand) + { + return; + } + + pCur = icvar->GetCommands(); + + if (!m_TopConCommandBase || !pCur) + { + return; + } + + if (pCur == pCommand) + { + *m_TopConCommandBase = const_cast(pCommand->GetNext()); + pCommand->SetNext(NULL); + return; + } + + pPrev = pCur; + pCur = const_cast(pCur->GetNext()); + + while (pCur) + { + if (pCur == pCommand) + { + pPrev->SetNext(const_cast(pCommand->GetNext())); + pCommand->SetNext(NULL); + } + + pPrev = pCur; + pCur = const_cast(pCur->GetNext()); + } +#endif } diff --git a/core/provider/console.h b/core/provider/console.h index 06b4323..ad56755 100644 --- a/core/provider/console.h +++ b/core/provider/console.h @@ -33,8 +33,8 @@ #undef _DEBUG #endif #include -#include #include "convar.h" +#include #include #if defined DEBUG2 #undef DEBUG2 @@ -47,6 +47,14 @@ public: bool RegisterConCommandBase(ConCommandBase *pCommand); bool Register(ConCommandBase *pCommand); void Unregister(ConCommandBase *pCommand); + void RemoveMetamodCommands(); +#if SOURCE_ENGINE == SE_DARKMESSIAH + bool InitConCommandBaseList(); +private: + ConCommandBase **m_TopConCommandBase; +#endif +private: + SourceHook::List m_RegisteredCommands; }; extern SMConVarAccessor g_SMConVarAccessor; diff --git a/core/provider/provider_ep2.cpp b/core/provider/provider_ep2.cpp index fedf300..b10a364 100644 --- a/core/provider/provider_ep2.cpp +++ b/core/provider/provider_ep2.cpp @@ -56,13 +56,23 @@ struct UsrMsgInfo int size; String name; }; + /* Imports */ +#if SOURCE_ENGINE == SE_DARKMESSIAH #undef CommandLine DLL_IMPORT ICommandLine *CommandLine(); +#endif + /* Functions */ bool CacheUserMessages(); +#if SOURCE_ENGINE >= SE_ORANGEBOX void ClientCommand(edict_t *pEdict, const CCommand &args); void LocalCommand_Meta(const CCommand &args); +#else +void ClientCommand(edict_t *pEdict); +void LocalCommand_Meta(); +#endif + void _ServerCommand(); /* Variables */ static bool usermsgs_extracted = false; @@ -79,7 +89,11 @@ IServerGameClients *gameclients = NULL; IMetamodSourceProvider *provider = &g_Ep1Provider; ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options"); +#if SOURCE_ENGINE >= SE_ORANGEBOX SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &); +#else +SH_DECL_HOOK1_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *); +#endif bool AssumeUserMessages() { @@ -99,7 +113,11 @@ bool AssumeUserMessages() void BaseProvider::ConsolePrint(const char *str) { +#if SOURCE_ENGINE >= SE_ORANGEBOX ConMsg("%s", str); +#else + Msg("%s", str); +#endif } void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, @@ -111,13 +129,17 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, DisplayError("Could not find IVEngineServer! Metamod cannot load."); return; } +#if SOURCE_ENGINE >= SE_ORANGEBOX icvar = (ICvar *)((engineFactory)(CVAR_INTERFACE_VERSION, NULL)); +#else + icvar = (ICvar *)((engineFactory)(VENGINE_CVAR_INTERFACE_VERSION, NULL)); +#endif if (!icvar) { DisplayError("Could not find ICvar! Metamod cannot load."); return; } - g_pCVar = icvar; + if ((gameclients = (IServerGameClients *)(serverFactory("ServerGameClients003", NULL))) == NULL) @@ -132,14 +154,26 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, return; } - RegisterConCommandBase(&meta_local_cmd); - conbases_unreg.push_back(&meta_local_cmd); +#if SOURCE_ENGINE >= SE_ORANGEBOX + g_pCVar = icvar; +#endif + + g_SMConVarAccessor.RegisterConCommandBase(&meta_local_cmd); if ((usermsgs_extracted = CacheUserMessages()) == false) { usermsgs_extracted = AssumeUserMessages(); } +#if SOURCE_ENGINE == SE_DARKMESSIAH + if (!g_SMConVarAccessor.InitConCommandBaseList()) + { + /* This is very unlikely considering it's old engine */ + mm_LogMessage("[META] Warning: Failed to find ConCommandBase list!"); + mm_LogMessage("[META] Warning: ConVars and ConCommands cannot be unregistered properly! Please file a bug report."); + } +#endif + if (gameclients) { SH_ADD_HOOK_STATICFUNC(IServerGameClients, ClientCommand, gameclients, ClientCommand, false); @@ -148,14 +182,14 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, void BaseProvider::Notify_DLLShutdown_Pre() { - List::iterator iter; + g_SMConVarAccessor.RemoveMetamodCommands(); - for (iter = conbases_unreg.begin(); - iter != conbases_unreg.end(); - iter++) +#if SOURCE_ENGINE == SE_DARKMESSIAH + if (g_Metamod.IsLoadedAsGameDLL()) { - UnregisterConCommandBase((*iter)); + icvar->UnlinkVariables(FCVAR_GAMEDLL); } +#endif } bool BaseProvider::IsRemotePrintingAvailable() @@ -204,7 +238,7 @@ const char *BaseProvider::GetCommandLineValue(const char *key, const char *defva { if (key[0] == '-' || key[0] == '+') { - return CommandLine_Tier0()->ParmValue(key, defval); + return CommandLine()->ParmValue(key, defval); } else if (icvar) { @@ -354,8 +388,10 @@ int BaseProvider::DetermineSourceEngine(const char *game) { #if SOURCE_ENGINE == SE_LEFT4DEAD return SOURCE_ENGINE_LEFT4DEAD; -#else +#elif SOURCE_ENGINE == SE_ORANGEBOX return SOURCE_ENGINE_ORANGEBOX; +#else + return SOURCE_ENGINE_DARKMESSIAH; #endif } @@ -376,8 +412,7 @@ ConVar *BaseProvider::CreateConVar(const char *name, ConVar *pVar = new ConVar(name, defval, newflags, help); - RegisterConCommandBase(pVar); - conbases_unreg.push_back(pVar); + g_SMConVarAccessor.RegisterConCommandBase(pVar); return pVar; } @@ -433,6 +468,7 @@ bool BaseProvider::ProcessVDF(const char *file, char path[], size_t path_len, ch return true; } +#if SOURCE_ENGINE >= SE_ORANGEBOX class GlobCommand : public IMetamodSourceCommandInfo { public: @@ -457,17 +493,48 @@ public: private: const CCommand *m_cmd; }; +#else +class GlobCommand : public IMetamodSourceCommandInfo +{ +public: + unsigned int GetArgCount() + { + return engine->Cmd_Argc() - 1; + } + const char *GetArg(unsigned int num) + { + return engine->Cmd_Argv(num); + } + + const char *GetArgString() + { + return engine->Cmd_Args(); + } +}; +#endif + +#if SOURCE_ENGINE >= SE_ORANGEBOX void LocalCommand_Meta(const CCommand &args) { GlobCommand cmd(&args); +#else +void LocalCommand_Meta() +{ + GlobCommand cmd; +#endif Command_Meta(&cmd); } +#if SOURCE_ENGINE >= SE_ORANGEBOX void ClientCommand(edict_t *pEdict, const CCommand &_cmd) { GlobCommand cmd(&_cmd); - +#else +void ClientCommand(edict_t *pEdict) +{ + GlobCommand cmd; +#endif if (strcmp(cmd.GetArg(0), "meta") == 0) { Command_ClientMeta(pEdict, &cmd); @@ -477,26 +544,6 @@ void ClientCommand(edict_t *pEdict, const CCommand &_cmd) RETURN_META(MRES_IGNORED); } -bool vcmp(const void *_addr1, const void *_addr2, size_t len) -{ - unsigned char *addr1 = (unsigned char *)_addr1; - unsigned char *addr2 = (unsigned char *)_addr2; - - for (size_t i=0; i UserMsgDict; -/* This is the ugliest function in all of SourceMM */ +/* This is the ugliest function in all of MM:S */ bool CacheUserMessages() { + UserMsgDict *dict = NULL; + /* Get address of original GetUserMessageInfo() */ char *vfunc = (char *)SH_GET_ORIG_VFNPTR_ENTRY(server, &IServerGameDLL::GetUserMessageInfo); @@ -558,9 +607,7 @@ bool CacheUserMessages() vfunc += *reinterpret_cast(vfunc + 1) + 5; } - CUtlDict *dict = NULL; - - if (vcmp(vfunc, MSGCLASS_SIG, MSGCLASS_SIGLEN)) + if (UTIL_VerifySignature(vfunc, MSGCLASS_SIG, MSGCLASS_SIGLEN)) { /* Get address of CUserMessages instance */ char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS_OFFS); @@ -568,7 +615,7 @@ bool CacheUserMessages() /* Get address of CUserMessages::m_UserMessages */ dict = reinterpret_cast(*userMsgClass); } - else if (vcmp(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) + else if (UTIL_VerifySignature(vfunc, MSGCLASS2_SIG, MSGCLASS2_SIGLEN)) { #ifdef OS_WIN32 /* If we get here, the code is possibly inlined like in Dystopia */ @@ -587,7 +634,7 @@ bool CacheUserMessages() #endif #ifdef OS_WIN32 } - else if (vcmp(vfunc, MSGCLASS3_SIG, MSGCLASS3_SIGLEN)) + else if (UTIL_VerifySignature(vfunc, MSGCLASS3_SIG, MSGCLASS3_SIGLEN)) { /* Get address of CUserMessages instance */ char **userMsgClass = *reinterpret_cast(vfunc + MSGCLASS3_OFFS); diff --git a/core/provider/vsp_listener.cpp b/core/provider/vsp_listener.cpp index 0204497..eede351 100644 --- a/core/provider/vsp_listener.cpp +++ b/core/provider/vsp_listener.cpp @@ -48,7 +48,11 @@ void VSPListener::ClientActive(edict_t *pEntity) { } +#if SOURCE_ENGINE >= SE_ORANGEBOX PLUGIN_RESULT VSPListener::ClientCommand(edict_t *pEntity, const CCommand &cmd) +#else +PLUGIN_RESULT VSPListener::ClientCommand(edict_t *pEntity) +#endif { return PLUGIN_CONTINUE; } @@ -134,6 +138,7 @@ bool VSPListener::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gam return true; } +#if SOURCE_ENGINE != SE_DARKMESSIAH void VSPListener::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, @@ -141,4 +146,5 @@ void VSPListener::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, const char *pCvarValue) { } +#endif diff --git a/core/provider/vsp_listener.h b/core/provider/vsp_listener.h index ab65dcd..52a6ac6 100644 --- a/core/provider/vsp_listener.h +++ b/core/provider/vsp_listener.h @@ -38,6 +38,8 @@ #define _DEBUG #endif + + class VSPListener : public IServerPluginCallbacks { public: @@ -58,9 +60,15 @@ public: virtual void SetCommandClient(int index); virtual void ClientSettingsChanged(edict_t *pEdict); virtual PLUGIN_RESULT ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); +#if SOURCE_ENGINE >= SE_ORANGEBOX virtual PLUGIN_RESULT ClientCommand(edict_t *pEntity, const CCommand &cmd); +#else + virtual PLUGIN_RESULT ClientCommand(edict_t *pEntity); +#endif virtual PLUGIN_RESULT NetworkIDValidated(const char *pszUserName, const char *pszNetworkID); +#if SOURCE_ENGINE != SE_DARKMESSIAH virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue ); +#endif public: bool IsLoaded(); void SetLoadable(bool loadable); diff --git a/core/vsp_bridge.cpp b/core/vsp_bridge.cpp index a6b389b..bc7eb42 100644 --- a/core/vsp_bridge.cpp +++ b/core/vsp_bridge.cpp @@ -34,17 +34,29 @@ #include #include "provider/provider_ep2.h" +#if SOURCE_ENGINE >= SE_ORANGEBOX SH_DECL_HOOK1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &); +#else +SH_DECL_HOOK0_void(ConCommand, Dispatch, SH_NOATTRIB, false); +#endif ConCommand *g_plugin_unload = NULL; bool g_bIsTryingToUnload; +#if SOURCE_ENGINE >= SE_ORANGEBOX void InterceptPluginUnloads(const CCommand &args) +#else +void InterceptPluginUnloads() +#endif { g_bIsTryingToUnload = true; } +#if SOURCE_ENGINE >= SE_ORANGEBOX void InterceptPluginUnloads_Post(const CCommand &args) +#else +void InterceptPluginUnloads_Post() +#endif { g_bIsTryingToUnload = false; } @@ -69,7 +81,7 @@ public: pGlobals = playerInfoManager->GetGlobalVars(); char gamedll_iface[] = "ServerGameDLL000"; - for (unsigned int i = 5; i <= 50; i++) + for (unsigned int i = 3; i <= 50; i++) { gamedll_iface[15] = '0' + i; if ((server = (IServerGameDLL *)info->gsFactory(gamedll_iface, NULL)) != NULL) @@ -107,7 +119,20 @@ public: g_Metamod.NotifyVSPListening(info->vsp_callbacks, info->vsp_version); mm_StartupMetamod(true); +#if SOURCE_ENGINE >= SE_ORANGEBOX g_plugin_unload = icvar->FindCommand("plugin_unload"); +#else + const ConCommandBase *pBase = icvar->GetCommands(); + while (pBase != NULL) + { + if (pBase->IsCommand() && strcmp(pBase->GetName(), "plugin_unload") == 0) + { + g_plugin_unload = (ConCommand *)pBase; + break; + } + pBase = pBase->GetNext(); + } +#endif if (g_plugin_unload != NULL) { diff --git a/loader/gamedll.cpp b/loader/gamedll.cpp index db21050..9209cbd 100644 --- a/loader/gamedll.cpp +++ b/loader/gamedll.cpp @@ -36,17 +36,14 @@ #include #include "utility.h" #include "gamedll.h" -#include "valve_commandline.h" - -#undef GetCommandLine class IServerGameDLL; -typedef ICommandLine *(*GetCommandLine)(); #define MAX_GAMEDLL_PATHS 10 IGameDllBridge* gamedll_bridge = NULL; static int game_info_detected = 0; +static const char *game_name = NULL; static char gamedll_paths[MAX_GAMEDLL_PATHS][PLATFORM_MAX_PATH]; static void *gamedll_libs[MAX_GAMEDLL_PATHS]; static unsigned int gamedll_path_count = 0; @@ -57,24 +54,15 @@ static int gamedll_version = 0; static int isgd_shutdown_index = -1; #if defined _WIN32 -#define TIER0_NAME "bin\\tier0.dll" -#define VSTDLIB_NAME "bin\\vstdlib.dll" #define SERVER_NAME "server.dll" #elif defined __linux__ -#define TIER0_NAME "bin/tier0_i486.so" -#define VSTDLIB_NAME "bin/vstdlib_i486.so" #define SERVER_NAME "server_i486.so" #endif static bool mm_DetectGameInformation() { - void *lib; - char error[255]; - GetCommandLine valve_cmdline; char mm_path[PLATFORM_MAX_PATH]; - char lib_path[PLATFORM_MAX_PATH]; - char game_name[PLATFORM_MAX_PATH]; char game_path[PLATFORM_MAX_PATH]; if (game_info_detected) @@ -82,43 +70,8 @@ mm_DetectGameInformation() game_info_detected = -1; - if (!mm_ResolvePath(TIER0_NAME, lib_path, sizeof(lib_path))) + if ((game_name = mm_GetGameName()) == NULL) { - mm_LogFatal("Could not find path for: " TIER0_NAME); - return false; - } - - if ((lib = mm_LoadLibrary(lib_path, error, sizeof(error))) == NULL) - { - mm_LogFatal("Could not load %s: %s", lib_path, error); - return false; - } - - valve_cmdline = (GetCommandLine)mm_GetLibAddress(lib, "CommandLine_Tier0"); - if (valve_cmdline == NULL) - { - /* We probably have a Ship engine. */ - mm_UnloadLibrary(lib); - if (!mm_ResolvePath(VSTDLIB_NAME, lib_path, sizeof(lib_path))) - { - mm_LogFatal("Could not find path for: " VSTDLIB_NAME); - return false; - } - - if ((lib = mm_LoadLibrary(lib_path, error, sizeof(error))) == NULL) - { - mm_LogFatal("Could not load %s: %s", lib_path, error); - return false; - } - - valve_cmdline = (GetCommandLine)mm_GetLibAddress(lib, "CommandLine"); - } - - mm_UnloadLibrary(lib); - - if (valve_cmdline == NULL) - { - mm_LogFatal("Could not locate any command line functionality"); return false; } @@ -128,10 +81,6 @@ mm_DetectGameInformation() return false; } - mm_Format(game_name, - sizeof(game_name), - "%s", - valve_cmdline()->ParmValue("-game", "hl2")); if (!mm_ResolvePath(game_name, game_path, sizeof(game_path))) { mm_LogFatal("Could not resolve path: %s", game_name); @@ -261,7 +210,7 @@ public: QueryValveInterface fileSystemFactory, void *pGlobals) { - MetamodBackend backend = mm_DetermineBackend(engineFactory); + MetamodBackend backend = mm_DetermineBackend(engineFactory, game_name); char error[255]; if (backend == MMBackend_UNKNOWN) diff --git a/loader/loader.cpp b/loader/loader.cpp index 9c6802a..4442a5e 100644 --- a/loader/loader.cpp +++ b/loader/loader.cpp @@ -35,6 +35,11 @@ #include "serverplugin.h" #include "gamedll.h" #include "utility.h" +#include "valve_commandline.h" + +#undef GetCommandLine + +typedef ICommandLine *(*GetCommandLine)(); static HMODULE mm_library = NULL; static char mm_fatal_logfile[PLATFORM_MAX_PATH] = "metamod-fatal.log"; @@ -64,11 +69,12 @@ mm_LogFatal(const char *message, ...) fclose(fp); } -static const char *backend_names[3] = +static const char *backend_names[] = { "1.ep1", "2.ep2", - "2.l4d" + "2.l4d", + "2.darkm" }; #if defined _WIN32 @@ -163,8 +169,76 @@ mm_GetProcAddress(const char *name) return mm_GetLibAddress(mm_library, name); } +#if defined _WIN32 +#define TIER0_NAME "bin\\tier0.dll" +#define VSTDLIB_NAME "bin\\vstdlib.dll" +#elif defined __linux__ +#define TIER0_NAME "bin/tier0_i486.so" +#define VSTDLIB_NAME "bin/vstdlib_i486.so" +#endif + +const char * +mm_GetGameName() +{ + void *lib; + char error[255]; + GetCommandLine valve_cmdline; + char lib_path[PLATFORM_MAX_PATH]; + const char *game_name; + + if (!mm_ResolvePath(TIER0_NAME, lib_path, sizeof(lib_path))) + { + mm_LogFatal("Could not find path for: " TIER0_NAME); + return NULL; + } + + if ((lib = mm_LoadLibrary(lib_path, error, sizeof(error))) == NULL) + { + mm_LogFatal("Could not load %s: %s", lib_path, error); + return NULL; + } + + valve_cmdline = (GetCommandLine)mm_GetLibAddress(lib, "CommandLine_Tier0"); + if (valve_cmdline == NULL) + { + /* We probably have a Ship engine. */ + mm_UnloadLibrary(lib); + if (!mm_ResolvePath(VSTDLIB_NAME, lib_path, sizeof(lib_path))) + { + mm_LogFatal("Could not find path for: " VSTDLIB_NAME); + return NULL; + } + + if ((lib = mm_LoadLibrary(lib_path, error, sizeof(error))) == NULL) + { + mm_LogFatal("Could not load %s: %s", lib_path, error); + return NULL; + } + + valve_cmdline = (GetCommandLine)mm_GetLibAddress(lib, "CommandLine"); + } + + mm_UnloadLibrary(lib); + + if (valve_cmdline == NULL) + { + mm_LogFatal("Could not locate any command line functionality"); + return NULL; + } + + game_name = valve_cmdline()->ParmValue("-game"); + + /* This probably means that the game directory is actually the current directory */ + if (!game_name) + { + game_name = "."; + } + + return game_name; +} + MetamodBackend -mm_DetermineBackend(QueryValveInterface engineFactory) +mm_DetermineBackend(QueryValveInterface engineFactory, const char *game_name) { /* Check for L4D */ if (engineFactory("VEngineServer022", NULL) != NULL && @@ -180,11 +254,16 @@ mm_DetermineBackend(QueryValveInterface engineFactory) { return MMBackend_Episode2; } - /* Check for EP1 */ + /* Check for Episode One/Old Engine */ else if (engineFactory("VModelInfoServer001", NULL) != NULL && (engineFactory("VEngineCvar003", NULL) != NULL || engineFactory("VEngineCvar002", NULL) != NULL)) { + /* Check for Dark Messiah which has a weird directory structure */ + if (strcmp(game_name, ".") == 0) + { + return MMBackend_DarkMessiah; + } return MMBackend_Episode1; } } diff --git a/loader/loader.h b/loader/loader.h index 8d4bc83..e8c47e1 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -64,6 +64,7 @@ enum MetamodBackend MMBackend_Episode1 = 0, MMBackend_Episode2, MMBackend_Left4Dead, + MMBackend_DarkMessiah, MMBackend_UNKNOWN }; @@ -79,8 +80,11 @@ mm_UnloadMetamodLibrary(); extern void mm_LogFatal(const char *message, ...); +extern const char * +mm_GetGameName(); + extern MetamodBackend -mm_DetermineBackend(QueryValveInterface qvi); +mm_DetermineBackend(QueryValveInterface qvi, const char *game_name); #endif /* _INCLUDE_METAMOD_SOURCE_LOADER_H_ */ diff --git a/loader/serverplugin.cpp b/loader/serverplugin.cpp index e4d8ff7..6bcef2b 100644 --- a/loader/serverplugin.cpp +++ b/loader/serverplugin.cpp @@ -77,6 +77,7 @@ IVspBridge *vsp_bridge = NULL; */ class ServerPlugin { + const char *game_name; unsigned int vsp_version; bool load_allowed; public: @@ -91,14 +92,19 @@ public: load_allowed = false; - MetamodBackend backend = mm_DetermineBackend(engineFactory); + if ((game_name = mm_GetGameName()) == NULL) + { + return false; + } + + MetamodBackend backend = mm_DetermineBackend(engineFactory, game_name); if (backend == MMBackend_UNKNOWN) { mm_LogFatal("Could not detect engine version"); return false; } - else if (backend >= MMBackend_Episode2) + else if (backend == MMBackend_Episode2 || backend == MMBackend_Left4Dead) { /* We need to insert the right type of call into this vtable */ void **vtable_src; diff --git a/sample_mm/engine_wrappers.h b/sample_mm/engine_wrappers.h index d7479ac..be242d6 100644 --- a/sample_mm/engine_wrappers.h +++ b/sample_mm/engine_wrappers.h @@ -22,10 +22,9 @@ extern IVEngineServer *engine; extern CGlobalVars *gpGlobals; -/** - * For non-OrangeBox builds, we have to make wrappers. - */ -#if SOURCE_ENGINE == SE_EPISODEONE +#if SOURCE_ENGINE == SE_EPISODEONE && defined METAMOD_PLAPI_VERSION +#error "Metamod:Source 1.6 API is not supported on the old engine." +#endif /** * MM:S 1.4.x needs older API calls. @@ -35,12 +34,15 @@ extern CGlobalVars *gpGlobals; #define GetServerFactory serverFactory #define MM_Format snprintf #define GetCGlobals pGlobals +#define ENGINE_CALL(func) SH_CALL(m_EngineCC, func) #else -#error "Metamod:Source 1.6 is not supported on the old engine." +#define ENGINE_CALL(func) SH_CALL(engine, func) +#define MM_Format g_SMAPI->Format #endif +#if SOURCE_ENGINE <= SE_DARKMESSIAH /** - * Wrap the CCommand class so our code looks the same for both engines. + * Wrap the CCommand class so our code looks the same on all engines. */ class CCommand { @@ -60,14 +62,7 @@ public: } }; -#define CVAR_INTERFACE_VERSION VENGINE_CVAR_INTERFACE_VERSION -#define ENGINE_CALL(func) SH_CALL(m_EngineCC, func) - -#elif SOURCE_ENGINE >= SE_ORANGEBOX - -#define ENGINE_CALL(func) SH_CALL(engine, func) -#define MM_Format g_SMAPI->Format - +#define CVAR_INTERFACE_VERSION VENGINE_CVAR_INTERFACE_VERSION #endif /** diff --git a/sample_mm/msvc8/sample_mm.sln b/sample_mm/msvc8/sample_mm.sln index 100c893..575c641 100644 --- a/sample_mm/msvc8/sample_mm.sln +++ b/sample_mm/msvc8/sample_mm.sln @@ -5,20 +5,26 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sample_mm", "sample_mm.vcpr EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - Dark Messiah|Win32 = Debug - Dark Messiah|Win32 Debug - Left 4 Dead|Win32 = Debug - Left 4 Dead|Win32 Debug - Orange Box|Win32 = Debug - Orange Box|Win32 Debug - Original|Win32 = Debug - Original|Win32 + Release - Dark Messiah|Win32 = Release - Dark Messiah|Win32 Release - Left 4 Dead|Win32 = Release - Left 4 Dead|Win32 Release - Orange Box|Win32 = Release - Orange Box|Win32 Release - Original|Win32 = Release - Original|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Dark Messiah|Win32.ActiveCfg = Debug - Dark Messiah|Win32 + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Dark Messiah|Win32.Build.0 = Debug - Dark Messiah|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Left 4 Dead|Win32.ActiveCfg = Debug - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Left 4 Dead|Win32.Build.0 = Debug - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Original|Win32.ActiveCfg = Debug - Original|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Original|Win32.Build.0 = Debug - Original|Win32 + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Dark Messiah|Win32.ActiveCfg = Release - Dark Messiah|Win32 + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Dark Messiah|Win32.Build.0 = Release - Dark Messiah|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Left 4 Dead|Win32.ActiveCfg = Release - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Left 4 Dead|Win32.Build.0 = Release - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32 @@ -27,6 +33,6 @@ Global {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Original|Win32.Build.0 = Release - Original|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution - HideSolutionNodde = FALSE + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/sample_mm/msvc8/sample_mm.vcproj b/sample_mm/msvc8/sample_mm.vcproj index b56a8f1..f1463c9 100644 --- a/sample_mm/msvc8/sample_mm.vcproj +++ b/sample_mm/msvc8/sample_mm.vcproj @@ -39,10 +39,10 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *m_EngineCC; #endif }; diff --git a/stub_mm/msvc8/stub_mm.sln b/stub_mm/msvc8/stub_mm.sln index fd99f3c..cc6035e 100644 --- a/stub_mm/msvc8/stub_mm.sln +++ b/stub_mm/msvc8/stub_mm.sln @@ -5,20 +5,26 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stub_mm", "stub_mm.vcproj", EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug - Dark Messiah|Win32 = Debug - Dark Messiah|Win32 Debug - Left 4 Dead|Win32 = Debug - Left 4 Dead|Win32 Debug - Orange Box|Win32 = Debug - Orange Box|Win32 Debug - Original|Win32 = Debug - Original|Win32 + Release - Dark Messiah|Win32 = Release - Dark Messiah|Win32 Release - Left 4 Dead|Win32 = Release - Left 4 Dead|Win32 Release - Orange Box|Win32 = Release - Orange Box|Win32 Release - Original|Win32 = Release - Original|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Dark Messiah|Win32.ActiveCfg = Debug - Dark Messiah|Win32 + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Dark Messiah|Win32.Build.0 = Debug - Dark Messiah|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Left 4 Dead|Win32.ActiveCfg = Debug - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Left 4 Dead|Win32.Build.0 = Debug - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Orange Box|Win32.ActiveCfg = Debug - Orange Box|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Orange Box|Win32.Build.0 = Debug - Orange Box|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Original|Win32.ActiveCfg = Debug - Original|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Debug - Original|Win32.Build.0 = Debug - Original|Win32 + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Dark Messiah|Win32.ActiveCfg = Release - Dark Messiah|Win32 + {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Dark Messiah|Win32.Build.0 = Release - Dark Messiah|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Left 4 Dead|Win32.ActiveCfg = Release - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Left 4 Dead|Win32.Build.0 = Release - Left 4 Dead|Win32 {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Orange Box|Win32.ActiveCfg = Release - Orange Box|Win32 @@ -27,6 +33,6 @@ Global {E62E5876-E1E2-41A0-85CA-1B41B9DA55F9}.Release - Original|Win32.Build.0 = Release - Original|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/stub_mm/msvc8/stub_mm.vcproj b/stub_mm/msvc8/stub_mm.vcproj index da04eb0..35e484a 100644 --- a/stub_mm/msvc8/stub_mm.vcproj +++ b/stub_mm/msvc8/stub_mm.vcproj @@ -39,7 +39,7 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +