diff --git a/sourcemm/changelog.txt b/sourcemm/changelog.txt
index f479ba2..1c93be6 100644
--- a/sourcemm/changelog.txt
+++ b/sourcemm/changelog.txt
@@ -14,6 +14,8 @@
- Added API for getting the VSP-simulation interface upon late loading.
- Added OnUnlinkConCommandBase to IMetamodListner to notify when Metamod:Source
is about to remove a concommand or convar.
+ - Added the ability for Metamod:Source to load as a VSP instead of through
+ gameinfo.txt.
- The output of the "meta list" command has been reformatted in order to allow
more space for plugins' name, version, and author fields.
diff --git a/sourcemm/episode2/msvc8/sourcemm.vcproj b/sourcemm/episode2/msvc8/sourcemm.vcproj
index 0645613..096f151 100644
--- a/sourcemm/episode2/msvc8/sourcemm.vcproj
+++ b/sourcemm/episode2/msvc8/sourcemm.vcproj
@@ -63,7 +63,7 @@
/>
#include
#include
@@ -82,11 +83,9 @@ void BaseProvider::ConsolePrint(const char *str)
ConMsg("%s", str);
}
-void BaseProvider::Notify_DLLInit_Pre(void *gamedll,
- CreateInterfaceFn engineFactory,
+void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory,
CreateInterfaceFn serverFactory)
{
- server = (IServerGameDLL *)gamedll;
engine = (IVEngineServer *)((engineFactory)(INTERFACEVERSION_VENGINESERVER, NULL));
if (!engine)
{
@@ -363,6 +362,11 @@ IServerPluginCallbacks *BaseProvider::GetVSPCallbacks(const char *iface)
return &g_VspListener;
}
+bool BaseProvider::IsAlternatelyLoaded()
+{
+ return g_VspListener.IsRootLoadMethod();
+}
+
class GlobCommand : public IMetamodSourceCommandInfo
{
public:
diff --git a/sourcemm/episode2/provider_ep2.h b/sourcemm/episode2/provider_ep2.h
index 634865e..bd4aa75 100644
--- a/sourcemm/episode2/provider_ep2.h
+++ b/sourcemm/episode2/provider_ep2.h
@@ -60,9 +60,7 @@ public:
virtual void DisplayError(const char *fmt, ...);
virtual void DisplayWarning(const char *fmt, ...);
virtual int TryServerGameDLL(const char *iface);
- virtual void Notify_DLLInit_Pre(void *gamedll,
- CreateInterfaceFn engineFactory,
- CreateInterfaceFn serverFactory);
+ virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory);
void Notify_DLLShutdown_Pre();
virtual void ServerCommand(const char *cmd);
virtual ConVar *CreateConVar(const char *name,
@@ -79,6 +77,7 @@ public:
virtual int FindUserMessage(const char *name, int *size=NULL);
virtual const char *GetUserMessage(int index, int *size=NULL);
virtual int DetermineSourceEngine(const char *game);
+ virtual bool IsAlternatelyLoaded();
};
extern IVEngineServer *engine;
diff --git a/sourcemm/episode2/vsp_listener.cpp b/sourcemm/episode2/vsp_listener.cpp
index 3adf2b5..6858c71 100644
--- a/sourcemm/episode2/vsp_listener.cpp
+++ b/sourcemm/episode2/vsp_listener.cpp
@@ -25,16 +25,46 @@
* Version: $Id$
*/
+#if defined _DEBUG
+#define DEBUG2
+#undef _DEBUG
+#endif
+#include "../metamod_oslink.h"
+#include
+#include
+#include
+#include "iplayerinfo.h"
+#if defined DEBUG2
+#undef DEBUG2
+#define _DEBUG
+#endif
#include "vsp_listener.h"
#include "svn_version.h"
#include "metamod.h"
+#include "provider_ep2.h"
+
+SH_DECL_HOOK1_void(ConCommand, Dispatch, SH_NOATTRIB, false, const CCommand &);
using namespace SourceMM;
+ConCommand *g_plugin_unload = NULL;
+bool g_bIsTryingToUnload;
+
+void InterceptPluginUnloads(const CCommand &args)
+{
+ g_bIsTryingToUnload = true;
+}
+
+void InterceptPluginUnloads_Post(const CCommand &args)
+{
+ g_bIsTryingToUnload = false;
+}
+
VSPListener::VSPListener()
{
m_bLoaded = false;
m_bLoadable = false;
+ m_bIsRootLoadMethod = false;
}
void VSPListener::ClientActive(edict_t *pEntity)
@@ -108,6 +138,24 @@ void VSPListener::ServerActivate(edict_t *pEdictList, int edictCount, int client
void VSPListener::Unload()
{
+ if (g_bIsTryingToUnload)
+ {
+ Error("Metamod:Source cannot be unloaded from VSP mode. Use \"meta unload\" to unload specific plugins.\n");
+ return;
+ }
+ if (IsRootLoadMethod())
+ {
+ if (g_plugin_unload != NULL)
+ {
+ SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_plugin_unload, InterceptPluginUnloads, false);
+ SH_REMOVE_HOOK_STATICFUNC(ConCommand, Dispatch, g_plugin_unload, InterceptPluginUnloads_Post, true);
+ g_plugin_unload = NULL;
+ }
+ UnloadMetamod();
+ }
+ m_bLoaded = false;
+ m_bLoadable = true;
+ m_bIsRootLoadMethod = false;
}
void VSPListener::SetLoadable(bool set)
@@ -117,15 +165,76 @@ void VSPListener::SetLoadable(bool set)
bool VSPListener::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory)
{
- if (!m_bLoadable)
+ if (!g_Metamod.IsLoadedAsGameDLL())
{
- provider->DisplayWarning("Do not manually load Metamod:Source as a Valve Server Plugin\n");
- return false;
- }
+ CGlobalVars *pGlobals;
+ IPlayerInfoManager *playerInfoManager;
- if (m_bLoaded)
- {
- return false;
+ playerInfoManager = (IPlayerInfoManager *)gameServerFactory("PlayerInfoManager002", NULL);
+ if (playerInfoManager == NULL)
+ {
+ Msg("Metamod:Source requires gameinfo.txt modification to load on this game.\n");
+ return false;
+ }
+
+ pGlobals = playerInfoManager->GetGlobalVars();
+
+ char gamedll_iface[] = "ServerGameDLL000";
+ for (unsigned int i = 5; i <= 50; i++)
+ {
+ gamedll_iface[15] = '0' + i;
+ if ((server = (IServerGameDLL *)gameServerFactory(gamedll_iface, NULL)) != NULL)
+ {
+ g_Metamod.SetGameDLLInfo(gameServerFactory, i);
+ break;
+ }
+ }
+
+ if (server == NULL)
+ {
+ Msg("Metamod:Source could not load (GameDLL version not compatible).\n");
+ return false;
+ }
+
+ char gameclients_iface[] = "ServerGameClients000";
+ for (unsigned int i = 3; i <= 4; i++)
+ {
+ gameclients_iface[19] = '0' + i;
+ if ((gameclients = (IServerGameClients *)gameServerFactory(gameclients_iface, NULL)) == NULL)
+ {
+ break;
+ }
+ }
+
+ if (!DetectGameInformation())
+ {
+ Msg("Metamod:Source failed to detect game paths; cannot load.\n");
+ return false;
+ }
+
+ m_bIsRootLoadMethod = true;
+ m_bLoaded = true;
+ SetLoadable(false);
+
+ InitializeForLoad();
+ InitializeGlobals(interfaceFactory, interfaceFactory, interfaceFactory, pGlobals);
+
+ 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();
+ }
+
+ if (g_plugin_unload != NULL)
+ {
+ SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_plugin_unload, InterceptPluginUnloads, false);
+ SH_ADD_HOOK_STATICFUNC(ConCommand, Dispatch, g_plugin_unload, InterceptPluginUnloads_Post, true);
+ }
}
m_bLoaded = true;
@@ -139,3 +248,8 @@ bool VSPListener::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gam
void VSPListener::OnQueryCvarValueFinished(QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
{
}
+
+bool VSPListener::IsRootLoadMethod()
+{
+ return m_bIsRootLoadMethod;
+}
diff --git a/sourcemm/episode2/vsp_listener.h b/sourcemm/episode2/vsp_listener.h
index 443606f..f30d663 100644
--- a/sourcemm/episode2/vsp_listener.h
+++ b/sourcemm/episode2/vsp_listener.h
@@ -63,10 +63,12 @@ public:
virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue );
public:
bool IsLoaded();
+ bool IsRootLoadMethod();
void SetLoadable(bool loadable);
private:
bool m_bLoaded;
bool m_bLoadable;
+ bool m_bIsRootLoadMethod;
};
#endif //_INCLUDE_METAMOD_SOURCE_VSP_LISTENER_H_
diff --git a/sourcemm/metamod.cpp b/sourcemm/metamod.cpp
index 1cbb159..005e2d0 100644
--- a/sourcemm/metamod.cpp
+++ b/sourcemm/metamod.cpp
@@ -25,6 +25,7 @@
* Version: $Id$
*/
+#include "metamod_oslink.h"
#if defined _DEBUG
#define DEBUG2
#undef _DEBUG
@@ -36,7 +37,6 @@
#include "metamod_plugins.h"
#include "metamod_util.h"
#include "metamod_console.h"
-#include "metamod_oslink.h"
#if defined DEBUG2
#undef DEBUG2
#define _DEBUG
@@ -99,6 +99,7 @@ ISourceHook *g_SHPtr = &g_SourceHook;
PluginId g_PLID = Pl_Console;
META_RES last_meta_res;
IServerPluginCallbacks *vsp_callbacks = NULL;
+bool were_plugins_loaded = false;
MetamodSource g_Metamod;
@@ -137,7 +138,7 @@ void ClearGamedllList();
}
/* Initialize everything here */
-void InitMainStates()
+void InitializeForLoad()
{
char full_path[PATH_SIZE] = {0};
GetFileOfAddress((void *)gamedll_info.factory, full_path, sizeof(full_path));
@@ -188,6 +189,31 @@ void InitMainStates()
SH_ADD_MANUALHOOK_STATICFUNC(SGD_DLLShutdown, server, Handler_DLLShutdown, false);
}
+bool DetectGameInformation()
+{
+ char mm_path[PATH_SIZE];
+ char game_path[PATH_SIZE];
+
+ /* Get path to SourceMM DLL */
+ if (!GetFileOfAddress((void *)InitializeForLoad, mm_path, sizeof(mm_path)))
+ {
+ return false;
+ }
+
+ 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");
+
+ /* Get absolute path */
+ abspath(game_path, game_dir);
+ mod_path.assign(game_path);
+
+ engine_build = provider->DetermineSourceEngine(game_dir);;
+
+ return true;
+}
+
/* This is where the magic happens */
SMM_API void *CreateInterface(const char *iface, int *ret)
{
@@ -221,31 +247,22 @@ SMM_API void *CreateInterface(const char *iface, int *ret)
return vsp_callbacks;
}
+ if (provider->IsAlternatelyLoaded())
+ {
+ IFACE_MACRO(gamedll_info.factory, GameDLL);
+ }
+
if (!parsed_game_info)
{
parsed_game_info = true;
const char *game_dir = NULL;
- char game_path[PATH_SIZE];
- char mm_path[PATH_SIZE];
- /* Get path to SourceMM DLL */
- if (!GetFileOfAddress((void *)CreateInterface, mm_path, sizeof(mm_path)))
+ if (!DetectGameInformation())
{
provider->DisplayError("GetFileOfAddress() failed! Metamod cannot load.\n");
return NULL;
}
- metamod_path.assign(mm_path);
-
- /* Get value of -game from command line, defaulting to hl2 as engine seems to do */
- game_dir = provider->GetCommandLineValue("-game", "hl2");
-
- engine_build = provider->DetermineSourceEngine(game_dir);;
-
- /* Get absolute path */
- abspath(game_path, game_dir);
- mod_path.assign(game_path);
-
char temp_path[PATH_SIZE];
/* Path to gameinfo.txt */
@@ -331,7 +348,7 @@ SMM_API void *CreateInterface(const char *iface, int *ret)
}
/* If not path to SourceMM... */
- if (!UTIL_PathCmp(mm_path, temp_path))
+ if (!UTIL_PathCmp(metamod_path.c_str(), temp_path))
{
FILE *temp_fp = fopen(temp_path, "rb");
if (!temp_fp)
@@ -413,7 +430,7 @@ SMM_API void *CreateInterface(const char *iface, int *ret)
if (is_gamedll_loaded)
{
ClearGamedllList();
- InitMainStates();
+ InitializeForLoad();
}
else
{
@@ -603,6 +620,11 @@ int LoadPluginsFromFile(const char *_file)
void InitializeVSP()
{
+ if (provider->IsAlternatelyLoaded())
+ {
+ return;
+ }
+
size_t len;
char engine_file[PATH_SIZE];
char engine_path[PATH_SIZE];
@@ -675,29 +697,8 @@ void LogMessage(const char *msg, ...)
}
}
-bool Handler_DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals)
+void DoInitialPluginLoads()
{
- engine_factory = engineFactory;
- filesystem_factory = filesystemFactory;
- physics_factory = physicsFactory;
- gpGlobals = pGlobals;
-
- provider->Notify_DLLInit_Pre(server, engineFactory, gamedll_info.factory);
-
- metamod_version = provider->CreateConVar("metamod_version",
- SOURCEMM_VERSION,
- "Metamod:Source Version",
- ConVarFlag_Notify|ConVarFlag_Replicated|ConVarFlag_SpOnly);
-
- mm_pluginsfile = provider->CreateConVar("mm_pluginsfile",
-#if defined WIN32 || defined _WIN32
- "addons\\metamod\\metaplugins.ini",
-#else
- "addons/metamod/metaplugins.ini",
-#endif
- "Metamod:Source Plugins File",
- ConVarFlag_SpOnly);
-
const char *pluginFile = provider->GetCommandLineValue("mm_pluginsfile", NULL);
if (!pluginFile)
{
@@ -712,8 +713,47 @@ bool Handler_DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsF
g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s", mod_path.c_str(), pluginFile);
LoadPluginsFromFile(full_path);
+}
- in_first_level = true;
+void StartupMetamod(bool bWaitForGameInit)
+{
+ metamod_version = provider->CreateConVar("metamod_version",
+ SOURCEMM_VERSION,
+ "Metamod:Source Version",
+ ConVarFlag_Notify|ConVarFlag_Replicated|ConVarFlag_SpOnly);
+
+ mm_pluginsfile = provider->CreateConVar("mm_pluginsfile",
+#if defined WIN32 || defined _WIN32
+ "addons\\metamod\\metaplugins.ini",
+#else
+ "addons/metamod/metaplugins.ini",
+#endif
+ "Metamod:Source Plugins File",
+ ConVarFlag_SpOnly);
+
+ if (!bWaitForGameInit)
+ {
+ DoInitialPluginLoads();
+ in_first_level = true;
+ }
+}
+
+void InitializeGlobals(CreateInterfaceFn engineFactory,
+ CreateInterfaceFn physicsFactory,
+ CreateInterfaceFn filesystemFactory,
+ CGlobalVars *pGlobals)
+{
+ engine_factory = engineFactory;
+ physics_factory = physicsFactory;
+ filesystem_factory = filesystemFactory;
+ gpGlobals = pGlobals;
+ provider->Notify_DLLInit_Pre(engineFactory, gamedll_info.factory);
+}
+
+bool Handler_DLLInit(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals)
+{
+ InitializeGlobals(engineFactory, physicsFactory, filesystemFactory, pGlobals);
+ StartupMetamod(false);
RETURN_META_VALUE(MRES_IGNORED, true);
}
@@ -730,6 +770,13 @@ bool Handler_GameInit()
InitializeVSP();
}
+ if (provider->IsAlternatelyLoaded() && !were_plugins_loaded)
+ {
+ DoInitialPluginLoads();
+ g_PluginMngr.SetAllLoaded();
+ were_plugins_loaded = true;
+ }
+
is_game_init = true;
RETURN_META_VALUE(MRES_IGNORED, true);
@@ -741,14 +788,17 @@ bool Handler_DLLInit_Post(CreateInterfaceFn engineFactory, CreateInterfaceFn phy
RETURN_META_VALUE(MRES_IGNORED, true);
}
-void Handler_DLLShutdown()
+void UnloadMetamod()
{
/* Unload plugins */
g_PluginMngr.UnloadAll();
provider->Notify_DLLShutdown_Pre();
- SH_CALL(server, &IServerGameDLL::DLLShutdown)();
+ if (is_gamedll_loaded)
+ {
+ SH_CALL(server, &IServerGameDLL::DLLShutdown)();
+ }
g_SourceHook.CompleteShutdown();
@@ -757,12 +807,24 @@ void Handler_DLLShutdown()
dlclose(gamedll_info.lib);
is_gamedll_loaded = false;
}
+}
+void Handler_DLLShutdown()
+{
+ UnloadMetamod();
RETURN_META(MRES_SUPERCEDE);
}
void Handler_LevelShutdown(void)
{
+ if (provider->IsAlternatelyLoaded() && !were_plugins_loaded)
+ {
+ g_PluginMngr.SetAllLoaded();
+ DoInitialPluginLoads();
+ were_plugins_loaded = true;
+ in_first_level = true;
+ }
+
if (!in_first_level)
{
char full_path[255];
@@ -1270,3 +1332,19 @@ size_t MetamodSource::FormatArgs(char *buffer, size_t maxlength, const char *for
{
return UTIL_FormatArgs(buffer, maxlength, format, ap);
}
+
+bool MetamodSource::IsLoadedAsGameDLL()
+{
+ return is_gamedll_loaded;
+}
+
+void MetamodSource::SetGameDLLInfo(CreateInterfaceFn serverFactory, int version)
+{
+ gamedll_info.factory = serverFactory;
+ gamedll_version = version;
+}
+
+bool MetamodSource::IsAlternateLoadComplete()
+{
+ return were_plugins_loaded;
+}
diff --git a/sourcemm/metamod.h b/sourcemm/metamod.h
index 0d8744b..d9c7436 100644
--- a/sourcemm/metamod.h
+++ b/sourcemm/metamod.h
@@ -93,14 +93,24 @@ public:
size_t Format(char *buffer, size_t maxlength, const char *format, ...);
size_t FormatArgs(char *buffer, size_t maxlength, const char *format, va_list ap);
public:
+ bool IsLoadedAsGameDLL();
const char *GetGameBinaryPath();
const char *GetPluginsFile();
void UnregisterConCommandBase(PluginId id, ConCommandBase *pCommand);
void NotifyVSPListening(IServerPluginCallbacks *callbacks);
+ void SetGameDLLInfo(CreateInterfaceFn serverFactory, int version);
+ bool IsAlternateLoadComplete();
};
+bool DetectGameInformation();
void LogMessage(const char *msg, ...);
int LoadPluginsFromFile(const char *_file);
+void InitializeForLoad();
+void InitializeGlobals(CreateInterfaceFn engineFactory,
+ CreateInterfaceFn physicsFactory,
+ CreateInterfaceFn filesystemFactory,
+ CGlobalVars *pGlobals);
+void UnloadMetamod();
extern MetamodSource g_Metamod;
extern SourceHook::Impl::CSourceHookImpl g_SourceHook;
diff --git a/sourcemm/metamod_console.cpp b/sourcemm/metamod_console.cpp
index caf2abd..b73e6a0 100644
--- a/sourcemm/metamod_console.cpp
+++ b/sourcemm/metamod_console.cpp
@@ -25,6 +25,7 @@
* Version: $Id$
*/
+#include "metamod_oslink.h"
#if defined _DEBUG
#define DEBUG2
#undef _DEBUG
@@ -54,6 +55,12 @@ bool Command_Meta(IMetamodSourceCommandInfo *info)
{
unsigned int args = info->GetArgCount();
+ if (provider->IsAlternatelyLoaded() && !g_Metamod.IsAlternateLoadComplete())
+ {
+ CONMSG("You must change the map to activate Metamod:Source.\n");
+ return true;
+ }
+
if (args >= 1)
{
const char *command = info->GetArg(1);
diff --git a/sourcemm/metamod_oslink.h b/sourcemm/metamod_oslink.h
index 2e6a311..fd7ad97 100644
--- a/sourcemm/metamod_oslink.h
+++ b/sourcemm/metamod_oslink.h
@@ -76,7 +76,7 @@
#endif
#if defined __linux__
- extern int errno;
+ #include
int GetLastError();
#endif
diff --git a/sourcemm/metamod_plugins.cpp b/sourcemm/metamod_plugins.cpp
index 1814956..f6e32fa 100644
--- a/sourcemm/metamod_plugins.cpp
+++ b/sourcemm/metamod_plugins.cpp
@@ -25,6 +25,7 @@
* Version: $Id$
*/
+#include "metamod_oslink.h"
#if defined _DEBUG
#define DEBUG2
#undef _DEBUG
@@ -210,6 +211,11 @@ CPluginManager::CPlugin *CPluginManager::FindById(PluginId id)
void CPluginManager::SetAllLoaded()
{
+ if (m_AllLoaded)
+ {
+ return;
+ }
+
m_AllLoaded = true;
PluginIter i;
diff --git a/sourcemm/metamod_provider.h b/sourcemm/metamod_provider.h
index 94458d7..ff5e1e5 100644
--- a/sourcemm/metamod_provider.h
+++ b/sourcemm/metamod_provider.h
@@ -186,9 +186,7 @@ namespace SourceMM
/**
* @brief Notifies the provider that the DLLInit pre-hook is almost done.
*/
- virtual void Notify_DLLInit_Pre(void *gamedll,
- CreateInterfaceFn engineFactory,
- CreateInterfaceFn serverFactory) =0;
+ virtual void Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, CreateInterfaceFn serverFactory) =0;
virtual void Notify_DLLShutdown_Pre() =0;
@@ -291,6 +289,14 @@ namespace SourceMM
* @return SOURCE_ENGINE constant.
*/
virtual int DetermineSourceEngine(const char *game) =0;
+
+ /**
+ * @brief Returns if the provider has loaded itself through an
+ * alternate means (that is, not through gameinfo.txt.
+ *
+ * @return True if loaded, false otherwise.
+ */
+ virtual bool IsAlternatelyLoaded() =0;
};
};
diff --git a/sourcemm/metamod_util.cpp b/sourcemm/metamod_util.cpp
index 730eddf..995bd7f 100644
--- a/sourcemm/metamod_util.cpp
+++ b/sourcemm/metamod_util.cpp
@@ -221,7 +221,10 @@ bool UTIL_PathCmp(const char *path1, const char *path2)
}
/* If we're at a different non-alphanumeric, the next character MUST match */
- if (!isalpha(path1[pos1]) && (path1[pos1] != path2[pos2]))
+ if ((((unsigned)path1[pos1] & 0x80) && path1[pos1] != path2[pos2])
+ ||
+ !isalpha(path1[pos1]) && (path1[pos1] != path2[pos2])
+ )
{
return false;
}