From f7c115e736d53eff242f21e1b6e7e0c247054e38 Mon Sep 17 00:00:00 2001 From: asmodai Date: Tue, 9 May 2017 18:34:55 +0300 Subject: [PATCH] Refactoring of mplugin and mlist --- metamod/include/dlls/util.h | 1 - metamod/msvc/metamod.vcxproj | 4 - metamod/msvc/metamod.vcxproj.filters | 12 - metamod/src/callback_jit.cpp | 8 +- metamod/src/commands_meta.cpp | 46 +- metamod/src/conf_meta.cpp | 2 +- metamod/src/engine_api.cpp | 4 +- metamod/src/engine_api.h | 3 +- metamod/src/enginecallbacks.h | 8 - metamod/src/game_support.cpp | 2 +- metamod/src/h_export.cpp | 5 +- metamod/src/meta_eiface.cpp | 20 - metamod/src/meta_eiface.h | 45 - metamod/src/metamod.cpp | 22 +- metamod/src/metamod.h | 5 +- metamod/src/mlist.cpp | 115 +-- metamod/src/mlist.h | 3 +- metamod/src/mplugin.cpp | 1211 ++++++++++++-------------- metamod/src/mplugin.h | 155 ++-- metamod/src/mreg.cpp | 8 +- metamod/src/mutil.cpp | 23 +- metamod/src/osdep.cpp | 55 +- metamod/src/plinfo.h | 4 +- metamod/src/precompiled.h | 2 - metamod/src/reg_support.cpp | 2 +- metamod/src/sdk_util.cpp | 2 +- metamod/src/sdk_util.h | 1 + metamod/src/support_meta.cpp | 91 -- metamod/src/support_meta.h | 23 - metamod/src/utils.cpp | 115 ++- metamod/src/utils.h | 25 +- 31 files changed, 917 insertions(+), 1105 deletions(-) delete mode 100644 metamod/src/meta_eiface.cpp delete mode 100644 metamod/src/meta_eiface.h delete mode 100644 metamod/src/support_meta.cpp delete mode 100644 metamod/src/support_meta.h diff --git a/metamod/include/dlls/util.h b/metamod/include/dlls/util.h index 8e36d48..3cbcb48 100644 --- a/metamod/include/dlls/util.h +++ b/metamod/include/dlls/util.h @@ -186,5 +186,4 @@ inline void UTIL_TraceLine(const Vector &vecStart, const Vector &vecEnd, IGNORE_ TRACE_LINE(vecStart, vecEnd, (igmon == ignore_monsters), pentIgnore, ptr); } -extern char *UTIL_VarArgs(char *format, ...); extern void UTIL_LogPrintf(const char *fmt, ...); diff --git a/metamod/msvc/metamod.vcxproj b/metamod/msvc/metamod.vcxproj index 1904227..114a127 100644 --- a/metamod/msvc/metamod.vcxproj +++ b/metamod/msvc/metamod.vcxproj @@ -197,7 +197,6 @@ - @@ -213,7 +212,6 @@ - @@ -232,7 +230,6 @@ - @@ -245,7 +242,6 @@ - diff --git a/metamod/msvc/metamod.vcxproj.filters b/metamod/msvc/metamod.vcxproj.filters index 3d5db74..d11fa0b 100644 --- a/metamod/msvc/metamod.vcxproj.filters +++ b/metamod/msvc/metamod.vcxproj.filters @@ -41,9 +41,6 @@ Source Files - - Source Files - Source Files @@ -77,9 +74,6 @@ Source Files - - Source Files - Source Files @@ -130,9 +124,6 @@ Source Files - - Source Files - Source Files @@ -169,9 +160,6 @@ Source Files - - Source Files - Source Files diff --git a/metamod/src/callback_jit.cpp b/metamod/src/callback_jit.cpp index 30832fd..e46763d 100644 --- a/metamod/src/callback_jit.cpp +++ b/metamod/src/callback_jit.cpp @@ -128,7 +128,7 @@ void CForwardCallbackJIT::naked_main() for (int i = 0, hookid = 0; i < m_jitdata->plugins_count; i++) { auto plug = &m_jitdata->plugins[i]; - if (plug->m_status < PL_RUNNING) // allow only running and paused + if (plug->status() < PL_RUNNING) // allow only running and paused continue; size_t fn_table = *(size_t *)(size_t(plug) + m_jitdata->table_offset); @@ -141,7 +141,7 @@ void CForwardCallbackJIT::naked_main() // check status and handler set mov(ecx, dword_ptr[fn_table + m_jitdata->pfn_offset]); - cmp(byte_ptr[size_t(&plug->m_status)], PL_RUNNING); + cmp(byte_ptr[plug->status_ptr()], PL_RUNNING); jecxz(go_next_plugin); jnz(go_next_plugin); @@ -209,7 +209,7 @@ void CForwardCallbackJIT::naked_main() for (int i = 0, hookid = 0; i < m_jitdata->plugins_count; i++) { auto plug = &m_jitdata->plugins[i]; - if (plug->m_status < PL_RUNNING) // allow only running and paused + if (plug->status() < PL_RUNNING) // allow only running and paused continue; size_t fn_table = *(size_t *)(size_t(plug) + m_jitdata->post_table_offset); @@ -222,7 +222,7 @@ void CForwardCallbackJIT::naked_main() // check status and handler set mov(ecx, dword_ptr[fn_table + m_jitdata->pfn_offset]); - cmp(byte_ptr[size_t(&plug->m_status)], PL_RUNNING); + cmp(byte_ptr[plug->status_ptr()], PL_RUNNING); jecxz(go_next_plugin); jnz(go_next_plugin); diff --git a/metamod/src/commands_meta.cpp b/metamod/src/commands_meta.cpp index 80d4cba..b11dfdb 100644 --- a/metamod/src/commands_meta.cpp +++ b/metamod/src/commands_meta.cpp @@ -335,7 +335,7 @@ void cmd_doplug(PLUG_CMD pcmd) // Otherwise, print error and exit. if (pcmd == PC_REQUIRE) { - if (findp && findp->m_status >= PL_RUNNING && unique) + if (findp && findp->status() >= PL_RUNNING && unique) { META_DEBUG(3, "Required plugin '%s' found loaded and running.", arg); return; @@ -375,69 +375,69 @@ void cmd_doplug(PLUG_CMD pcmd) { case PC_PAUSE: if (findp->pause()) - META_CONS("Paused plugin '%s'", findp->m_desc); + META_CONS("Paused plugin '%s'", findp->description()); else - META_CONS("Pause failed for plugin '%s'", findp->m_desc); + META_CONS("Pause failed for plugin '%s'", findp->description()); break; case PC_UNPAUSE: if (findp->unpause()) - META_CONS("Unpaused plugin '%s'", findp->m_desc); + META_CONS("Unpaused plugin '%s'", findp->description()); else - META_CONS("Unpause failed for plugin '%s'", findp->m_desc); + META_CONS("Unpause failed for plugin '%s'", findp->description()); break; case PC_UNLOAD: { - findp->m_action = PA_UNLOAD; - if (findp->unload(PT_ANYTIME, PNL_COMMAND, PNL_COMMAND)) + findp->set_action(PA_UNLOAD); + if (findp->unload(PT_ANYTIME, PNL_COMMAND)) { - META_CONS("Unloaded plugin '%s'", findp->m_desc); + META_CONS("Unloaded plugin '%s'", findp->description()); g_plugins->show(); } else if (false /*meta_errno == ME_DELAYED*/) // TODO - META_CONS("Unload delayed for plugin '%s'", findp->m_desc); + META_CONS("Unload delayed for plugin '%s'", findp->description()); else - META_CONS("Unload failed for plugin '%s'", findp->m_desc); + META_CONS("Unload failed for plugin '%s'", findp->description()); break; } case PC_FORCE_UNLOAD: { - findp->m_action = PA_UNLOAD; - if (findp->unload(PT_ANYTIME, PNL_CMD_FORCED, PNL_CMD_FORCED)) + findp->set_action(PA_UNLOAD); + if (findp->unload(PT_ANYTIME, PNL_CMD_FORCED)) { - META_CONS("Forced unload plugin '%s'", findp->m_desc); + META_CONS("Forced unload plugin '%s'", findp->description()); g_plugins->show(); } else - META_CONS("Forced unload failed for plugin '%s'", findp->m_desc); + META_CONS("Forced unload failed for plugin '%s'", findp->description()); break; } case PC_RELOAD: { - findp->m_action = PA_RELOAD; + findp->set_action(PA_RELOAD); if (findp->reload(PT_ANYTIME, PNL_COMMAND)) - META_CONS("Reloaded plugin '%s'", findp->m_desc); + META_CONS("Reloaded plugin '%s'", findp->description()); else if (0/*meta_errno == ME_DELAYED*/) // TODO - META_CONS("Reload delayed for plugin '%s'", findp->m_desc); + META_CONS("Reload delayed for plugin '%s'", findp->description()); else if (0/*meta_errno == ME_NOTALLOWED*/) - META_CONS("Reload not allowed for plugin '%s' now, only allowed %s", findp->m_desc, findp->str_loadable(SL_ALLOWED)); + META_CONS("Reload not allowed for plugin '%s' now, only allowed %s", findp->description(), findp->str_loadable(SL_ALLOWED)); else - META_CONS("Reload failed for plugin '%s'", findp->m_desc); + META_CONS("Reload failed for plugin '%s'", findp->description()); break; } case PC_RETRY: if (findp->retry(PT_ANYTIME, PNL_COMMAND)) - META_CONS("Retry succeeded for plugin '%s'", findp->m_desc); + META_CONS("Retry succeeded for plugin '%s'", findp->description()); else - META_CONS("Retry failed for plugin '%s'", findp->m_desc); + META_CONS("Retry failed for plugin '%s'", findp->description()); break; case PC_CLEAR: if (!findp->clear()) { - META_CONS("Clear failed for plugin '%s'", findp->m_desc); + META_CONS("Clear failed for plugin '%s'", findp->description()); return; } - META_CONS("Cleared failed plugin '%s' from list", findp->m_desc); + META_CONS("Cleared failed plugin '%s' from list", findp->description()); g_plugins->show(); break; case PC_INFO: diff --git a/metamod/src/conf_meta.cpp b/metamod/src/conf_meta.cpp index 94c30c5..fd151f1 100644 --- a/metamod/src/conf_meta.cpp +++ b/metamod/src/conf_meta.cpp @@ -193,7 +193,7 @@ void MConfig::set_directory() } #endif - NormalizePath(m_directory); + normalize_path(m_directory); // get directory char *dir = Q_strrchr(m_directory, '/'); diff --git a/metamod/src/engine_api.cpp b/metamod/src/engine_api.cpp index f8e062e..48ea1dd 100644 --- a/metamod/src/engine_api.cpp +++ b/metamod/src/engine_api.cpp @@ -3,8 +3,8 @@ #define CDATA_ENG_H(x, p, h) CDATA_ENTRY(enginefuncs_t, x, p, size_t(h)) #define CDATA_ENG(x) CDATA_ENTRY(enginefuncs_t, x, P_PRE, 0u) -meta_enginefuncs_t g_meta_engfuncs; // static trampolines to dynamic callbacks (for gamedll) -meta_enginefuncs_t g_meta_engfuncs_jit; // dynamic jit callbacks +enginefuncs_t g_meta_engfuncs; // static trampolines to dynamic callbacks (for gamedll) +enginefuncs_t g_meta_engfuncs_jit; // dynamic jit callbacks void MM_PRE_HOOK mm_QueryClientCvarValue(const edict_t* pEdict, const char* cvarName) { diff --git a/metamod/src/engine_api.h b/metamod/src/engine_api.h index 29d5456..337ec14 100644 --- a/metamod/src/engine_api.h +++ b/metamod/src/engine_api.h @@ -12,8 +12,7 @@ typedef int (*GET_ENGINE_FUNCTIONS_FN)(enginefuncs_s *pengfuncsFromEngine, int * // Protect against other projects which use this include file but use the // normal enginefuncs_t type for their meta_engfuncs. #ifdef METAMOD_CORE - #include "meta_eiface.h" // meta_enginefuncs_t - extern meta_enginefuncs_t g_meta_engfuncs; + extern enginefuncs_t g_meta_engfuncs; void compile_engine_callbacks(); #else diff --git a/metamod/src/enginecallbacks.h b/metamod/src/enginecallbacks.h index e45aa71..187209c 100644 --- a/metamod/src/enginecallbacks.h +++ b/metamod/src/enginecallbacks.h @@ -10,14 +10,6 @@ // "hack" our way around that by using a flag METAMOD_CORE which is set // when compiling Metamod proper. -#ifdef METAMOD_CORE - #include "meta_eiface.h" // HL_enginefuncs_t - - // Use a #define to bend the enginefuncs_t type to our HL_enginefuncs_t - // type instead as we now use that for the global object g_engfuncs. - #define enginefuncs_t HL_enginefuncs_t -#endif - #include // ALERT, etc #ifdef METAMOD_CORE diff --git a/metamod/src/game_support.cpp b/metamod/src/game_support.cpp index 6ab1568..10bd0e0 100644 --- a/metamod/src/game_support.cpp +++ b/metamod/src/game_support.cpp @@ -116,7 +116,7 @@ bool setup_gamedll(gamedll_t *gamedll) // If the path is relative, the gamedll file will be missing and // it might be found in the cache file. - if (!IsAbsolutePath(gamedll->pathname)) + if (!is_abs_path(gamedll->pathname)) { char szInstallPath[MAX_PATH]; Q_snprintf(szInstallPath, sizeof(szInstallPath), "%s/%s", gamedll->gamedir, gamedll->pathname); diff --git a/metamod/src/h_export.cpp b/metamod/src/h_export.cpp index 789fd1b..ffb6348 100644 --- a/metamod/src/h_export.cpp +++ b/metamod/src/h_export.cpp @@ -34,7 +34,7 @@ void _fini() #endif //! Holds engine functionality callbacks -HL_enginefuncs_t g_engfuncs; +enginefuncs_t g_engfuncs; globalvars_t* gpGlobals; engine_t g_engine; @@ -49,7 +49,8 @@ void WINAPI GiveFnptrsToDll(enginefuncs_t *pengfuncsFromEngine, globalvars_t *pG g_engine.funcs = &g_engfuncs; g_engine.globals = pGlobals; - g_engfuncs.initialise_interface(pengfuncsFromEngine); + g_engfuncs = *pengfuncsFromEngine; + flush_ALERT_buffer(); // NOTE! Have to call logging function _after_ initialising g_engfuncs, so // that g_engfuncs.pfnAlertMessage() can be resolved properly, heh. :) META_DEV("called: GiveFnptrsToDll"); diff --git a/metamod/src/meta_eiface.cpp b/metamod/src/meta_eiface.cpp deleted file mode 100644 index f0b1cd4..0000000 --- a/metamod/src/meta_eiface.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "precompiled.h" - -void meta_new_dll_functions_t::set_from(NEW_DLL_FUNCTIONS* _pFuncs) -{ - Q_memcpy(this, _pFuncs, sizeof(NEW_DLL_FUNCTIONS)); -} - -void meta_enginefuncs_t::set_from(enginefuncs_t* _pFuncs) -{ - Q_memcpy(this, _pFuncs, sizeof(enginefuncs_t)); -} - -void HL_enginefuncs_t::initialise_interface(enginefuncs_t* _pFuncs) -{ - set_from(_pFuncs); - - // Now the pfnAlertMessage is available and we trust it to be a valid - // pointer, so flush the message buffer. - flush_ALERT_buffer(); -} diff --git a/metamod/src/meta_eiface.h b/metamod/src/meta_eiface.h deleted file mode 100644 index 8ff6f12..0000000 --- a/metamod/src/meta_eiface.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -// We use our own versions of the engine/dll interface structs. We add a -// few dummy entries to the end and set them to 0. That way we are -// protected from updates to the HL SDK adding new functions which would -// cause a) the game dll copying arbitrary values from us and b) the game -// dll overwriting our memory when using an old Metamod with a new game -// dll. - -// meta_new_dll_functions_t -struct meta_new_dll_functions_t: public NEW_DLL_FUNCTIONS -{ - // Fill this object with pointers copied from a NEW_DLL_FUNCTIONS struct. - void set_from(NEW_DLL_FUNCTIONS* pFuncs); -}; - -// meta_enginefuncs_t -struct meta_enginefuncs_t : public enginefuncs_t -{ - // Fill this object with pointers copied from an enginefuncs_t struct. - void set_from(enginefuncs_t* pFuncs); -}; - -// This is a specialisation of the meta_enginefuncs_t struct which is only -// used for the initial copy of the engine functions, i.e. those we get -// passed from the HL engine right at the beginning. -// Since there is only one master copy of engine functions this could be -// implemented as a singleton. This is left as an option for later. -struct HL_enginefuncs_t: public meta_enginefuncs_t -{ - HL_enginefuncs_t() {}; - - // Fill this object with pointers copied from an enginefuncs_t struct - // and fixup the interface. - // For this class this happens in the GiveFptrsToDll() function - // with the pointers passed from the HL engine. - void initialise_interface(enginefuncs_t* pFuncs); - -private: - // Moving copy_to() and set_from() to the private space. - void set_from(enginefuncs_t* pFuncs) - { - meta_enginefuncs_t::set_from(pFuncs); - } -}; diff --git a/metamod/src/metamod.cpp b/metamod/src/metamod.cpp index 752d781..8720a24 100644 --- a/metamod/src/metamod.cpp +++ b/metamod/src/metamod.cpp @@ -20,7 +20,7 @@ gamedll_t g_GameDLL; ALIGN16 meta_globals_t g_metaGlobals; -meta_enginefuncs_t g_plugin_engfuncs; +enginefuncs_t g_plugin_engfuncs; MPluginList *g_plugins; MRegCmdList *g_regCmds; @@ -84,7 +84,7 @@ void metamod_startup() { META_LOG("Configfile specified via localinfo: %s", cp); - if (FileExistsInGameDir(cp)) + if (is_file_exists_in_gamedir(cp)) { Q_strncpy(configFile, cp, sizeof configFile - 1); configFile[sizeof configFile - 1] = '\0'; @@ -93,7 +93,7 @@ void metamod_startup() META_ERROR("Empty/missing config.ini file: %s; falling back to %s", cp, configFile); } - if (!FileExistsInGameDir(configFile)) + if (!is_file_exists_in_gamedir(configFile)) { Q_strncpy(configFile, g_config->directory(), sizeof configFile - 1); configFile[sizeof configFile - 1] = '\0'; @@ -105,14 +105,14 @@ void metamod_startup() } Q_strcat(configFile, "/" CONFIG_INI); - if (!FileExistsInGameDir(configFile)) + if (!is_file_exists_in_gamedir(configFile)) { META_DEBUG(2, "No config.ini file found: %s", CONFIG_INI); } } // Load config file - if (FileExistsInGameDir(configFile)) + if (is_file_exists_in_gamedir(configFile)) g_config->load(configFile); // Now, override config options with localinfo commandline options. @@ -163,7 +163,7 @@ void metamod_startup() // Copy, and store pointer in g_engine struct. Yes, we could just store // the actual engine_t struct in g_engine, but then it wouldn't be a // pointer to match the other g_engfuncs. - g_plugin_engfuncs.set_from(g_engine.funcs); + g_plugin_engfuncs = *g_engine.funcs; g_engine.pl_funcs = &g_plugin_engfuncs; // substitute our special versions of various commands g_engine.pl_funcs->pfnAddServerCommand = meta_AddServerCommand; @@ -197,7 +197,7 @@ void metamod_startup() // In fact, we need gamedir even earlier, so moved up above. // Load plugins file - if (!FileExistsInGameDir(pluginFile)) + if (!is_file_exists_in_gamedir(pluginFile)) { Q_strncpy(pluginFile, g_config->directory(), sizeof pluginFile - 1); pluginFile[sizeof pluginFile - 1] = '\0'; @@ -209,7 +209,7 @@ void metamod_startup() } Q_strcat(pluginFile, "/" PLUGINS_INI); - if (!FileExistsInGameDir(pluginFile)) + if (!is_file_exists_in_gamedir(pluginFile)) { META_DEBUG(2, "No plugins.ini file found: %s", PLUGINS_INI); } @@ -243,7 +243,7 @@ void metamod_startup() execFile[sizeof execFile - 1] = '\0'; } - if (FileExistsInGameDir(execFile)) + if (is_file_exists_in_gamedir(execFile)) { if (execFile[0] == '/') META_ERROR("Cannot exec absolute pathnames: %s", execFile); @@ -268,7 +268,7 @@ bool meta_init_gamedll() Q_memset(&g_GameDLL, 0, sizeof g_GameDLL); GET_GAME_DIR(gamedir); - NormalizePath(gamedir); + normalize_path(gamedir); // As of 1.1.1.1, the engine routine GET_GAME_DIR no longer returns a // full-pathname, but rather just the path specified as the argument to @@ -280,7 +280,7 @@ bool meta_init_gamedll() // Note: the code has always assumed the server op wouldn't do: // hlds -game other/firearms // - if (IsAbsolutePath(gamedir)) + if (is_abs_path(gamedir)) { // Old style; GET_GAME_DIR returned full pathname. Copy this into // our gamedir, and truncate to get the game name. diff --git a/metamod/src/metamod.h b/metamod/src/metamod.h index bfb5b35..036da33 100644 --- a/metamod/src/metamod.h +++ b/metamod/src/metamod.h @@ -6,7 +6,6 @@ #include "conf_meta.h" // MConfig #include "osdep.h" // NAME_MAX, etc #include "mplayer.h" // MPlayerList -#include "meta_eiface.h" // HL_enginefuncs_t, meta_enginefuncs_t #include "engine_t.h" // engine_t, Engine #define PLUGINS_INI "plugins.ini" // file that lists plugins to load at startup @@ -32,11 +31,11 @@ struct gamedll_t extern gamedll_t g_GameDLL; // SDK variables for storing engine funcs and globals. -extern HL_enginefuncs_t g_engfuncs; +extern enginefuncs_t g_engfuncs; extern globalvars_t *gpGlobals; // Our modified version of the engine funcs, to give to plugins. -extern meta_enginefuncs_t g_plugin_engfuncs; +extern enginefuncs_t g_plugin_engfuncs; // g_config structure. extern MConfig *g_config; diff --git a/metamod/src/mlist.cpp b/metamod/src/mlist.cpp index bcabb07..59238e9 100644 --- a/metamod/src/mlist.cpp +++ b/metamod/src/mlist.cpp @@ -11,7 +11,7 @@ MPluginList::MPluginList(const char* ifile) : m_max_loaded_count(0) Q_memset(m_plist, 0, sizeof m_plist); for (int i = 0; i < MAX_PLUGINS; i++) { - m_plist[i].m_index = i + 1; // 1-based + new(m_plist + i) MPlugin(i + 1); // 1-based } m_max_loaded_count = 0; @@ -67,7 +67,6 @@ MPlugin *MPluginList::find(plid_t id) { if (m_plist[i].m_status < PL_VALID) continue; - if (m_plist[i].m_info == id) return &m_plist[i]; } @@ -131,10 +130,10 @@ MPlugin *MPluginList::find_match(const char *prefix, bool& unique) if (plug->m_status < PL_VALID) continue; - if (plug->m_info && !Q_strnicmp(plug->m_info->name, prefix, len) + if (plug->info() && !Q_strnicmp(plug->info()->name, prefix, len) || !Q_strnicmp(plug->m_desc, prefix, len) || !Q_strnicmp(plug->m_file, prefix, len) - || plug->m_info && !Q_strnicmp(plug->m_info->logtag, prefix, len)) + || plug->info() && !Q_strnicmp(plug->info()->logtag, prefix, len)) { if (pfound) { unique = false; @@ -182,7 +181,7 @@ MPlugin* MPluginList::plugin_addload(plid_t plid, const char* fname, PLUG_LOADTI return nullptr; } - if (pl_temp.resolve() != true) { + if (!pl_temp.resolve()) { META_DEBUG(1, "Couldn't resolve given path into a file: %s", pl_temp.m_file); return nullptr; } @@ -215,7 +214,6 @@ MPlugin* MPluginList::plugin_addload(plid_t plid, const char* fname, PLUG_LOADTI return nullptr; } - meta_rebuild_callbacks(); META_DEBUG(1, "Loaded plugin '%s' successfully", pl_added->m_desc); return pl_added; @@ -263,7 +261,7 @@ MPlugin* MPluginList::add(MPlugin* padd) // copy pathname Q_strncpy(iplug->m_pathname, padd->m_pathname, sizeof iplug->m_pathname - 1); iplug->m_pathname[sizeof iplug->m_pathname - 1] = '\0'; - NormalizePath(iplug->m_pathname); + normalize_path(iplug->m_pathname); iplug->m_source = padd->m_source; iplug->m_status = padd->m_status; @@ -272,15 +270,13 @@ MPlugin* MPluginList::add(MPlugin* padd) return iplug; } - // Read plugins.ini at server startup. bool MPluginList::ini_startup() { char line[MAX_STRBUF_LEN]; int n, ln; - MPlugin *pmatch; - if (!FileExistsInGameDir(m_inifile)) + if (!is_file_exists_in_gamedir(m_inifile)) { META_ERROR("ini: Metamod plugins file empty or missing: %s", m_inifile); return false; @@ -307,9 +303,7 @@ bool MPluginList::ini_startup() // Parse directly into next entry in array if (!m_plist[n].ini_parseline(line)) - { continue; - } // Check for a duplicate - an existing entry with this pathname. if (find(m_plist[n].m_pathname)) @@ -321,18 +315,13 @@ bool MPluginList::ini_startup() // Check for a matching platform with different platform specifics // level. - if (nullptr != (pmatch = find_match(&m_plist[n]))) + auto pmatch = find_match(&m_plist[n]); + if (pmatch) { - if (pmatch->m_pfspecific >= m_plist[n].m_pfspecific) - { - META_DEBUG(1, "ini: Skipping plugin, line %d of %s: plugin with higher platform specific level already exists. (%d >= %d)", ln, m_inifile, pmatch->m_pfspecific, m_plist[n].m_pfspecific); - continue; - } - - META_DEBUG(1, "ini: Plugin in line %d overrides existing plugin with lower platform specific level %d, ours %d", ln, pmatch->m_pfspecific, m_plist[n].m_pfspecific); - int _index = pmatch->m_index; + META_DEBUG(1, "ini: Plugin in line %d overrides existing plugin", ln); + int index = pmatch->m_index; Q_memset(pmatch, 0, sizeof(MPlugin)); - pmatch->m_index = _index; + pmatch->m_index = index; } m_plist[n].m_action = PA_LOAD; META_LOG("ini: Read plugin config for: %s", m_plist[n].m_desc); @@ -340,12 +329,10 @@ bool MPluginList::ini_startup() m_max_loaded_count = n; // mark end of list } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins to load", m_inifile, n); - fclose(fp); + if (!n) - { - META_ERROR("ini: Warning; no plugins found to load?"); - } + META_LOG("ini: Warning; no plugins found to load?"); return true; } @@ -355,8 +342,6 @@ bool MPluginList::ini_refresh() { char line[MAX_STRBUF_LEN]; int n, ln; - MPlugin pl_temp; - MPlugin *pl_found, *pl_added; FILE* fp = fopen(m_inifile, "r"); if (!fp) @@ -377,7 +362,7 @@ bool MPluginList::ini_refresh() *cp = '\0'; // Parse into a temp plugin - Q_memset(&pl_temp, 0, sizeof pl_temp); + MPlugin pl_temp = {}; if (!pl_temp.ini_parseline(line)) { META_ERROR("ini: Skipping malformed line %d of %s",ln, m_inifile); @@ -385,32 +370,30 @@ bool MPluginList::ini_refresh() } // Try to find plugin with this pathname in the current list of // plugins. - if (!(pl_found = find(pl_temp.m_pathname))) + auto pl_found = find(pl_temp.m_pathname); + if (!pl_found) { // Check for a matching platform with higher platform specifics // level. - if (nullptr != (pl_found = find_match(&pl_temp))) + pl_found = find_match(&pl_temp); + if (pl_found) { - if (pl_found->m_pfspecific >= pl_temp.m_pfspecific) + if (pl_found->m_action == PA_LOAD) { - META_DEBUG(1, "ini: Skipping plugin, line %d of %s: plugin with higher platform specific level already exists. (%d >= %d)", ln, m_inifile, pl_found->m_pfspecific, pl_temp.m_pfspecific); - continue; - } - if (PA_LOAD == pl_found->m_action) - { - META_DEBUG(1, "ini: Plugin in line %d overrides loading of plugin with lower platform specific level %d, ours %d", ln, pl_found->m_pfspecific, pl_temp.m_pfspecific); + META_DEBUG(1, "ini: Plugin in line %d overrides loading of plugin", ln); int _index = pl_found->m_index; Q_memset(pl_found, 0, sizeof(MPlugin)); pl_found->m_index = _index; } else { - META_DEBUG(1, "ini: Plugin in line %d should override existing plugin with lower platform specific level %d, ours %d. Unable to comply.", ln, pl_found->m_pfspecific, pl_temp.m_pfspecific); + META_DEBUG(1, "ini: Plugin in line %d should override existing plugin. Unable to comply.", ln); continue; } } // new plugin; add to list - if ((pl_added = add(&pl_temp))) + auto pl_added = add(&pl_temp); + if (pl_added) { // try to load this plugin at the next opportunity pl_added->m_action = PA_LOAD; @@ -447,22 +430,17 @@ bool MPluginList::ini_refresh() } if (pl_found) - { META_LOG("ini: Read plugin config for: %s", pl_found->m_desc); - } else - { META_LOG("ini: Read plugin config for: %s", pl_temp.m_desc); - } + n++; } META_LOG("ini: Finished reading plugins list: %s; Found %d plugins", m_inifile, n); - fclose(fp); + if (!n) - { META_ERROR("ini: Warning; no plugins found to load?"); - } return true; } @@ -471,9 +449,7 @@ bool MPluginList::ini_refresh() bool MPluginList::cmd_addload(const char* args) { MPlugin pl_temp = {}; - MPlugin *pl_found, *pl_added; - - if (pl_temp.cmd_parseline(args) != true) + if (!pl_temp.cmd_parseline(args)) { META_CONS("Couldn't parse 'meta load' arguments: %s", args); return false; @@ -481,7 +457,7 @@ bool MPluginList::cmd_addload(const char* args) // resolve given path into a file; accepts various "shortcut" // pathnames. - if (pl_temp.resolve() != true) + if (!pl_temp.resolve()) { // Couldn't find a matching file on disk META_CONS("Couldn't resolve given path into a file: %s", pl_temp.m_file); @@ -490,14 +466,16 @@ bool MPluginList::cmd_addload(const char* args) // Try to find plugin with this pathname in the current list of // plugins. - if ((pl_found = find(pl_temp.m_pathname))) + auto pl_found = find(pl_temp.m_pathname); + if (pl_found) { // Already in list META_CONS("Plugin '%s' already in current list; file=%s desc='%s'", pl_temp.m_file, pl_found->m_file, pl_found->m_desc); return false; } // new plugin; add to list - if (!(pl_added = add(&pl_temp))) + auto pl_added = add(&pl_temp); + if (!pl_added) { META_CONS("Couldn't add plugin '%s' to list; see log", pl_temp.m_desc); return false; @@ -513,14 +491,13 @@ bool MPluginList::cmd_addload(const char* args) else META_CONS("Couldn't load plugin '%s'; see log", pl_added->m_desc); - show(0); + show(); return false; } META_CONS("Loaded plugin '%s' successfully", pl_added->m_desc); + show(); meta_rebuild_callbacks(); - show(0); - return true; } @@ -540,16 +517,15 @@ bool MPluginList::load() if (m_plist[i].m_status < PL_VALID) continue; - if (m_plist[i].load(PT_STARTUP) == true) + if (m_plist[i].load(PT_STARTUP)) n++; else // all plugins should be loadable at startup... META_ERROR("dll: Failed to load plugin '%s'", m_plist[i].m_file); } - meta_rebuild_callbacks(); - META_LOG("dll: Finished loading %d plugins", n); + meta_rebuild_callbacks(); return true; } @@ -598,7 +574,7 @@ bool MPluginList::refresh(PLUG_LOADTIME now) { META_DEBUG(1, "Unloading plugin '%s'", iplug->m_desc); iplug->m_action = PA_UNLOAD; - if (iplug->unload(now, PNL_INI_DELETED, PNL_INI_DELETED)) + if (iplug->unload(now, PNL_INI_DELETED)) nunloaded++; /*else if (meta_errno == ME_DELAYED) TODO ndelayed++;*/ @@ -630,9 +606,8 @@ bool MPluginList::refresh(PLUG_LOADTIME now) ndone++; } - meta_rebuild_callbacks(); - META_LOG("dll: Finished updating %d plugins; kept %d, loaded %d, unloaded %d, reloaded %d, delayed %d", ndone, nkept, nloaded, nunloaded, nreloaded, ndelayed); + meta_rebuild_callbacks(); return true; } @@ -688,9 +663,9 @@ void MPluginList::show(int source_index) Q_strncpy(file, pl->m_file, sizeof file - 1); file[sizeof file - 1] = '\0'; - if (pl->m_info && pl->m_info->version) + if (pl->info() && pl->info()->version) { - Q_strncpy(vers, pl->m_info->version, sizeof vers - 1); + Q_strncpy(vers, pl->info()->version, sizeof vers - 1); vers[sizeof vers - 1] = '\0'; } else @@ -724,16 +699,16 @@ void MPluginList::show_client(edict_t *pEntity) for (int i = 0; i < m_max_loaded_count; i++) { auto pl = &m_plist[i]; - if (pl->m_status != PL_RUNNING || !pl->m_info) + if (pl->m_status != PL_RUNNING || !pl->info()) continue; n++; META_CLIENT(pEntity, " [%3d] %s, v%s, %s, by %s, see %s", n, - pl->m_info->name ? pl->m_info->name : "", - pl->m_info->version ? pl->m_info->version : "", - pl->m_info->date ? pl->m_info->date : "<../../..>", - pl->m_info->author ? pl->m_info->author : "", - pl->m_info->url ? pl->m_info->url : ""); + pl->info()->name ? pl->info()->name : "", + pl->info()->version ? pl->info()->version : "", + pl->info()->date ? pl->info()->date : "<../../..>", + pl->info()->author ? pl->info()->author : "", + pl->info()->url ? pl->info()->url : ""); } META_CLIENT(pEntity, "%d plugins", n); diff --git a/metamod/src/mlist.h b/metamod/src/mlist.h index 4588b4f..4781f6f 100644 --- a/metamod/src/mlist.h +++ b/metamod/src/mlist.h @@ -12,7 +12,8 @@ #define WIDTH_MAX_PLUGINS 2 // A list of plugins. -class MPluginList { +class MPluginList +{ public: MPluginList(const char *ifile); diff --git a/metamod/src/mplugin.cpp b/metamod/src/mplugin.cpp index 7b28277..fd2264c 100644 --- a/metamod/src/mplugin.cpp +++ b/metamod/src/mplugin.cpp @@ -1,49 +1,54 @@ #include "precompiled.h" -const char* g_platform_postfixes[] = { - "_i386.so", - "_i486.so", - "_i586.so", - "_i686.so", +const char *MPlugin::s_rPrintLoadTime[][4] = { + // SL_SIMPLE // SL_SHOW // SL_ALLOWED // SL_NOW + { "never", "Never", "never", "never" }, // PT_NEVER + { "startup", "Start", "at server startup", "during server startup" }, // PT_STARTUP + { "changelevel","Chlvl", "at changelevel", "during changelevel" }, // PT_CHANGELEVEL + { "anytime", "ANY", "at any time", "during map" }, // PT_ANYTIME + { "pausable", "Pause", "at any time, and pausable", "for requested pause" }, // PT_ANYPAUSE }; -// Parse a line from plugins.ini into a plugin. -bool MPlugin::ini_parseline(char *_line) +// ReSharper disable once CppPossiblyUninitializedMember +MPlugin::MPlugin() { - char line[1024]; - strncpy(line, _line, sizeof line - 1); - line[sizeof line - 1] = '\0'; +} - trimbuf(line); +// ReSharper disable once CppPossiblyUninitializedMember +MPlugin::MPlugin(int index) : m_index(index) +{ +} + +// Parse a line from plugins.ini into a plugin. +bool MPlugin::ini_parseline(char *line) +{ + char buf[1024]; + strncpy(buf, line, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; + + trimbuf(buf); // skip empty lines - if (line[0] == '\0') { - META_DEBUG(7, "ini: Ignoring empty line: %s", line); + if (buf[0] == '\0') { return false; } - if (line[0] == '#' || line[0] == ';' || !Q_strncmp(line, "//", 2)) { - META_DEBUG(7, "ini: Ignoring commented line: %s", line); + // skip comments + if (buf[0] == '#' || buf[0] == ';' || !Q_strncmp(buf, "//", 2)) { return false; } // grab platform ("win32" or "linux") char* ptr_token; - char* token = strtok_r(line, " \t", &ptr_token); - if (!token) + char* token = strtok_r(buf, " \t", &ptr_token); + if (!token) { return false; + } - if (!Q_stricmp(token, PLATFORM)) { - m_pfspecific = 0; // TODO: remove it? - } - else if (!Q_stricmp(token, PLATFORM_SPC)) { - m_pfspecific = 1; - } - else { - // plugin is not for this OS - META_DEBUG(7, "ini: Ignoring entry for %s", token); - return false; - } + if (!strcmp(token, "linux")) + m_platform = SP_LINUX; + else + m_platform = SP_WINDOWS; // grab filename token = strtok_r(nullptr, " \t\r\n", &ptr_token); @@ -53,7 +58,7 @@ bool MPlugin::ini_parseline(char *_line) Q_strncpy(m_filename, token, sizeof m_filename - 1); m_filename[sizeof m_filename - 1] = '\0'; - NormalizePath(m_filename); + normalize_path(m_filename); // Store name of just the actual _file_, without dir components. char* cp = Q_strrchr(m_filename, '/'); @@ -87,74 +92,6 @@ bool MPlugin::ini_parseline(char *_line) return true; } -// Unload a plugin from plugin request -bool MPlugin::plugin_unload(plid_t plid, PLUG_LOADTIME now, PL_UNLOAD_REASON reason) -{ - MPlugin *pl_unloader = g_plugins->find(plid); - - if (!pl_unloader) { - META_WARNING("dll: Not unloading plugin '%s'; plugin that requested unload is not found.", m_desc); - return false; - } - if (pl_unloader->m_index == m_index) { - META_WARNING("dll: Not unloading plugin '%s'; Plugin tried to unload itself.", m_desc); - return false; - } - if (m_is_unloader) { - META_WARNING("dll: Not unloading plugin '%s'; Plugin is unloading plugin that tried to unload it.", m_desc); - return false; - } - - m_unloader_index = pl_unloader->m_index; - - //block unloader from being unloaded by other plugin - pl_unloader->m_is_unloader = true; - - //try unload - PLUG_ACTION old_action = m_action; - m_action = PA_UNLOAD; - - if (unload(now, reason, PNL_PLG_FORCED)) { - META_DEBUG(1, "Unloaded plugin '%s'", m_desc); - pl_unloader->m_is_unloader = false; - return true; - } - - pl_unloader->m_is_unloader = false; - m_action = old_action; - - return false; -} - -// Parse a filename string from PEXT_LOAD_PLUGIN_BY_ *function into a plugin. -bool MPlugin::plugin_parseline(const char *fname, int loader_index) -{ - m_source_plugin_index = loader_index; - - Q_strncpy(m_filename, fname, sizeof m_filename - 1); - m_filename[sizeof m_filename - 1] = '\0'; - NormalizePath(m_filename); - - //store just name of the actual _file, without path - char* cp = Q_strrchr(m_filename, '/'); - if (cp) - m_file = cp + 1; - else - m_file = m_filename; - - //grab description - //temporarily use plugin file until plugin can be queried - Q_snprintf(m_desc, sizeof m_desc, "<%s>", m_file); - - //make full pathname - full_gamedir_path(m_filename, m_pathname); - - m_source = PS_PLUGIN; - m_status = PL_VALID; - - return true; -} - // Parse a line from console "load" command into a plugin. bool MPlugin::cmd_parseline(const char *line) { @@ -176,7 +113,7 @@ bool MPlugin::cmd_parseline(const char *line) Q_strncpy(m_filename, token, sizeof m_filename - 1); m_filename[sizeof m_filename - 1] = '\0'; - NormalizePath(m_filename); + normalize_path(m_filename); // store name of just the actual _file_, without dir components char* cp = Q_strrchr(m_filename, '/'); @@ -188,14 +125,12 @@ bool MPlugin::cmd_parseline(const char *line) // Grab description. // Specify no delimiter chars, as we just want the rest of the line. token = strtok_r(nullptr, "", &ptr_token); - if (token) - { + if (token) { token = token + strspn(token, " \t"); // skip whitespace Q_strncpy(m_desc, token, sizeof m_desc - 1); m_desc[sizeof m_desc - 1] = '\0'; } - else - { + else { // if no description is specified, temporarily use plugin file, // until plugin can be queried, and desc replaced with info->name. Q_snprintf(m_desc, sizeof m_desc, "<%s>", m_file); @@ -210,6 +145,35 @@ bool MPlugin::cmd_parseline(const char *line) return true; } +// Parse a filename string from PEXT_LOAD_PLUGIN_BY_ *function into a plugin. +bool MPlugin::plugin_parseline(const char *fname, int loader_index) +{ + m_source_plugin_index = loader_index; + + Q_strncpy(m_filename, fname, sizeof m_filename - 1); + m_filename[sizeof m_filename - 1] = '\0'; + normalize_path(m_filename); + + //store just name of the actual _file, without path + char* cp = Q_strrchr(m_filename, '/'); + if (cp) + m_file = cp + 1; + else + m_file = m_filename; + + //grab description + //temporarily use plugin file until plugin can be queried + Q_snprintf(m_desc, sizeof m_desc, "<%s>", m_file); + + //make full pathname + full_gamedir_path(m_filename, m_pathname); + + m_source = PS_PLUGIN; + m_status = PL_VALID; + + return true; +} + // Make sure this plugin has the necessary minimal information. bool MPlugin::check_input() { @@ -230,7 +194,6 @@ bool MPlugin::check_input() META_ERROR("dll: Tried to operate on plugin[%d] with an empty pathname", m_index); return false; } - if (!m_desc[0]) { // if no description is specified, temporarily use plugin file, // until plugin can be queried, and desc replaced with info->name. @@ -242,29 +205,26 @@ bool MPlugin::check_input() // Try to resolve a plugin's filename as a (possibly partial) path to an // actual filename on disk, to facilitate easier console load-command -// arguments. Uses resolve_dirs, resolve_prefix, and resolve_suffix below. +// arguments. Uses resolve_dirs and resolve_suffix below. // Example paths that it tries: // filename // Gamedir/filename.dll, Gamedir/filename.so // Gamedir/filename_i386.so -// Gamedir/dlls/mm_filename_i386.so // Gamedir/dlls/filename_mm_i386.so // Gamedir/dlls/filename_MM_i386.so bool MPlugin::resolve() { - char *found; - if (!check_input()) { return false; } - if (IsAbsolutePath(m_filename)) - found = resolve_prefix(m_filename); + char* found, tempbuf[MAX_PATH]; + if (is_abs_path(m_filename)) + found = resolve_suffix(m_filename, tempbuf, sizeof tempbuf); else - found = resolve_dirs(m_filename); + found = resolve_dirs(m_filename, tempbuf, sizeof tempbuf); - if (!found) - { + if (!found) { META_DEBUG(2, "Couldn't resolve '%s' to file", m_filename); return false; } @@ -282,20 +242,15 @@ bool MPlugin::resolve() else m_file = m_pathname; + const char* filename = m_pathname; + // store pathname: the gamedir relative path, or an absolute path size_t len = Q_strlen(g_GameDLL.gamedir); - if (!Q_strnicmp(m_pathname, g_GameDLL.gamedir, len)) - { - Q_strncpy(m_filename, m_pathname + len + 1, sizeof m_filename - 1); - m_filename[sizeof m_filename - 1] = '\0'; - } - else - { - Q_strncpy(m_filename, m_pathname, sizeof m_filename - 1); - m_filename[sizeof m_filename - 1] = '\0'; - } + filename += len + 1; + Q_strncpy(m_filename, filename, sizeof m_filename - 1); + m_filename[sizeof m_filename - 1] = '\0'; return true; } @@ -304,73 +259,32 @@ bool MPlugin::resolve() // Try: // GAMEDIR/filename // GAMEDIR/dlls/filename -char *MPlugin::resolve_dirs(char *path) const +char *MPlugin::resolve_dirs(const char *path, char *tempbuf, size_t maxlen) const { - static char buf[PATH_MAX]; + // try in gamedir + Q_snprintf(tempbuf, maxlen, "%s/%s", g_GameDLL.gamedir, path); + if (is_valid_path(tempbuf)) + return tempbuf; - Q_snprintf(buf, sizeof buf, "%s/%s", g_GameDLL.gamedir, path); + // try other file prefixes in gamedir + char buf[MAX_PATH]; + strncpy(buf, tempbuf, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; - // try this path - struct stat st; - if (!stat(buf, &st) && S_ISREG(st.st_mode)) - return buf; - - // try other file prefixes in this path - char* found = resolve_prefix(buf); + char* found = resolve_suffix(buf, tempbuf, maxlen); if (found) return found; - Q_snprintf(buf, sizeof buf, "%s/dlls/%s", g_GameDLL.gamedir, path); + // try in gamedir/dlls/ + Q_snprintf(tempbuf, maxlen, "%s/dlls/%s", g_GameDLL.gamedir, path); + if (is_valid_path(tempbuf)) + return tempbuf; - // try this path - if (!stat(buf, &st) && S_ISREG(st.st_mode)) - return buf; + strncpy(buf, tempbuf, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; // try other file prefixes for this path - return resolve_prefix(buf); -} - -// For the given path, tries several possible filename prefixes. -// Try: -// dir/mm_file -// dir/file -char *MPlugin::resolve_prefix(char *path) const -{ - struct stat st; - char dname[PATH_MAX]; - static char buf[PATH_MAX]; - char *found; - - // try "mm_" prefix FIRST. - // split into dirname and filename - Q_strncpy(dname, path, sizeof dname - 1); - dname[sizeof dname - 1] = '\0'; - - char* cp = Q_strrchr(dname, '/'); - if (cp) - { - *cp = '\0'; - Q_snprintf(buf, sizeof buf, "%s/mm_%s", dname, cp + 1); - } - else - { - // no directory in given path - Q_snprintf(buf, sizeof buf, "mm_%s", path); - } - - // try this path - if (!stat(buf, &st) && S_ISREG(st.st_mode)) - return buf; - - // try other suffixes for this path - if ((found = resolve_suffix(buf))) - return found; - - // try other suffixes for the original path - if ((found = resolve_suffix(path))) - return found; - - return nullptr; + return resolve_suffix(buf, tempbuf, maxlen); } // For the given path, tries several different filename suffixes. @@ -378,66 +292,42 @@ char *MPlugin::resolve_prefix(char *path) const // path // path_mm // path_MM -// path.so (linux), path.dll (win32), path.dylib (osx) +// path.so (linux), path.dll (win32) // path_i386.so, path_i486.so, etc (if linux) -char *MPlugin::resolve_suffix(char *path) const +char* MPlugin::resolve_suffix(const char *path, char *tempbuf, size_t bufsize) const { - char *found = nullptr; - - auto check = [](const char* path) -> char* - { - struct stat st; - - if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) { - static char buf[PATH_MAX]; - strncpy(buf, path, sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - return buf; - } - - return nullptr; - }; - - if (Q_strstr(path, PLATFORM_DLEXT)) { - return check(path); + if (Q_strstr(path, PLATFORM_DLEXT) && is_valid_path(path)) { + strncpy(tempbuf, path, bufsize - 1); + tempbuf[bufsize - 1] = '\0'; + return tempbuf; } - char tmpbuf[PATH_MAX]; - Q_snprintf(tmpbuf, sizeof tmpbuf, "%s%s", path, PLATFORM_DLEXT); - if ((found = check(tmpbuf))) return found; + // try with ext + Q_snprintf(tempbuf, bufsize, "%s%s", path, PLATFORM_DLEXT); + if (is_valid_path(tempbuf)) + return tempbuf; - // Hmm, recursion. - if (!Q_strstr(path, "_mm")) - { - Q_snprintf(tmpbuf, sizeof tmpbuf, "%s_mm", path); - if ((found = resolve_suffix(tmpbuf))) return found; + // try add suffix + if (!Q_strstr(path, "_mm")) { + char buf[MAX_PATH]; + Q_snprintf(buf, sizeof buf, "%s_mm", path); + + char* found = resolve_suffix(buf, tempbuf, bufsize); + if (found) + return found; } #ifndef _WIN32 for (size_t i = 0; i < arraysize(g_platform_postfixes); i++) { - Q_snprintf(tmpbuf, sizeof tmpbuf, "%s%s", path, g_platform_postfixes[i]); - if ((found = check(tmpbuf))) return found; + Q_snprintf(tempbuf, bufsize, "%s%s", path, g_platform_postfixes[i]); + if (is_valid_path(tempbuf)) + return tempbuf; } #endif return nullptr; } -// Check if a passed string starts with a known platform postfix. -// It does not check beyond the period in order to work for both -// Linux and Win32. -static bool is_platform_postfix(char *pf) -{ - if (pf) { - for (size_t i = 0; i < arraysize(g_platform_postfixes); i++) { - if (!Q_strcmp(pf, g_platform_postfixes[i])) - return true; - } - } - return false; -} - - // Check if a given plugin is the same but possibly for a // different platform. A match is considered to be found if // 1. the filename without the path is the same, or @@ -450,34 +340,34 @@ static bool is_platform_postfix(char *pf) // the part up to the last dot, if one exists. bool MPlugin::platform_match(MPlugin *other) const { - if (m_status == PL_EMPTY || other->m_status == PL_EMPTY) + if (m_status == PL_EMPTY || other->status() == PL_EMPTY) return false; - if (!Q_strcmp(m_file, other->m_file)) + if (!Q_strcmp(m_file, other->file())) return true; - if (m_status >= PL_OPENED && other->m_status >= PL_OPENED && !Q_strcmp(m_info->logtag, other->m_info->logtag)) + if (m_status >= PL_OPENED && other->status() >= PL_OPENED && !Q_strcmp(m_info->logtag, other->info()->logtag)) return true; - if (*m_desc != '\0' && !Q_stricmp(m_desc, other->m_desc)) + if (m_desc[0] != '\0' && other->description()[0] != '0' && !Q_stricmp(m_desc, other->description())) return true; - char* end = Q_strrchr(m_file, '_'); + const char* end = Q_strrchr(m_file, '_'); if (!end || !is_platform_postfix(end)) end = Q_strrchr(m_file, '.'); - - char* other_end = Q_strrchr(other->m_file, '_'); + + const char* other_end = Q_strrchr(other->file(), '_'); if (!other_end || !is_platform_postfix(other_end)) - other_end = Q_strrchr(other->m_file, '.'); + other_end = Q_strrchr(other->file(), '.'); if (!end || !other_end) return false; int prefixlen = end - m_file; - if (other_end - other->m_file != prefixlen) + if (other_end - other->file() != prefixlen) return false; - if (!Q_strncmp(m_file, other->m_file, prefixlen)) + if (!Q_strncmp(m_file, other->file(), prefixlen)) return true; return false; @@ -486,29 +376,23 @@ bool MPlugin::platform_match(MPlugin *other) const // Load a plugin; query, check allowed time, attach. bool MPlugin::load(PLUG_LOADTIME now) { - if (!check_input()) - { + if (!check_input()) { return false; } - if (m_status >= PL_RUNNING) - { + if (m_status >= PL_RUNNING) { META_ERROR("dll: Not loading plugin '%s'; already loaded (status=%s)", m_desc, str_status()); return false; } - if (m_action != PA_LOAD && m_action != PA_ATTACH) - { + if (m_action != PA_LOAD && m_action != PA_ATTACH) { META_ERROR("dll: Not loading plugin '%s'; not marked for load (action=%s)", m_desc, str_action()); return false; } - if (m_status < PL_OPENED) - { + if (m_status < PL_OPENED) { // query plugin; open file and get info about it - if (!query()) - { + if (!query()) { META_ERROR("dll: Skipping plugin '%s'; couldn't query", m_desc); - if (!m_sys_module.unload()) - { + if (!m_sys_module.unload()) { META_ERROR("dll: Couldn't close plugin file '%s': %s", m_file, "invalid handle"); } m_status = PL_BADFILE; @@ -519,26 +403,21 @@ bool MPlugin::load(PLUG_LOADTIME now) } // are we allowed to attach this plugin at this time? - if (m_info->loadable < now) - { - if (m_info->loadable > PT_STARTUP) - { + if (m_info->loadable < now) { + if (m_info->loadable > PT_STARTUP) { // will try to attach again at next opportunity META_DEBUG(2, "dll: Delaying load plugin '%s'; can't attach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); return false; } - else - { - META_DEBUG(2, "dll: Failed load plugin '%s'; can't attach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); - // don't try to attach again later - m_action = PA_NONE; - return false; - } + + META_DEBUG(2, "dll: Failed load plugin '%s'; can't attach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); + // don't try to attach again later + m_action = PA_NONE; + return false; } // attach plugin; get function tables - if (attach(now) != true) - { + if (!attach(now)) { META_ERROR("dll: Failed to attach plugin '%s'", m_desc); // Note we don't dlclose() here, since we're returning PL_FAILED, // which implies that it's been dlopened and queried successfully. @@ -567,6 +446,279 @@ bool MPlugin::load(PLUG_LOADTIME now) return true; } +// Unload a plugin. Check time, detach. +bool MPlugin::unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) +{ + if (!check_input()) { + return false; + } + if (m_status < PL_RUNNING) { + if (reason != PNL_CMD_FORCED && reason != PNL_RELOAD) { + META_ERROR("dll: Not unloading plugin '%s'; already unloaded (status=%s)", m_desc, str_status()); + return false; + } + } + if (m_action != PA_UNLOAD && m_action != PA_RELOAD) { + META_WARNING("dll: Not unloading plugin '%s'; not marked for unload (action=%s)", m_desc, str_action()); + return false; + } + + // Are we allowed to detach this plugin at this time? + // If forcing unload, we disregard when plugin wants to be unloaded. + if (m_info && m_info->unloadable < now) { + if (reason == PNL_CMD_FORCED) { + META_DEBUG(2, "dll: Forced unload plugin '%s' overriding allowed times: allowed=%s; now=%s", m_desc, str_unloadable(), str_loadtime(now, SL_SIMPLE)); + } + else { + if (m_info->unloadable > PT_STARTUP) { + META_DEBUG(2, "dll: Delaying unload plugin '%s'; can't detach now: allowed=%s; now=%s", m_desc, str_unloadable(), str_loadtime(now, SL_SIMPLE)); + // caller should give message to user + // try to unload again at next opportunity + return false; + } + + META_DEBUG(2, "dll: Failed unload plugin '%s'; can't detach now: allowed=%s; now=%s", m_desc, str_unloadable(), str_loadtime(now, SL_SIMPLE)); + // don't try to unload again later + m_action = PA_NONE; + return false; + } + } + + // If unloading during map, then I'd like to call plugin's + // ServerDeactivate. However, I don't want to do this until I start + // calling ServerActivate when loading during map, since the SDK + // indicates these two routines should match call for call. + + // detach plugin + if (!detach(now, reason)) { + if (reason == PNL_RELOAD) { + META_DEBUG(2, "dll: Reload plugin '%s' overriding failed detach", m_desc); + } + else if (reason == PNL_CMD_FORCED) { + META_DEBUG(2, "dll: Forced unload plugin '%s' overriding failed detach"); + } + else { + META_WARNING("dll: Failed to detach plugin '%s'; ", m_desc); + return false; + } + } + + g_plugins->clear_source_plugin_index(m_index); + + // successful detach, or forced unload + + // Unmark registered commands for this plugin (by index number). + g_regCmds->remove(m_index); + // Unmark registered cvars for this plugin (by index number). + g_regCvars->disable(m_index); + + // Close the file. Note: after this, attempts to reference any memory + // locations in the file will produce a segfault. + if (!m_sys_module.unload()) { + // If DLL cannot be closed, OS is badly broken or we are giving invalid handle. + // So we don't return here but instead remove plugin from our listings. + META_WARNING("dll: Couldn't close plugin file '%s': %s", m_file, "invalid handle"); + } + + if (m_action == PA_UNLOAD) { + m_status = PL_EMPTY; + } + else if (m_action == PA_RELOAD) { + m_status = PL_VALID; + m_action = PA_LOAD; + } + + clear(); + + META_LOG("dll: Unloaded plugin '%s' for reason '%s'", m_desc, str_reason(reason)); + meta_rebuild_callbacks(); + return true; +} + +// Reload a plugin; unload and load again. +bool MPlugin::reload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) +{ + if (!check_input()) { + return false; + } + // Are we allowed to load this plugin at this time? + // If we cannot load the plugin after unloading it, we keep it. + if (m_info && m_info->loadable < now) { + if (m_info->loadable > PT_STARTUP) { + META_DEBUG(2, "dll: Delaying reload plugin '%s'; would not be able to reattach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); + // caller should give message to user + // try to reload again at next opportunity + return false; + } + else { + META_DEBUG(2, "dll: Failed reload plugin '%s'; would not be able to reattach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); + // don't try to reload again later + m_action = PA_NONE; + return false; + } + } + + if (m_status < PL_RUNNING) { + META_WARNING("dll: Plugin '%s' isn't running; Forcing unload plugin for reloading", m_desc); + reason = PNL_RELOAD; + } + + if (!unload(now, reason)) { + META_WARNING("dll: Failed to unload plugin '%s' for reloading", m_desc); + return false; + } + + if (!load(now)) { + META_WARNING("dll: Failed to reload plugin '%s' after unloading", m_desc); + return false; + } + + return true; +} + +// Pause a plugin; temporarily disabled for API routines. +bool MPlugin::pause() +{ + if (m_status == PL_PAUSED) { + META_ERROR("Not pausing plugin '%s'; already paused", m_desc); + return false; + } + if (m_status != PL_RUNNING) { + META_ERROR("Cannot pause plugin '%s'; not currently running (status=%s)", m_desc, str_status()); + return false; + } + + // are we allowed to pause this plugin? + if (m_info->unloadable < PT_ANYPAUSE) { + META_ERROR("Cannot pause plugin '%s'; not allowed by plugin (allowed=%s)", m_desc, str_unloadable()); + m_action = PA_NONE; + return false; + } + + m_status = PL_PAUSED; + META_LOG("Paused plugin '%s'", m_desc); + return true; +} + +// Unpause a plugin. +bool MPlugin::unpause() +{ + if (m_status != PL_PAUSED) { + META_ERROR("Cannot unpause plugin '%s'; not currently paused (status=%s)", m_desc, str_status()); + return false; + } + + m_status = PL_RUNNING; + META_LOG("Unpaused plugin '%s'", m_desc); + return true; +} + +// Retry pending action, presumably from a previous failure. +bool MPlugin::retry(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) +{ + if (m_action == PA_LOAD || m_action == PA_ATTACH) + return load(now); + if (m_action == PA_UNLOAD) + return unload(now, reason); + if (m_action == PA_RELOAD) + return reload(now, reason); + + META_ERROR("No pending action to retry for plugin '%s'; (status=%s, action=%s)", m_desc, str_status(), str_action()); + return false; +} + +template +void free_table(T& table) +{ + if (table) { + Q_free(table); + table = nullptr; + } +} + +// Clear a plugin (it failed a previous action and should be +// removed from the list, or it's being unloaded). +bool MPlugin::clear() +{ + if (m_status != PL_FAILED && m_status != PL_BADFILE && m_status != PL_EMPTY && m_status != PL_OPENED) { + META_ERROR("Cannot clear plugin '%s'; not marked as failed, empty, or open (status=%s)", m_desc, str_status()); + return false; + } + // If file is open, close the file. Note: after this, attempts to + // reference any memory locations in the file will produce a segfault. + if (!m_sys_module.unload()) { + META_ERROR("dll: Couldn't close plugin file '%s': %s", m_file, "invalid handle"); + m_status = PL_FAILED; + return false; + } + + free_table(m_gamedll_funcs.dllapi_table); + free_table(m_gamedll_funcs.newapi_table); + free_table(m_dllapi_table); + free_table(m_dllapi_post_table); + free_table(m_newapi_table); + free_table(m_newapi_post_table); + free_table(m_engine_table); + free_table(m_engine_post_table); + + m_status = PL_EMPTY; + m_action = PA_NULL; + m_info = nullptr; + m_time_loaded = 0; + m_dllapi_table = nullptr; + m_dllapi_post_table = nullptr; + m_newapi_table = nullptr; + m_newapi_post_table = nullptr; + m_engine_table = nullptr; + m_engine_post_table = nullptr; + m_gamedll_funcs.dllapi_table = nullptr; + m_gamedll_funcs.newapi_table = nullptr; + m_source_plugin_index = 0; + m_unloader_index = 0; + m_is_unloader = false; + + return true; +} + +// Unload a plugin from plugin request +bool MPlugin::plugin_unload(plid_t plid, PLUG_LOADTIME now, PL_UNLOAD_REASON reason) +{ + MPlugin *pl_unloader = g_plugins->find(plid); + + if (!pl_unloader) { + META_WARNING("dll: Not unloading plugin '%s'; plugin that requested unload is not found.", m_desc); + return false; + } + if (pl_unloader->index() == m_index) { + META_WARNING("dll: Not unloading plugin '%s'; Plugin tried to unload itself.", m_desc); + return false; + } + if (m_is_unloader) { + META_WARNING("dll: Not unloading plugin '%s'; Plugin is unloading plugin that tried to unload it.", m_desc); + return false; + } + + m_unloader_index = pl_unloader->index(); + + //block unloader from being unloaded by other plugin + pl_unloader->m_is_unloader = true; + + //try unload + PLUG_ACTION old_action = m_action; + m_action = PA_UNLOAD; + + if (unload(now, reason)) { + META_DEBUG(1, "Unloaded plugin '%s'", m_desc); + pl_unloader->m_is_unloader = false; + return true; + } + + pl_unloader->m_is_unloader = false; + m_action = old_action; + + return false; +} + // Query a plugin: // - dlopen() the file, store the handle // - dlsym() and call: @@ -629,11 +781,10 @@ bool MPlugin::query() auto pfn_give_engfuncs = (GIVE_ENGINE_FUNCTIONS_FN)m_sys_module.getsym("GiveFnptrsToDll"); if (!pfn_give_engfuncs) { - // META_ERROR("dll: Couldn't find GiveFnptrsToDll() in plugin '%s': %s", desc, DLERROR()); META_ERROR("dll: Failed query plugin '%s'; Couldn't find GiveFnptrsToDll(): %s", m_desc, "function not found"); - // caller will dlclose() return false; } + pfn_give_engfuncs(g_engine.pl_funcs, g_engine.globals); META_DEBUG(6, "dll: Plugin '%s': Called GiveFnptrsToDll()", m_desc); @@ -671,25 +822,27 @@ bool MPlugin::query() if (Q_strcmp(m_info->ifvers, META_INTERFACE_VERSION)) { - int mmajor = 0, mminor = 0, pmajor = 0, pminor = 0; + int mmajor, mminor, pmajor, pminor; META_DEBUG(3, "dll: Note: Plugin '%s' interface version didn't match; expected %s, found %s", m_desc, META_INTERFACE_VERSION, m_info->ifvers); - sscanf(META_INTERFACE_VERSION, "%d:%d", &mmajor, &mminor); - sscanf(m_info->ifvers, "%d:%d", &pmajor, &pminor); + sscanf(META_INTERFACE_VERSION, "%i:%i", &mmajor, &mminor); + sscanf(m_info->ifvers, "%i:%i", &pmajor, &pminor); // If plugin has later interface version, it's incompatible // (update metamod). if (pmajor > mmajor || (pmajor == mmajor && pminor > mminor)) { META_ERROR("dll: Plugin '%s' requires a newer version of Metamod (Metamod needs at least interface %s not the current %s)", m_desc, m_info->ifvers, META_INTERFACE_VERSION); + return false; } // If plugin has older major interface version, it's incompatible // (update plugin). - else if (pmajor < mmajor) + if (pmajor < mmajor) { META_ERROR("dll: Plugin '%s' is out of date and incompatible with this version of Metamod; please find a newer version of the plugin (plugin needs at least interface %s not the current %s)", m_desc, META_INTERFACE_VERSION, m_info->ifvers); + return false; } // Plugin has identical major, with older minor. This is supposed to be // backwards compatible, so we warn, but we still accept it. - else if (pmajor == mmajor && pminor < mminor) + if (pmajor == mmajor && pminor < mminor) META_LOG("dll: Note: plugin '%s' is using an older interface version (%s), not the latest interface version (%s); there might be an updated version of the plugin", m_desc, m_info->ifvers, META_INTERFACE_VERSION); else META_LOG("dll: Plugin '%s': unexpected version comparision; metavers=%s, mmajor=%d, mminor=%d; plugvers=%s, pmajor=%d, pminor=%d", m_desc, META_INTERFACE_VERSION, mmajor, mminor, m_info->ifvers, pmajor, pminor); @@ -781,7 +934,7 @@ bool MPlugin::attach(PLUG_LOADTIME now) { // Make copy of gameDLL's function tables for each plugin, so we don't // risk the plugins screwing with the tables everyone uses. - if (g_GameDLL.funcs.dllapi_table && !m_gamedll_funcs.dllapi_table) // TODO: check it + if (g_GameDLL.funcs.dllapi_table && !m_gamedll_funcs.dllapi_table) { m_gamedll_funcs.dllapi_table = (DLL_FUNCTIONS *)Q_malloc(sizeof(DLL_FUNCTIONS)); if (!m_gamedll_funcs.dllapi_table) @@ -793,19 +946,18 @@ bool MPlugin::attach(PLUG_LOADTIME now) } if (g_GameDLL.funcs.newapi_table && !m_gamedll_funcs.newapi_table) { - m_gamedll_funcs.newapi_table = (NEW_DLL_FUNCTIONS *)Q_calloc(1, sizeof(meta_new_dll_functions_t)); + m_gamedll_funcs.newapi_table = (NEW_DLL_FUNCTIONS *)Q_malloc(sizeof(NEW_DLL_FUNCTIONS)); if (!m_gamedll_funcs.newapi_table) { META_ERROR("dll: Failed attach plugin '%s': Failed malloc() for newapi_table"); return false; } - static_cast(m_gamedll_funcs.newapi_table)->set_from(g_GameDLL.funcs.newapi_table); + Q_memcpy(m_gamedll_funcs.newapi_table, g_GameDLL.funcs.newapi_table, sizeof(NEW_DLL_FUNCTIONS)); } auto pfn_attach = (META_ATTACH_FN)m_sys_module.getsym("Meta_Attach"); if (!pfn_attach) { META_ERROR("dll: Failed attach plugin '%s': Couldn't find Meta_Attach(): %s", m_desc, "function not found"); - // caller will dlclose() return false; } @@ -817,7 +969,6 @@ bool MPlugin::attach(PLUG_LOADTIME now) if (ret != TRUE) { META_ERROR("dll: Failed attach plugin '%s': Error from Meta_Attach(): %d", m_desc, ret); - // caller will dlclose() return false; } @@ -853,121 +1004,17 @@ bool MPlugin::attach(PLUG_LOADTIME now) return true; } -// Unload a plugin. Check time, detach. -bool MPlugin::unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason, PL_UNLOAD_REASON real_reason) -{ - if (!check_input()) - { - return false; - } - if (m_status < PL_RUNNING) - { - if (reason != PNL_CMD_FORCED && reason != PNL_RELOAD) - { - META_ERROR("dll: Not unloading plugin '%s'; already unloaded (status=%s)", m_desc, str_status()); - return false; - } - } - if (m_action != PA_UNLOAD && m_action != PA_RELOAD) - { - META_WARNING("dll: Not unloading plugin '%s'; not marked for unload (action=%s)", m_desc, str_action()); - return false; - } - - // Are we allowed to detach this plugin at this time? - // If forcing unload, we disregard when plugin wants to be unloaded. - if (m_info && m_info->unloadable < now) - { - if (reason == PNL_CMD_FORCED) - { - META_DEBUG(2, "dll: Forced unload plugin '%s' overriding allowed times: allowed=%s; now=%s", m_desc, str_unloadable(), str_loadtime(now, SL_SIMPLE)); - } - else - { - if (m_info->unloadable > PT_STARTUP) - { - META_DEBUG(2, "dll: Delaying unload plugin '%s'; can't detach now: allowed=%s; now=%s", m_desc, str_unloadable(), str_loadtime(now, SL_SIMPLE)); - // caller should give message to user - // try to unload again at next opportunity - return false; - } - - META_DEBUG(2, "dll: Failed unload plugin '%s'; can't detach now: allowed=%s; now=%s", m_desc, str_unloadable(), str_loadtime(now, SL_SIMPLE)); - // don't try to unload again later - m_action = PA_NONE; - return false; - } - } - - // If unloading during map, then I'd like to call plugin's - // ServerDeactivate. However, I don't want to do this until I start - // calling ServerActivate when loading during map, since the SDK - // indicates these two routines should match call for call. - - // detach plugin - if (!detach(now, reason)) - { - if (reason == PNL_RELOAD) - { - META_DEBUG(2, "dll: Reload plugin '%s' overriding failed detach", m_desc); - } - else if (reason == PNL_CMD_FORCED) - { - META_DEBUG(2, "dll: Forced unload plugin '%s' overriding failed detach"); - } - else - { - META_WARNING("dll: Failed to detach plugin '%s'; ", m_desc); - return false; - } - } - - g_plugins->clear_source_plugin_index(m_index); - - // successful detach, or forced unload - - // Unmark registered commands for this plugin (by index number). - g_regCmds->remove(m_index); - // Unmark registered cvars for this plugin (by index number). - g_regCvars->disable(m_index); - - // Close the file. Note: after this, attempts to reference any memory - // locations in the file will produce a segfault. - if (!m_sys_module.unload()) - { - // If DLL cannot be closed, OS is badly broken or we are giving invalid handle. - // So we don't return here but instead remove plugin from our listings. - META_WARNING("dll: Couldn't close plugin file '%s': %s", m_file, "invalid handle"); - } - - if (m_action == PA_UNLOAD) - { - m_status = PL_EMPTY; - clear(); - } - else if (m_action == PA_RELOAD) - { - m_status = PL_VALID; - m_action = PA_LOAD; - clear(); - } - - META_LOG("dll: Unloaded plugin '%s' for reason '%s'", m_desc, str_reason(reason, real_reason)); - return true; -} - // Inform plugin we're going to unload it. bool MPlugin::detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { - META_DETACH_FN pfn_detach; - // If we have no handle, i.e. no dll loaded, we return true because the // dll is obviously detached. We shouldn't call DLSYM() with a nullptr // handle since this will DLSYM() ourself. if (!m_sys_module.gethandle()) return true; - if (!(pfn_detach = (META_DETACH_FN)m_sys_module.getsym("Meta_Detach"))) + auto pfn_detach = (META_DETACH_FN)m_sys_module.getsym("Meta_Detach"); + if (!pfn_detach) { META_ERROR("dll: Error detach plugin '%s': Couldn't find Meta_Detach(): %s", m_desc, "function not found"); // caller will dlclose() @@ -985,157 +1032,6 @@ bool MPlugin::detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) return true; } -// Reload a plugin; unload and load again. -bool MPlugin::reload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) -{ - if (!check_input()) - { - return false; - } - // Are we allowed to load this plugin at this time? - // If we cannot load the plugin after unloading it, we keep it. - if (m_info && m_info->loadable < now) - { - if (m_info->loadable > PT_STARTUP) - { - META_DEBUG(2, "dll: Delaying reload plugin '%s'; would not be able to reattach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); - // caller should give message to user - // try to reload again at next opportunity - return false; - } - else - { - META_DEBUG(2, "dll: Failed reload plugin '%s'; would not be able to reattach now: allowed=%s; now=%s", m_desc, str_loadable(), str_loadtime(now, SL_SIMPLE)); - // don't try to reload again later - m_action = PA_NONE; - return false; - } - } - - if (m_status < PL_RUNNING) - { - META_WARNING("dll: Plugin '%s' isn't running; Forcing unload plugin for reloading", m_desc); - reason = PNL_RELOAD; - } - - if (!unload(now, reason, reason)) - { - META_WARNING("dll: Failed to unload plugin '%s' for reloading", m_desc); - return false; - } - - if (!load(now)) - { - META_WARNING("dll: Failed to reload plugin '%s' after unloading", m_desc); - return false; - } - - return true; -} - -// Pause a plugin; temporarily disabled for API routines. -bool MPlugin::pause() -{ - if (m_status == PL_PAUSED) - { - META_ERROR("Not pausing plugin '%s'; already paused", m_desc); - return false; - } - if (m_status != PL_RUNNING) - { - META_ERROR("Cannot pause plugin '%s'; not currently running (status=%s)", m_desc, str_status()); - return false; - } - - // are we allowed to pause this plugin? - if (m_info->unloadable < PT_ANYPAUSE) - { - META_ERROR("Cannot pause plugin '%s'; not allowed by plugin (allowed=%s)", m_desc, str_unloadable()); - m_action = PA_NONE; - return false; - } - - m_status = PL_PAUSED; - META_LOG("Paused plugin '%s'", m_desc); - return true; -} - -// Unpause a plugin. -bool MPlugin::unpause() -{ - if (m_status != PL_PAUSED) - { - META_ERROR("Cannot unpause plugin '%s'; not currently paused (status=%s)", m_desc, str_status()); - return false; - } - - m_status = PL_RUNNING; - META_LOG("Unpaused plugin '%s'", m_desc); - return true; -} - -// Retry pending action, presumably from a previous failure. -bool MPlugin::retry(PLUG_LOADTIME now, PL_UNLOAD_REASON reason) -{ - if (m_action == PA_LOAD) - return load(now); - if (m_action == PA_ATTACH) - return load(now); - if (m_action == PA_UNLOAD) - return unload(now, reason, reason); - if (m_action == PA_RELOAD) - return reload(now, reason); - - META_ERROR("No pending action to retry for plugin '%s'; (status=%s, action=%s)", m_desc, str_status(), str_action()); - return false; -} - -// Clear a plugin (it failed a previous action and should be -// removed from the list, or it's being unloaded). -bool MPlugin::clear() -{ - if (m_status != PL_FAILED && m_status != PL_BADFILE && m_status != PL_EMPTY && m_status != PL_OPENED) - { - META_ERROR("Cannot clear plugin '%s'; not marked as failed, empty, or open (status=%s)", m_desc, str_status()); - return false; - } - // If file is open, close the file. Note: after this, attempts to - // reference any memory locations in the file will produce a segfault. - if (!m_sys_module.unload()) - { - META_ERROR("dll: Couldn't close plugin file '%s': %s", m_file, "invalid handle"); - m_status = PL_FAILED; - return false; - } - - if (m_gamedll_funcs.dllapi_table) Q_free(m_gamedll_funcs.dllapi_table); - if (m_gamedll_funcs.newapi_table) Q_free(m_gamedll_funcs.newapi_table); - if (m_dllapi_table) Q_free(m_dllapi_table); - if (m_dllapi_post_table) Q_free(m_dllapi_post_table); - if (m_newapi_table) Q_free(m_newapi_table); - if (m_newapi_post_table) Q_free(m_newapi_post_table); - if (m_engine_table) Q_free(m_engine_table); - if (m_engine_post_table) Q_free(m_engine_post_table); - - m_status = PL_EMPTY; - m_action = PA_NULL; - m_info = nullptr; - m_time_loaded = 0; - m_dllapi_table = nullptr; - m_dllapi_post_table = nullptr; - m_newapi_table = nullptr; - m_newapi_post_table = nullptr; - m_engine_table = nullptr; - m_engine_post_table = nullptr; - m_gamedll_funcs.dllapi_table = nullptr; - m_gamedll_funcs.newapi_table = nullptr; - m_source_plugin_index = 0; - m_unloader_index = 0; - m_is_unloader = false; - - return true; -} - template void show_table(const char* table_name, table_t* table, info_t* info_begin, bool post) { @@ -1218,7 +1114,7 @@ bool MPlugin::newer_file() const return false; } - time_t file_time = st.st_ctime > st.st_mtime ? st.st_ctime : st.st_mtime; + time_t file_time = max(st.st_ctime, st.st_mtime); META_DEBUG(5, "newer_file? file=%s; load=%d, file=%d; ctime=%d, mtime=%d", m_file, m_time_loaded, file_time, st.st_ctime, st.st_mtime); if (file_time > m_time_loaded) return true; @@ -1231,32 +1127,16 @@ bool MPlugin::newer_file() const // SHOW is max 4 chars, for "show" output. const char *MPlugin::str_status(STR_STATUS fmt) const { - switch (m_status) - { - case PL_EMPTY: - if (fmt == ST_SHOW) return "empt"; - else return "empty"; - case PL_VALID: - if (fmt == ST_SHOW) return "info"; - else return "valid"; - case PL_BADFILE: - if (fmt == ST_SHOW) return "badf"; - else return "badfile"; - case PL_OPENED: - if (fmt == ST_SHOW) return "open"; - else return "opened"; - case PL_FAILED: - if (fmt == ST_SHOW) return "fail"; - else return "failed"; - case PL_RUNNING: - if (fmt == ST_SHOW) return "RUN"; - else return "running"; - case PL_PAUSED: - if (fmt == ST_SHOW) return "PAUS"; - else return "paused"; - default: - if (fmt == ST_SHOW) return UTIL_VarArgs("UNK%d", m_status); - return UTIL_VarArgs("unknown (%d)", m_status); + bool show = fmt == ST_SHOW; + switch (m_status) { + case PL_EMPTY: return show ? "empt" : "empty"; + case PL_VALID: return show ? "info" : "valid"; + case PL_BADFILE: return show ? "badf" : "badfile"; + case PL_OPENED: return show ? "open" : "opened"; + case PL_FAILED: return show ? "fail" : "failed"; + case PL_RUNNING: return show ? "RUN" : "running"; + case PL_PAUSED: return show ? "PAUS" : "paused"; + default: return UTIL_VarArgs(show ? "UNK%d" : "unknown (%d)", m_status); } } @@ -1265,32 +1145,48 @@ const char *MPlugin::str_status(STR_STATUS fmt) const // SHOW is max 4 chars, for "show" output. const char *MPlugin::str_action(STR_ACTION fmt) const { + bool show = fmt == ST_SHOW; switch (m_action) { - case PA_NULL: - if (fmt == SA_SHOW) return "nullptr"; - else return "nullptr"; - case PA_NONE: - if (fmt == SA_SHOW) return " - "; - else return "none"; - case PA_KEEP: - if (fmt == SA_SHOW) return "keep"; - else return "keep"; - case PA_LOAD: - if (fmt == SA_SHOW) return "load"; - else return "load"; - case PA_ATTACH: - if (fmt == SA_SHOW) return "atch"; - else return "attach"; - case PA_UNLOAD: - if (fmt == SA_SHOW) return "unld"; - else return "unload"; - case PA_RELOAD: - if (fmt == SA_SHOW) return "relo"; - else return "reload"; - default: - if (fmt == SA_SHOW) return UTIL_VarArgs("UNK%d", m_action); - else return UTIL_VarArgs("unknown (%d)", m_action); + case PA_NULL: return "null"; + case PA_NONE: return show ? " - " : "none"; + case PA_KEEP: return "keep"; + case PA_LOAD: return "load"; + case PA_ATTACH: return show ? "atch" : "attach"; + case PA_UNLOAD: return show ? "unld" : "unload"; + case PA_RELOAD: return show ? "relo" : "reload"; + default: return UTIL_VarArgs(show ? "UNK%d" : "unknown (%d)", m_action); + } +} + +// Return a string describing how the plugin was loaded. +const char *MPlugin::str_source(STR_SOURCE fmt) const +{ + bool show = fmt == SO_SHOW; + switch (m_source) { + case PS_INI: return show ? "ini" : "ini file"; + case PS_CMD: return show ? "cmd" : "console command"; + case PS_PLUGIN: + if (m_source_plugin_index <= 0) + return show ? "plUN" : "unloaded plugin"; + return show ? UTIL_VarArgs("pl%d", m_source_plugin_index) : UTIL_VarArgs("plugin [%s]", g_plugins->find(m_source_plugin_index)->description()); + default: return UTIL_VarArgs(show ? "UNK%d" : "unknown (%d)", m_source); + } +} + +// Return a string describing why a plugin is to be unloaded. +const char *MPlugin::str_reason(PL_UNLOAD_REASON reason) const +{ + switch (reason) { + case PNL_NULL: return "null"; + case PNL_INI_DELETED: return "deleted from ini file"; + case PNL_FILE_NEWER: return "file on disk is newer"; + case PNL_COMMAND: return "server command"; + case PNL_CMD_FORCED: return "forced by server command"; + case PNL_PLUGIN: return UTIL_VarArgs("%s (request from plugin[%d])", str_reason(PNL_NULL), m_unloader_index); + case PNL_PLG_FORCED: return UTIL_VarArgs("%s (forced request from plugin[%d])", str_reason(PNL_NULL), m_unloader_index); + case PNL_RELOAD: return "reloading"; + default: return UTIL_VarArgs("unknown (%d)", reason); } } @@ -1301,91 +1197,88 @@ const char *MPlugin::str_action(STR_ACTION fmt) const // NOW is to describe current situation of load/unload attempt. const char *MPlugin::str_loadtime(PLUG_LOADTIME ptime, STR_LOADTIME fmt) { - static const char *rPrintLoadTime[][26] = { - // SL_SIMPLE // SL_SHOW // SL_ALLOWED // SL_NOW - { "never", "Never", "never", "never" }, // PT_NEVER - { "startup", "Start", "at server startup", "during server startup" }, // PT_STARTUP - { "changelevel","Chlvl", "at changelevel", "during changelevel" }, // PT_CHANGELEVEL - { "anytime", "ANY", "at any time", "during map" }, // PT_ANYTIME - { "pausable", "Pause", "at any time, and pausable", "for requested pause" }, // PT_ANYPAUSE - }; - if (ptime >= PT_NEVER || ptime <= PT_ANYPAUSE) - return rPrintLoadTime[ptime][fmt]; + return s_rPrintLoadTime[ptime][fmt]; - if (fmt == SL_SHOW) - return UTIL_VarArgs("UNK-%d", ptime); - - return UTIL_VarArgs("unknown (%d)", ptime); + return UTIL_VarArgs((fmt == SL_SHOW) ? "UNK-%i" : "unknown (%i)", ptime); } -// Return a string describing why a plugin is to be unloaded. -const char *MPlugin::str_reason(PL_UNLOAD_REASON preason, PL_UNLOAD_REASON preal_reason) const +const char* MPlugin::str_loadable() const { - char buf[128]; - - if (preason == PNL_PLUGIN) - preason = PNL_NULL; - else if (preason == PNL_PLG_FORCED) - preason = PNL_NULL; - - switch (preal_reason) - { - case PNL_NULL: - return "null"; - case PNL_INI_DELETED: - return "deleted from ini file"; - case PNL_FILE_NEWER: - return "file on disk is newer"; - case PNL_COMMAND: - return "server command"; - case PNL_CMD_FORCED: - return "forced by server command"; - case PNL_PLUGIN: - { - Q_strncpy(buf, str_reason(PNL_NULL, preason), sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - - return UTIL_VarArgs("%s (request from plugin[%d])", buf, m_unloader_index); - } - case PNL_PLG_FORCED: - { - Q_strncpy(buf, str_reason(PNL_NULL, preason), sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - - return UTIL_VarArgs("%s (forced request from plugin[%d])", buf, m_unloader_index); - } - case PNL_RELOAD: - return "reloading"; - default: - return UTIL_VarArgs("unknown (%d)", preal_reason); - } + return m_info ? str_loadtime(m_info->loadable, SL_SIMPLE) : " -"; } -// Return a string describing how the plugin was loaded. -const char *MPlugin::str_source(STR_SOURCE fmt) const +const char* MPlugin::str_unloadable() const { - switch (m_source) - { - case PS_INI: - if (fmt == SO_SHOW) return "ini"; - else return "ini file"; - case PS_CMD: - if (fmt == SO_SHOW) return "cmd"; - else return "console command"; - case PS_PLUGIN: - if (m_source_plugin_index <= 0) - { - if (fmt == SO_SHOW) return "plUN"; - else return "unloaded plugin"; - } - else - { - if (fmt == SO_SHOW) return UTIL_VarArgs("pl%d", m_source_plugin_index); - else return UTIL_VarArgs("plugin [%s]", g_plugins->find(m_source_plugin_index)->m_desc); - } - default: - if (fmt == SO_SHOW) return UTIL_VarArgs("UNK%d", m_source); - else return UTIL_VarArgs("unknown (%d)", m_source); - } + return m_info ? str_loadtime(m_info->unloadable, SL_SIMPLE) : " -"; +} + +const char* MPlugin::str_loadable(STR_LOADTIME fmt) const +{ + return m_info ? str_loadtime(m_info->loadable, fmt) : " -"; +} + +const char* MPlugin::str_unloadable(STR_LOADTIME fmt) const +{ + return m_info ? str_loadtime(m_info->unloadable, fmt) : " -"; +} + +PLUG_STATUS MPlugin::status() const +{ + return m_status; +} + +PLUG_ACTION MPlugin::action() const +{ + return m_action; +} + +const char* MPlugin::description() const +{ + return m_desc; +} + +plugin_info_t* MPlugin::info() const +{ + return m_info; +} + +int MPlugin::index() const +{ + return m_index; +} + +int MPlugin::source_index() const +{ + return m_source_plugin_index; +} + +const char* MPlugin::file() const +{ + return m_file; +} + +const char* MPlugin::filename() const +{ + return m_filename; +} + +const char* MPlugin::pathname() const +{ + return m_pathname; +} + +const CSysModule& MPlugin::sys_module() const +{ + return m_sys_module; +} + +size_t MPlugin::status_ptr() +{ + return size_t(&m_status); +} + +void MPlugin::set_action(PLUG_ACTION action) +{ + m_action = action; } diff --git a/metamod/src/mplugin.h b/metamod/src/mplugin.h index 9573ae9..075090b 100644 --- a/metamod/src/mplugin.h +++ b/metamod/src/mplugin.h @@ -1,13 +1,12 @@ #pragma once -#include "api_info.h" -#include "support_meta.h" +#include "utils.h" // Flags to indicate current "load" state of plugin. // NOTE: order is important, as greater/less comparisons are made. enum PLUG_STATUS : uint8 { - PL_EMPTY = 0, // empty slot + PL_EMPTY, // empty slot PL_VALID, // has valid info in it PL_BADFILE, // nonexistent file (open failed), or not a valid plugin file (query failed) PL_OPENED, // dlopened and queried @@ -19,7 +18,7 @@ enum PLUG_STATUS : uint8 // Action to take for plugin at next opportunity. enum PLUG_ACTION : uint8 { - PA_NULL = 0, + PA_NULL, PA_NONE, // no action needed right now PA_KEEP, // keep, after ini refresh PA_LOAD, // load (dlopen, query) and try to attach @@ -31,7 +30,7 @@ enum PLUG_ACTION : uint8 // Flags to indicate from where the plugin was loaded. enum PLOAD_SOURCE : uint8 { - PS_INI = 0, // was loaded from the plugins.ini + PS_INI, // was loaded from the plugins.ini PS_CMD, // was loaded via a server command PS_PLUGIN, // was loaded by other plugin }; @@ -48,68 +47,50 @@ enum STR_LOADTIME : uint8 // Flags for how to format description of status. enum STR_STATUS : uint8 { - ST_SIMPLE = 0, // single word + ST_SIMPLE, // single word ST_SHOW, // for "show" output, 4 chars }; // Flags for how to format description of action. enum STR_ACTION : uint8 { - SA_SIMPLE = 0, // single word + SA_SIMPLE, // single word SA_SHOW, // for "show" output, 4 chars }; // Flags for how to format description of source. enum STR_SOURCE : uint8 { - SO_SIMPLE = 0, // two words + SO_SIMPLE, // two words SO_SHOW, // for "list" output, 3 chars }; +enum STR_PLATFORM : uint8 +{ + SP_WINDOWS, + SP_LINUX +}; + // An individual plugin. -class MPlugin { +class MPlugin +{ public: - PLUG_STATUS m_status; // current status of plugin (loaded, etc) - PLUG_ACTION m_action; // what to do with plugin (load, unload, etc) - PLOAD_SOURCE m_source; // source of the request to load the plugin - int m_index; // 1-based - plugin_info_t *m_info; // information plugin provides about itself - CSysModule m_sys_module; - time_t m_time_loaded; // when plugin was loaded - int m_source_plugin_index; // who loaded this plugin - int m_unloader_index; - bool m_is_unloader; // fix to prevent other plugins unload active unloader. + MPlugin(); + MPlugin(int index); - DLL_FUNCTIONS *m_dllapi_table; - DLL_FUNCTIONS *m_dllapi_post_table; - NEW_DLL_FUNCTIONS *m_newapi_table; - NEW_DLL_FUNCTIONS *m_newapi_post_table; - enginefuncs_t *m_engine_table; - enginefuncs_t *m_engine_post_table; - - gamedll_funcs_t m_gamedll_funcs; - mutil_funcs_t m_mutil_funcs; - - char m_filename[PATH_MAX]; // ie "dlls/mm_test_i386.so", from inifile - char *m_file; // ie "mm_test_i386.so", ptr from filename - char m_desc[MAX_DESC_LEN]; // ie "Test metamod plugin", from inifile - char m_pathname[PATH_MAX]; // UNIQUE, ie "/home/willday/half-life/cstrike/dlls/mm_test_i386.so", built with GameDLL.gamedir - int m_pfspecific; // level of specific platform affinity, used during load time - - bool ini_parseline(char *line); // parse line from inifile - bool cmd_parseline(const char *line); // parse from console command + bool ini_parseline(char *line); // parse line from .ini file + bool cmd_parseline(const char *line); // parse from console command bool plugin_parseline(const char *fname, int loader_index); // parse from plugin - bool check_input(); + bool check_input(); // check filename, path, status - bool resolve(); // find a matching file on disk - char *resolve_dirs(char *path) const; - char *resolve_prefix(char *path) const; - char *resolve_suffix(char *path) const; + bool resolve(); // find a matching file on disk + char *resolve_dirs(const char *path, char *tempbuf, size_t bufsize) const; // try resolve filename in different dirs + char *resolve_suffix(const char *path, char *tempbuf, size_t bufsize) const; // try resolve given filename with different suffixes - bool platform_match(MPlugin* plugin) const; + bool platform_match(MPlugin* plugin) const; // check if a given plugin is the same but possibly for a different platform - bool load(PLUG_LOADTIME now); - bool unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason, PL_UNLOAD_REASON real_reason); + bool load(PLUG_LOADTIME now); // load parsed plugin + bool unload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); bool reload(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); bool pause(); bool unpause(); @@ -120,36 +101,66 @@ public: bool newer_file() const; // check for newer file on disk - const char *str_status(STR_STATUS fmt) const; - const char *str_action(STR_ACTION fmt) const; - const char *str_source(STR_SOURCE fmt) const; - - const char *str_reason(PL_UNLOAD_REASON preason, PL_UNLOAD_REASON preal_reason) const; + const char *str_status(STR_STATUS fmt = ST_SIMPLE) const; + const char *str_action(STR_ACTION fmt = SA_SIMPLE) const; + const char *str_source(STR_SOURCE fmt = SO_SIMPLE) const; + const char *str_reason(PL_UNLOAD_REASON reason) const; static const char *str_loadtime(PLUG_LOADTIME pallow, STR_LOADTIME fmt); - const char *str_status() { return str_status(ST_SIMPLE); }; - const char *str_action() { return str_action(SA_SIMPLE); }; - const char *str_source() { return str_source(SO_SIMPLE); }; + const char* str_loadable() const; + const char* str_unloadable() const; + const char* str_loadable(STR_LOADTIME fmt) const; + const char* str_unloadable(STR_LOADTIME fmt) const; - const char *str_loadable() { - if (m_info) return str_loadtime(m_info->loadable, SL_SIMPLE); - else return " -"; - }; - const char *str_unloadable() { - if (m_info) return str_loadtime(m_info->unloadable, SL_SIMPLE); - else return " -"; - }; - const char *str_loadable(STR_LOADTIME fmt) { - if (m_info) return str_loadtime(m_info->loadable, fmt); - else return " -"; - }; - const char *str_unloadable(STR_LOADTIME fmt) { - if (m_info) return str_loadtime(m_info->unloadable, fmt); - else return " -"; - }; + PLUG_STATUS status() const; + PLUG_ACTION action() const; + const char* description() const; + plugin_info_t* info() const; + int index() const; + int source_index() const; + const char* file() const; + const char* filename() const; + const char* pathname() const; + const CSysModule& sys_module() const; + size_t status_ptr(); + void set_action(PLUG_ACTION action); private: - bool query(); - bool attach(PLUG_LOADTIME now); - bool detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); + bool query(); // check exports, call init, getfnptrs and query + bool attach(PLUG_LOADTIME now); // call attach + bool detach(PLUG_LOADTIME now, PL_UNLOAD_REASON reason); // call detach + + PLUG_STATUS m_status; // current status of plugin (loaded, etc) + PLUG_ACTION m_action; // what to do with plugin (load, unload, etc) + PLOAD_SOURCE m_source; // source of the request to load the plugin + STR_PLATFORM m_platform; // plugin platform + int m_index; // 1-based + plugin_info_t *m_info; // information plugin provides about itself + CSysModule m_sys_module; // system module + time_t m_time_loaded; // when plugin was loaded + int m_source_plugin_index; // who loaded this plugin + int m_unloader_index; // index of unloader plugin + bool m_is_unloader; // fix to prevent other plugins unload active unloader. + +public: + // pointers to tables inside plugins + DLL_FUNCTIONS *m_dllapi_table; + DLL_FUNCTIONS *m_dllapi_post_table; + NEW_DLL_FUNCTIONS *m_newapi_table; + NEW_DLL_FUNCTIONS *m_newapi_post_table; + enginefuncs_t *m_engine_table; + enginefuncs_t *m_engine_post_table; + +private: + gamedll_funcs_t m_gamedll_funcs; + mutil_funcs_t m_mutil_funcs; + + char m_filename[PATH_MAX]; // ie "dlls/mm_test_i386.so", from inifile + char *m_file; // ie "mm_test_i386.so", ptr from filename + char m_desc[MAX_DESC_LEN]; // ie "Test metamod plugin", from inifile + char m_pathname[PATH_MAX]; // UNIQUE, ie "/home/willday/half-life/cstrike/dlls/mm_test_i386.so", built with GameDLL.gamedir + + static const char *s_rPrintLoadTime[][4]; + + friend class MPluginList; }; diff --git a/metamod/src/mreg.cpp b/metamod/src/mreg.cpp index 4a9f423..db53082 100644 --- a/metamod/src/mreg.cpp +++ b/metamod/src/mreg.cpp @@ -1,6 +1,6 @@ #include "precompiled.h" -MRegCmd::MRegCmd(char* cmd_name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin) : m_pfunction(cmd_handler), m_plugid(cmd_plugin->m_index), m_status(RG_VALID) +MRegCmd::MRegCmd(char* cmd_name, REG_CMD_FN cmd_handler, MPlugin* cmd_plugin) : m_pfunction(cmd_handler), m_plugid(cmd_plugin->index()), m_status(RG_VALID) { m_name = Q_strdup(cmd_name); } @@ -112,7 +112,7 @@ void MRegCmdList::show() const { auto iplug = g_plugins->find(reg->m_plugid); - Q_strncpy(bplug, iplug ? iplug->m_desc : "(unknown)", sizeof bplug - 1); + Q_strncpy(bplug, iplug ? iplug->description() : "(unknown)", sizeof bplug - 1); bplug[sizeof bplug - 1] = '\0'; } else @@ -149,7 +149,7 @@ void MRegCmdList::show(int plugin_id) const META_CONS("%d commands", total_count); } -MRegCvar::MRegCvar(cvar_t* cv_ptr, MPlugin* cv_plugin) : m_cvar(cv_ptr), m_plugid(cv_plugin ? cv_plugin->m_index : 0), m_status(RG_VALID) +MRegCvar::MRegCvar(cvar_t* cv_ptr, MPlugin* cv_plugin) : m_cvar(cv_ptr), m_plugid(cv_plugin ? cv_plugin->index() : 0), m_status(RG_VALID) { m_cvar = new cvar_t; m_cvar->name = Q_strdup(cv_ptr->name); @@ -228,7 +228,7 @@ void MRegCvarList::show() const if (reg->m_status == RG_VALID) { auto plug = g_plugins->find(reg->m_plugid); - Q_strncpy(bplug, plug ? plug->m_desc : "(unknown)", sizeof bplug - 1); + Q_strncpy(bplug, plug ? plug->description() : "(unknown)", sizeof bplug - 1); bplug[sizeof bplug - 1] = '\0'; } else diff --git a/metamod/src/mutil.cpp b/metamod/src/mutil.cpp index 5e2851a..3284afb 100644 --- a/metamod/src/mutil.cpp +++ b/metamod/src/mutil.cpp @@ -252,7 +252,7 @@ const char* EXT_FUNC mutil_GetPluginPath(plid_t plid) return nullptr; } - Q_strncpy(buf, plug->m_pathname, sizeof buf - 1); + Q_strncpy(buf, plug->pathname(), sizeof buf - 1); buf[sizeof buf - 1] = '\0'; return buf; } @@ -301,7 +301,6 @@ int EXT_FUNC mutil_LoadMetaPlugin(plid_t plid, const char* fname, PLUG_LOADTIME } auto pl_loaded = g_plugins->plugin_addload(plid, fname, now); - if (!pl_loaded) { if (plugin_handle) @@ -309,13 +308,13 @@ int EXT_FUNC mutil_LoadMetaPlugin(plid_t plid, const char* fname, PLUG_LOADTIME return 1; // TODO: WTF } - else - { - if (plugin_handle) - *plugin_handle = (void *)pl_loaded->m_sys_module.gethandle(); - return 0; - } + meta_rebuild_callbacks(); + + if (plugin_handle) + *plugin_handle = (void *)pl_loaded->sys_module().gethandle(); + + return 0; } int EXT_FUNC mutil_UnloadMetaPlugin(plid_t plid, const char *fname, PLUG_LOADTIME now, PL_UNLOAD_REASON reason) @@ -339,8 +338,10 @@ int EXT_FUNC mutil_UnloadMetaPlugin(plid_t plid, const char *fname, PLUG_LOADTIM if (!findp || !unique) return 1; - if (findp->plugin_unload(plid, now, reason)) + if (findp->plugin_unload(plid, now, reason)) { + meta_rebuild_callbacks(); return 0; + } return 1; } @@ -357,8 +358,10 @@ int EXT_FUNC mutil_UnloadMetaPluginByHandle(plid_t plid, void *plugin_handle, PL if (!findp) return 1; - if (findp->plugin_unload(plid, now, reason)) + if (findp->plugin_unload(plid, now, reason)) { + meta_rebuild_callbacks(); return 0; + } return 1; } diff --git a/metamod/src/osdep.cpp b/metamod/src/osdep.cpp index e501070..d825d8f 100644 --- a/metamod/src/osdep.cpp +++ b/metamod/src/osdep.cpp @@ -7,12 +7,14 @@ CSysModule::CSysModule() : m_handle(0), m_base(0), m_size(0) #ifdef _WIN32 module_handle_t CSysModule::load(const char* filepath) { - m_handle = LoadLibrary(filepath); + if (!m_handle) { + m_handle = LoadLibrary(filepath); - MODULEINFO module_info; - if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof module_info)) { - m_base = (uintptr_t)module_info.lpBaseOfDll; - m_size = module_info.SizeOfImage; + MODULEINFO module_info; + if (GetModuleInformation(GetCurrentProcess(), m_handle, &module_info, sizeof module_info)) { + m_base = (uintptr_t)module_info.lpBaseOfDll; + m_size = module_info.SizeOfImage; + } } return m_handle; @@ -20,7 +22,7 @@ module_handle_t CSysModule::load(const char* filepath) bool CSysModule::unload() { - bool ret = false; + bool ret = true; if (m_handle) { ret = FreeLibrary(m_handle) != ERROR; @@ -34,7 +36,7 @@ bool CSysModule::unload() void* CSysModule::getsym(const char* name) const { - return GetProcAddress(m_handle, name); + return m_handle ? GetProcAddress(m_handle, name) : nullptr; } #else static ElfW(Addr) dlsize(void* base) @@ -59,35 +61,38 @@ static ElfW(Addr) dlsize(void* base) module_handle_t CSysModule::load(const char* filepath) { - m_handle = dlopen(filepath, RTLD_NOW); + if (!m_handle) { + m_handle = dlopen(filepath, RTLD_NOW); - char buf[1024], dummy[1024], path[260]; - sprintf(buf, "/proc/%i/maps", getpid()); + char buf[1024], dummy[1024], path[260]; + sprintf(buf, "/proc/%i/maps", getpid()); - FILE* fp = fopen(buf, "r"); + FILE* fp = fopen(buf, "r"); - while (fgets(buf, sizeof buf, fp)) { - uintptr_t start, end; + while (fgets(buf, sizeof buf, fp)) { + uintptr_t start, end; - int args = sscanf(buf, "%x-%x %128s %128s %128s %128s %255s", &start, &end, dummy, dummy, dummy, dummy, path); - if (args != 7) { - continue; + int args = sscanf(buf, "%x-%x %128s %128s %128s %128s %255s", &start, &end, dummy, dummy, dummy, dummy, path); + if (args != 7) { + continue; + } + + if (!Q_stricmp(path, filepath)) { + m_base = start; + m_size = end - start; + break; + } } - if (!Q_stricmp(path, filepath)) { - m_base = start; - m_size = end - start; - break; - } + fclose(fp); } - fclose(fp); return m_handle; } bool CSysModule::unload() { - bool ret = false; + bool ret = true; if (m_handle) { ret = dlclose(m_handle) != 0; @@ -101,7 +106,7 @@ bool CSysModule::unload() void* CSysModule::getsym(const char* name) const { - return dlsym(m_handle, name); + return m_handle ? dlsym(m_handle, name) : nullptr; } #endif @@ -130,7 +135,7 @@ const char* CSysModule::getloaderror() // http://msdn.microsoft.com/library/en-us/debug/errors_0sdh.asp // except without FORMAT_MESSAGE_ALLOCATE_BUFFER, since we use a local // static buffer. -const char *str_GetLastError() +static const char *str_GetLastError() { static char buf[MAX_STRBUF_LEN]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&buf, MAX_STRBUF_LEN - 1, nullptr); diff --git a/metamod/src/plinfo.h b/metamod/src/plinfo.h index 9d40002..876429e 100644 --- a/metamod/src/plinfo.h +++ b/metamod/src/plinfo.h @@ -4,7 +4,7 @@ // NOTE: order is crucial, as greater/less comparisons are made. enum PLUG_LOADTIME { - PT_NEVER = 0, + PT_NEVER, PT_STARTUP, // should only be loaded/unloaded at initial hlds execution PT_CHANGELEVEL, // can be loaded/unloaded between maps PT_ANYTIME, // can be loaded/unloaded at any time @@ -14,7 +14,7 @@ enum PLUG_LOADTIME // Flags to indicate why the plugin is being unloaded. enum PL_UNLOAD_REASON { - PNL_NULL = 0, + PNL_NULL, PNL_INI_DELETED, // was deleted from plugins.ini PNL_FILE_NEWER, // file on disk is newer than last load PNL_COMMAND, // requested by server/console command diff --git a/metamod/src/precompiled.h b/metamod/src/precompiled.h index 4282e40..45a2602 100644 --- a/metamod/src/precompiled.h +++ b/metamod/src/precompiled.h @@ -31,11 +31,9 @@ #include "metamod.h" #include "log_meta.h" #include "conf_meta.h" -#include "support_meta.h" #include "dllapi.h" #include "engine_api.h" #include "game_support.h" -#include "meta_eiface.h" #include "mreg.h" #include "meta_api.h" #include "mutil.h" diff --git a/metamod/src/reg_support.cpp b/metamod/src/reg_support.cpp index 60ec7f2..bef7b25 100644 --- a/metamod/src/reg_support.cpp +++ b/metamod/src/reg_support.cpp @@ -30,7 +30,7 @@ void EXT_FUNC meta_AddServerCommand(char *cmd_name, void (*function)()) { MPlugin *plug = g_plugins->find_memloc(function); - META_DEBUG(4, "called: meta_AddServerCommand; cmd_name=%s, function=%d, plugin=%s", cmd_name, function, plug ? plug->m_file : "unknown"); + META_DEBUG(4, "called: meta_AddServerCommand; cmd_name=%s, function=%d, plugin=%s", cmd_name, function, plug ? plug->file() : "unknown"); if (!plug) { META_ERROR("Failed to find memloc for regcmd '%s'", cmd_name); diff --git a/metamod/src/sdk_util.cpp b/metamod/src/sdk_util.cpp index 245e9be..5a979c9 100644 --- a/metamod/src/sdk_util.cpp +++ b/metamod/src/sdk_util.cpp @@ -1,6 +1,6 @@ #include "precompiled.h" -char *UTIL_VarArgs(char *format, ...) +char *UTIL_VarArgs(const char *format, ...) { va_list argptr; static char string[1024]; diff --git a/metamod/src/sdk_util.h b/metamod/src/sdk_util.h index 0584c41..5141fe2 100644 --- a/metamod/src/sdk_util.h +++ b/metamod/src/sdk_util.h @@ -13,4 +13,5 @@ #include "enginecallbacks.h" #include +char *UTIL_VarArgs(const char *format, ...); void UTIL_HudMessage(edict_t *pEntity, const hudtextparms_t &textparms, const char *pMessage); diff --git a/metamod/src/support_meta.cpp b/metamod/src/support_meta.cpp deleted file mode 100644 index e88d68b..0000000 --- a/metamod/src/support_meta.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "precompiled.h" - -void __declspec(noreturn) do_exit(int exitval) -{ - //Allahu Akbar!! - *((int *)nullptr) = 0; -} - -// Checks for a non-empty file, relative to the gamedir if necessary. -// Formerly used LOAD_FILE_FOR_ME, which provided a simple way to check for -// a file under the gamedir, but which would _also_ look in the sibling -// "valve" directory, thus sometimes finding files that weren't desired. -// Also, formerly named just "valid_file". -// -// Special-case-recognize "/dev/null" as a valid file. -bool FileExistsInGameDir(const char *path) -{ - struct stat st; - char buf[PATH_MAX]; - - if (!path) - return false; - - if (!Q_strcmp(path, "/dev/null")) - return true; - - if (IsAbsolutePath(path)) - { - Q_strncpy(buf, path, sizeof buf); - buf[sizeof buf - 1] = '\0'; - } - else - snprintf(buf, sizeof buf, "%s/%s", g_GameDLL.gamedir, path); - - int ret = stat(buf, &st); - if (ret != 0) - { - META_DEBUG(5, "Unable to stat '%s': %s", buf, strerror(errno)); - return false; - } - - int reg = S_ISREG(st.st_mode); - if (!reg) - { - META_DEBUG(5, "Not a regular file: %s", buf); - return false; - } - - if (!st.st_size) - { - META_DEBUG(5, "Empty file: %s", buf); - return false; - } - - if (ret == 0 && reg) - return true; - - return false; -} - -// Turns path into a full path: -// - if not absolute, prepends gamedir -// - calls realpath() to collapse ".." and such -// - calls NormalizePath() to fix backslashes, etc -// -// Much like realpath, buffer pointed to by fullpath is assumed to be -// able to store a string of PATH_MAX length. -char* full_gamedir_path(const char* path, char* fullpath) -{ - char buf[PATH_MAX]; - - // Build pathname from filename, plus gamedir if relative path. - if (IsAbsolutePath(path)) - { - Q_strncpy(buf, path, sizeof buf - 1); - buf[sizeof buf - 1] = '\0'; - } - else snprintf(buf, sizeof buf, "%s/%s", g_GameDLL.gamedir, path); - - // Remove relative path components, if possible. - if (!realpath(buf, fullpath)) - { - META_DEBUG(4, "Unable to get realpath for '%s': %s", buf, str_os_error()); - Q_strncpy(fullpath, path, sizeof fullpath - 1); - fullpath[sizeof fullpath - 1] = '\0'; - } - - // Replace backslashes, etc. - NormalizePath(fullpath); - return fullpath; -} diff --git a/metamod/src/support_meta.h b/metamod/src/support_meta.h deleted file mode 100644 index 8242794..0000000 --- a/metamod/src/support_meta.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include // stat -#include // stat - -#include "osdep.h" // _stricmp, S_ISREG, - -void __declspec(noreturn) do_exit(int exitval); - -bool FileExistsInGameDir(const char *path); -char *full_gamedir_path(const char *path, char *fullpath); - -// Turn a variable/function name into the corresponding string, optionally -// stripping off the leading "len" characters. Useful for things like -// turning 'pfnClientCommand' into "ClientCommand" so we don't have to -// specify strings used for all the debugging/log messages. -#define STRINGIZE(name, len) #name+len - -// Max description length for plugins.ini and other places. -#define MAX_DESC_LEN 256 - -// For various character string buffers. -#define MAX_STRBUF_LEN 1024 diff --git a/metamod/src/utils.cpp b/metamod/src/utils.cpp index 7044ad4..c537a58 100644 --- a/metamod/src/utils.cpp +++ b/metamod/src/utils.cpp @@ -1,5 +1,13 @@ #include "precompiled.h" +const char* g_platform_postfixes[4] = +{ + "_i386.so", + "_i486.so", + "_i586.so", + "_i686.so", +}; + bool is_yes(const char* str) { return !Q_strcmp(str, "true") || !Q_strcmp(str, "yes") || !Q_strcmp(str, "1"); @@ -119,7 +127,7 @@ char *trimbuf(char *str) return str; } -void NormalizePath(char *path) +void normalize_path(char *path) { #ifdef _WIN32 for (char* cp = path; *cp; cp++) @@ -133,7 +141,7 @@ void NormalizePath(char *path) #endif } -bool IsAbsolutePath(const char *path) +bool is_abs_path(const char *path) { if (path[0] == '/') return true; #ifdef _WIN32 @@ -143,6 +151,23 @@ bool IsAbsolutePath(const char *path) return false; } +bool is_valid_path(const char *path) +{ + struct stat st; + return !stat(path, &st) && S_ISREG(st.st_mode); +} + +bool is_platform_postfix(const char* pf) +{ + if (pf) { + for (size_t i = 0; i < arraysize(g_platform_postfixes); i++) { + if (!Q_strcmp(pf, g_platform_postfixes[i])) + return true; + } + } + return false; +} + #ifdef _WIN32 char *realpath(const char *file_name, char *resolved_name) { @@ -162,10 +187,94 @@ char *realpath(const char *file_name, char *resolved_name) } FindClose(handle); - NormalizePath(resolved_name); + normalize_path(resolved_name); return resolved_name; } return nullptr; } #endif // _WIN32 + +void __declspec(noreturn) do_exit(int exitval) +{ + //Allahu Akbar!! + *((int *)nullptr) = 0; +} + +// Checks for a non-empty file, relative to the gamedir if necessary. +// Formerly used LOAD_FILE_FOR_ME, which provided a simple way to check for +// a file under the gamedir, but which would _also_ look in the sibling +// "valve" directory, thus sometimes finding files that weren't desired. +// Also, formerly named just "valid_file". +// +// Special-case-recognize "/dev/null" as a valid file. +bool is_file_exists_in_gamedir(const char *path) +{ + char buf[PATH_MAX]; + + if (!path) + return false; + + if (!Q_strcmp(path, "/dev/null")) + return true; + + if (is_abs_path(path)) { + Q_strncpy(buf, path, sizeof buf); + buf[sizeof buf - 1] = '\0'; + } + else + snprintf(buf, sizeof buf, "%s/%s", g_GameDLL.gamedir, path); + + struct stat st; + int ret = stat(buf, &st); + if (ret != 0) { + META_DEBUG(5, "Unable to stat '%s': %s", buf, strerror(errno)); + return false; + } + + int reg = S_ISREG(st.st_mode); + if (!reg) { + META_DEBUG(5, "Not a regular file: %s", buf); + return false; + } + + if (!st.st_size) { + META_DEBUG(5, "Empty file: %s", buf); + return false; + } + + if (ret == 0 && reg) + return true; + + return false; +} + +// Turns path into a full path: +// - if not absolute, prepends gamedir +// - calls realpath() to collapse ".." and such +// - calls NormalizePath() to fix backslashes, etc +// +// Much like realpath, buffer pointed to by fullpath is assumed to be +// able to store a string of PATH_MAX length. +char* full_gamedir_path(const char* path, char* fullpath) +{ + char buf[PATH_MAX]; + + // Build pathname from filename, plus gamedir if relative path. + if (is_abs_path(path)) { + Q_strncpy(buf, path, sizeof buf - 1); + buf[sizeof buf - 1] = '\0'; + } + else snprintf(buf, sizeof buf, "%s/%s", g_GameDLL.gamedir, path); + + // Remove relative path components, if possible. + if (!realpath(buf, fullpath)) { + META_DEBUG(4, "Unable to get realpath for '%s': %s", buf, str_os_error()); + Q_strncpy(fullpath, path, sizeof fullpath - 1); + fullpath[sizeof fullpath - 1] = '\0'; + } + + // Replace backslashes, etc. + normalize_path(fullpath); + return fullpath; +} diff --git a/metamod/src/utils.h b/metamod/src/utils.h index 2913d07..049e71b 100644 --- a/metamod/src/utils.h +++ b/metamod/src/utils.h @@ -1,5 +1,17 @@ #pragma once +// Turn a variable/function name into the corresponding string, optionally +// stripping off the leading "len" characters. Useful for things like +// turning 'pfnClientCommand' into "ClientCommand" so we don't have to +// specify strings used for all the debugging/log messages. +#define STRINGIZE(name, len) #name+len + +// Max description length for plugins.ini and other places. +#define MAX_DESC_LEN 256 + +// For various character string buffers. +#define MAX_STRBUF_LEN 1024 + template char(&ArraySizeHelper(T(&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) @@ -54,5 +66,14 @@ char *realpath(const char *file_name, char *resolved_name); #endif // _WIN32 char* trimbuf(char *str); -void NormalizePath(char *path); -bool IsAbsolutePath(const char *path); +void normalize_path(char *path); +bool is_abs_path(const char *path); +bool is_valid_path(const char *path); +bool is_platform_postfix(const char *pf); + +void __declspec(noreturn) do_exit(int exitval); + +bool is_file_exists_in_gamedir(const char *path); +char *full_gamedir_path(const char *path, char *fullpath); + +extern const char* g_platform_postfixes[4];