diff --git a/sourcemm/ISmmPlugin.h b/sourcemm/ISmmPlugin.h index 89a7248..d80982b 100644 --- a/sourcemm/ISmmPlugin.h +++ b/sourcemm/ISmmPlugin.h @@ -385,6 +385,11 @@ namespace SourceMM * loading plugin should use ISmmAPI::GetVSPInfo() before relying on * this callback. * + * This callback is never called if Metamod:Source is in VSP mode. + * If in VSP mode, a VSP instance is automatically and always available + * via ISmmAPI::GetVSPInfo(), which should be called anyway (to handle + * late loading cases). + * * @param iface Interface pointer. If NULL, then the VSP * listening construct failed to initialize and * is not available. diff --git a/sourcemm/changelog.txt b/sourcemm/changelog.txt index 1c93be6..a397251 100644 --- a/sourcemm/changelog.txt +++ b/sourcemm/changelog.txt @@ -8,6 +8,9 @@ instance pointer rather than a callclass pointer. - Metamod:Source has now received a large internal rewrite to improve coding standards and to separate internal logic from engine specifics. + - Added ability to load from a VDF file instead of gameinfo.txt. + - Added ability to load MM:S plugins from VDF files. + - Added mm_basedir cvar to specify Metamod's base folder. - Added API for getting highest supported IServerPluginCallbacks interface version. - Added API for detecting the engine version. diff --git a/sourcemm/episode2/provider_ep2.cpp b/sourcemm/episode2/provider_ep2.cpp index ffb7a45..a6b02f1 100644 --- a/sourcemm/episode2/provider_ep2.cpp +++ b/sourcemm/episode2/provider_ep2.cpp @@ -43,6 +43,7 @@ #include "console.h" #include "metamod_console.h" #include "vsp_listener.h" +#include #if defined DEBUG2 #undef DEBUG2 #define _DEBUG @@ -74,6 +75,7 @@ VSPListener g_VspListener; BaseProvider g_Ep1Provider; IMetamodSourceProvider *provider = &g_Ep1Provider; List conbases_unreg; +IFileSystem *baseFs = NULL; ConCommand meta_local_cmd("meta", LocalCommand_Meta, "Metamod:Source control options"); SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, edict_t *, const CCommand &); @@ -106,6 +108,13 @@ void BaseProvider::Notify_DLLInit_Pre(CreateInterfaceFn engineFactory, gameclients = (IServerGameClients *)(serverFactory("ServerGameClients004", NULL)); } + baseFs = (IFileSystem *)((engineFactory)(FILESYSTEM_INTERFACE_VERSION, NULL)); + if (baseFs == NULL) + { + ::LogMessage("Unable to find \"%s\": .vdf files will not be parsed", FILESYSTEM_INTERFACE_VERSION); + return; + } + RegisterConCommandBase(&meta_local_cmd); conbases_unreg.push_back(&meta_local_cmd); @@ -154,6 +163,11 @@ const char *BaseProvider::GetConVarString(ConVar *convar) return convar->GetString(); } +void BaseProvider::SetConVarString(ConVar *convar, const char *str) +{ + convar->SetValue(str); +} + bool BaseProvider::IsConCommandBaseACommand(ConCommandBase *pCommand) { return pCommand->IsCommand(); @@ -367,6 +381,68 @@ bool BaseProvider::IsAlternatelyLoaded() return g_VspListener.IsRootLoadMethod(); } +bool BaseProvider::ProcessVDF(const char *file, char path[], size_t path_len, char alias[], size_t alias_len) +{ + if (baseFs == NULL) + { + return false; + } + + KeyValues *pValues; + const char *plugin_file, *p_alias; + + pValues = new KeyValues("Metamod Plugin"); + + if (!pValues->LoadFromFile(baseFs, file)) + { + pValues->deleteThis(); + return false; + } + + if ((plugin_file = pValues->GetString("file", NULL)) == NULL) + { + pValues->deleteThis(); + return false; + } + + if ((p_alias = pValues->GetString("alias", NULL)) != NULL) + { + UTIL_Format(alias, alias_len, "%s", p_alias); + } + else + { + UTIL_Format(alias, alias_len, ""); + } + + /* Attempt to find a file extension */ + if (UTIL_GetExtension(plugin_file) == NULL) + { + g_pMetamod->PathFormat(path, + path_len, + "%s/%s%s", + g_pMetamod->GetBaseDir(), + plugin_file, +#if defined WIN32 || defined _WIN32 + ".dll" +#else + "_i486.so" +#endif + ); + } + else + { + g_pMetamod->PathFormat(path, + path_len, + "%s/%s", + g_pMetamod->GetBaseDir(), + plugin_file); + } + + pValues->deleteThis(); + + return true; +} + class GlobCommand : public IMetamodSourceCommandInfo { public: diff --git a/sourcemm/episode2/provider_ep2.h b/sourcemm/episode2/provider_ep2.h index bd4aa75..b066f25 100644 --- a/sourcemm/episode2/provider_ep2.h +++ b/sourcemm/episode2/provider_ep2.h @@ -68,6 +68,7 @@ public: const char *help, int flags); virtual const char *GetConVarString(ConVar *convar); + virtual void SetConVarString(ConVar *convar, const char *str); virtual const char *GetGameDescription(); virtual IConCommandBaseAccessor *GetConCommandBaseAccessor(); virtual bool RegisterConCommandBase(ConCommandBase *pCommand); @@ -78,6 +79,7 @@ public: virtual const char *GetUserMessage(int index, int *size=NULL); virtual int DetermineSourceEngine(const char *game); virtual bool IsAlternatelyLoaded(); + virtual bool ProcessVDF(const char *file, char path[], size_t path_len, char alias[], size_t alias_len); }; extern IVEngineServer *engine; diff --git a/sourcemm/episode2/vsp_listener.cpp b/sourcemm/episode2/vsp_listener.cpp index 6858c71..137234c 100644 --- a/sourcemm/episode2/vsp_listener.cpp +++ b/sourcemm/episode2/vsp_listener.cpp @@ -218,6 +218,7 @@ bool VSPListener::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gam InitializeForLoad(); InitializeGlobals(interfaceFactory, interfaceFactory, interfaceFactory, pGlobals); + StartupMetamod(true); const ConCommandBase *pBase = icvar->GetCommands(); while (pBase != NULL) @@ -240,7 +241,10 @@ bool VSPListener::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gam m_bLoaded = true; SetLoadable(false); - g_Metamod.NotifyVSPListening(this); + if (!m_bIsRootLoadMethod) + { + g_Metamod.NotifyVSPListening(this); + } return true; } diff --git a/sourcemm/metamod.cpp b/sourcemm/metamod.cpp index 14b60e5..9577758 100644 --- a/sourcemm/metamod.cpp +++ b/sourcemm/metamod.cpp @@ -64,6 +64,7 @@ void Handler_LevelShutdown(); bool Handler_LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); bool Handler_GameInit(); void InitializeVSP(); +void LookForVDFs(const char *dir); struct game_dll_t { @@ -88,6 +89,7 @@ bool vsp_loaded = false; game_dll_t gamedll_info; ConVar *metamod_version = NULL; ConVar *mm_pluginsfile = NULL; +ConVar *mm_basedir = NULL; IServerGameDLL *server = NULL; CreateInterfaceFn engine_factory = NULL; CreateInterfaceFn physics_factory = NULL; @@ -102,6 +104,7 @@ IServerPluginCallbacks *vsp_callbacks = NULL; bool were_plugins_loaded = false; MetamodSource g_Metamod; +SourceMM::ISmmAPI *g_pMetamod = &g_Metamod; void ClearGamedllList(); @@ -699,6 +702,7 @@ void LogMessage(const char *msg, ...) void DoInitialPluginLoads() { const char *pluginFile = provider->GetCommandLineValue("mm_pluginsfile", NULL); + const char *mmBaseDir = provider->GetCommandLineValue("mm_basedir", NULL); if (!pluginFile) { pluginFile = provider->GetConVarString(mm_pluginsfile); @@ -707,20 +711,41 @@ void DoInitialPluginLoads() pluginFile = "addons/metamod/metaplugins.ini"; } } + if (!mmBaseDir) + { + mmBaseDir = provider->GetConVarString(mm_basedir); + if (mmBaseDir == NULL) + { + mmBaseDir = "addons/metamod"; + } + } char full_path[260]; - g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s", mod_path.c_str(), pluginFile); + g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s", mod_path.c_str(), pluginFile); LoadPluginsFromFile(full_path); + + g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s", mod_path.c_str(), mmBaseDir); + LookForVDFs(full_path); } -void StartupMetamod(bool bWaitForGameInit) +void StartupMetamod(bool is_vsp_load) { + char buffer[255]; + + UTIL_Format(buffer, + sizeof(buffer), + "%s%s", + SOURCEMM_VERSION, + is_vsp_load ? "V" : ""); + metamod_version = provider->CreateConVar("metamod_version", SOURCEMM_VERSION, "Metamod:Source Version", ConVarFlag_Notify|ConVarFlag_Replicated|ConVarFlag_SpOnly); + provider->SetConVarString(metamod_version, buffer); + mm_pluginsfile = provider->CreateConVar("mm_pluginsfile", #if defined WIN32 || defined _WIN32 "addons\\metamod\\metaplugins.ini", @@ -730,7 +755,16 @@ void StartupMetamod(bool bWaitForGameInit) "Metamod:Source Plugins File", ConVarFlag_SpOnly); - if (!bWaitForGameInit) + mm_basedir = provider->CreateConVar("mm_basedir", +#if defined __linux__ + "addons/metamod", +#else + "addons\\metamod", +#endif + "Metamod:Source Base Folder", + ConVarFlag_SpOnly); + + if (!is_vsp_load) { DoInitialPluginLoads(); in_first_level = true; @@ -827,13 +861,20 @@ void Handler_LevelShutdown(void) if (!in_first_level) { char full_path[255]; + g_Metamod.PathFormat(full_path, sizeof(full_path), "%s/%s", mod_path.c_str(), provider->GetConVarString(mm_pluginsfile)); - LoadPluginsFromFile(full_path); + + g_Metamod.PathFormat(full_path, + sizeof(full_path), + "%s/%s", + mod_path.c_str(), + provider->GetConVarString(mm_basedir)); + LookForVDFs(full_path); } else { @@ -1347,3 +1388,95 @@ bool MetamodSource::IsAlternateLoadComplete() { return were_plugins_loaded; } + +void ProcessVDF(const char *path) +{ + PluginId id; + bool already; + char alias[24], file[255], error[255]; + + if (!provider->ProcessVDF(path, file, sizeof(file), alias, sizeof(alias))) + { + return; + } + + if (alias[0] != '\0') + { + g_PluginMngr.SetAlias(alias, file); + } + + id = g_PluginMngr.Load(file, Pl_File, already, error, sizeof(error)); + if (id < Pl_MinId || g_PluginMngr.FindById(id)->m_Status < Pl_Paused) + { + LogMessage("[META] Failed to load plugin %s: %s", file, error); + } +} + +void LookForVDFs(const char *dir) +{ + char path[MAX_PATH]; + +#if defined _MSC_VER + HANDLE hFind; + WIN32_FIND_DATA fd; + char error[255]; + + g_Metamod.PathFormat(path, sizeof(path), "%s\\*.*", dir); + if ((hFind = FindFirstFile(path, &fd)) == INVALID_HANDLE_VALUE) + { + DWORD dw = GetLastError(); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + error, + sizeof(error), + NULL); + LogMessage("[META] Could not open folder \"%s\" (%s)", dir, error); + return; + } + + do + { + if (strcmp(fd.cFileName, ".") == 0 + || strcmp(fd.cFileName, "..") == 0) + { + continue; + } + if (strstr(fd.cFileName, ".vdf") == NULL) + { + continue; + } + g_Metamod.PathFormat(path, sizeof(path), "%s\\%s", dir, fd.cFileName); + ProcessVDF(path); + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); +#else + DIR *pDir; + struct dirent *pEnt; + + if ((pDir = opendir(dir)) == NULL) + { + LogMessage("[META] Could not open folder \"%s\" (%s)", dir, strerror(errno)); + return; + } + + while ((pEnt = readdir(pDir)) != NULL) + { + if (strcmp(pEnt->d_name, ".") == 0 + || strcmp(pEnt->d_name, "..") == 0) + { + continue; + } + if (strstr(pEnt->d_name, ".vdf") == NULL) + { + continue; + } + g_SmmAPI.PathFormat(path, sizeof(path), "%s/%s", dir, pEnt->d_name); + ProcessVDF(path); + } + + closedir(pDir); +#endif +} diff --git a/sourcemm/metamod.h b/sourcemm/metamod.h index d9c7436..b3618d8 100644 --- a/sourcemm/metamod.h +++ b/sourcemm/metamod.h @@ -110,6 +110,7 @@ void InitializeGlobals(CreateInterfaceFn engineFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn filesystemFactory, CGlobalVars *pGlobals); +void StartupMetamod(bool is_vsp_load); void UnloadMetamod(); extern MetamodSource g_Metamod; diff --git a/sourcemm/metamod_provider.h b/sourcemm/metamod_provider.h index ff5e1e5..d351cec 100644 --- a/sourcemm/metamod_provider.h +++ b/sourcemm/metamod_provider.h @@ -219,6 +219,14 @@ namespace SourceMM */ virtual const char *GetConVarString(ConVar *convar) =0; + /** + * @brief Sets a ConVar string. + * + * @param convar ConVar pointer. + * @param str String pointer. + */ + virtual void SetConVarString(ConVar *convar, const char *str) =0; + /** * @brief Retrieves the game description. * @@ -297,12 +305,19 @@ namespace SourceMM * @return True if loaded, false otherwise. */ virtual bool IsAlternatelyLoaded() =0; + + /** + * @brief Processes a VDF plugin file. + * + */ + virtual bool ProcessVDF(const char *file, char path[], size_t path_len, char alias[], size_t alias_len) =0; }; }; extern PluginId g_PLID; extern SourceHook::ISourceHook *g_SHPtr; extern SourceMM::IMetamodSourceProvider *provider; +extern SourceMM::ISmmAPI *g_pMetamod; #endif //_INCLUDE_METAMOD_SOURCE_SUPPORT_H_ diff --git a/sourcemm/metamod_util.h b/sourcemm/metamod_util.h index de507ee..5f340b4 100644 --- a/sourcemm/metamod_util.h +++ b/sourcemm/metamod_util.h @@ -95,4 +95,6 @@ bool UTIL_Relatize(char buffer[], const char *relTo, const char *relFrom); +void LogMessage(const char *msg, ...); + #endif //_INCLUDE_UTIL_H