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; }