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 @@
/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+