diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index ec1403f9..b87b34eb 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -10,6 +10,7 @@ #include "ExtensionSys.h" SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); +SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false); SourcePawnEngine g_SourcePawn; SourceModBase g_SourceMod; @@ -39,6 +40,7 @@ void ShutdownJIT() SourceModBase::SourceModBase() { m_IsMapLoading = false; + m_ExecPluginReload = false; } bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late) @@ -131,6 +133,7 @@ void SourceModBase::StartSourceMod(bool late) { /* First initialize the global hooks we need */ SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); + SH_ADD_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false); /* Notify! */ SMGlobalClass *pBase = SMGlobalClass::head; @@ -158,6 +161,7 @@ void SourceModBase::StartSourceMod(bool late) bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) { m_IsMapLoading = true; + m_ExecPluginReload = true; g_Logger.MapChange(pMapName); @@ -168,6 +172,15 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch RETURN_META_VALUE(MRES_IGNORED, true); } +void SourceModBase::LevelShutdown() +{ + if (m_ExecPluginReload) + { + g_PluginSys.ReloadOrUnloadPlugins(); + m_ExecPluginReload = false; + } +} + bool SourceModBase::IsMapLoading() { return m_IsMapLoading; @@ -241,6 +254,9 @@ void SourceModBase::CloseSourceMod() pBase = pBase->m_pGlobalClassNext; } + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelInit, gamedll, this, &SourceModBase::LevelInit, false); + SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, LevelShutdown, gamedll, this, &SourceModBase::LevelShutdown, false); + /* Rest In Peace */ ShutdownJIT(); } diff --git a/core/sourcemod.h b/core/sourcemod.h index bd74af48..b2c9c5d0 100644 --- a/core/sourcemod.h +++ b/core/sourcemod.h @@ -33,6 +33,11 @@ public: */ bool LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background); + /** + * @brief Level shutdown hook + */ + void LevelShutdown(); + /** * @brief Returns whether or not a mapload is in progress */ @@ -52,6 +57,7 @@ private: private: char m_SMBaseDir[PLATFORM_MAX_PATH+1]; bool m_IsMapLoading; + bool m_ExecPluginReload; }; extern SourceModBase g_SourceMod; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index fd82b260..cdfff088 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -449,6 +449,35 @@ bool CPlugin::IsRunnable() const return (m_status <= Plugin_Paused) ? true : false; } +time_t CPlugin::GetFileTimeStamp() +{ + char path[PLATFORM_MAX_PATH+1]; + g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename); +#ifdef PLATFORM_WINDOWS + struct _stat s; + if (_stat(path, &s) != 0) +#else if defined PLATFORM_POSIX + struct stat s; + if (stat(path, &s) != 0) +#endif + { + g_Logger.LogError("Could not obtain plugin time stamp, error: %d", errno); + return 0; + } else { + return s.st_mtime; + } +} + +time_t CPlugin::GetTimeStamp() const +{ + return m_LastAccess; +} + +void CPlugin::SetTimeStamp(time_t t) +{ + m_LastAccess = t; +} + /******************* * PLUGIN ITERATOR * *******************/ @@ -681,6 +710,10 @@ bool CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug } } + /* Save the time stamp */ + time_t t = pPlugin->GetFileTimeStamp(); + pPlugin->SetTimeStamp(t); + if (_plugin) { *_plugin = pPlugin; @@ -1522,3 +1555,31 @@ void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argc g_RootMenu.DrawGenericOption("info", "Information about a plugin"); g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin"); } + +void CPluginManager::ReloadOrUnloadPlugins() +{ + List::iterator iter; + List tmp_list = m_plugins; + CPlugin *pl; + time_t t; + + for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++) + { + pl = (*iter); + if (pl->GetType() == PluginType_MapOnly) + { + UnloadPlugin((IPlugin *)pl); + } else if (pl->GetType() == PluginType_MapUpdated) { + t=pl->GetFileTimeStamp(); + if (!t) + { + continue; + } + if (t > pl->GetTimeStamp()) + { + pl->SetTimeStamp(t); + UnloadPlugin((IPlugin *)pl); + } + } + } +} diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index fa4930eb..721b94ba 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -2,6 +2,8 @@ #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ #include +#include +#include #include #include #include @@ -155,7 +157,15 @@ public: */ bool IsRunnable() const; public: - time_t HasUpdatedFile(); + /** + * Returns the modification time during last plugin load. + */ + time_t GetTimeStamp() const; + + /** + * Returns the current modification time of the plugin file. + */ + time_t GetFileTimeStamp(); /** * Returns true if the plugin was running, but is now invalid. @@ -163,6 +173,7 @@ public: bool WasRunning(); protected: void UpdateInfo(); + void SetTimeStamp(time_t t); private: ContextPair m_ctx; PluginType m_type; @@ -272,6 +283,11 @@ public: */ const char *GetStatusText(PluginStatus status); + /** + * Reload or update plugins on level shutdown. + */ + void ReloadOrUnloadPlugins(); + private: bool _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t err_max); diff --git a/public/IHandleSys.h b/public/IHandleSys.h index 6f105f1c..ea5b6958 100644 --- a/public/IHandleSys.h +++ b/public/IHandleSys.h @@ -17,7 +17,6 @@ namespace SourceMod typedef unsigned int HandleType_t; typedef unsigned int Handle_t; - class SourcePawn::IPluginContext; /** * About type checking: