diff --git a/core/interfaces/IPluginSys.h b/core/interfaces/IPluginSys.h index dca8e2db..3f728536 100644 --- a/core/interfaces/IPluginSys.h +++ b/core/interfaces/IPluginSys.h @@ -36,9 +36,10 @@ namespace SourceMod Plugin_Running=0, /* Plugin is running */ /* All states below are unexecutable */ Plugin_Paused, /* Plugin is loaded but paused */ + Plugin_Error, /* Plugin is loaded but errored/locked */ /* All states below do not have all natives */ Plugin_Loaded, /* Plugin has passed loading and can be finalized */ - Plugin_Error, /* Plugin has a blocking error */ + Plugin_Failed, /* Plugin has a fatal failure */ Plugin_Created, /* Plugin is created but not initialized */ Plugin_Uncompiled, /* Plugin is not yet compiled by the JIT */ Plugin_BadLoad, /* Plugin failed to load */ diff --git a/core/sm_srvcmds.cpp b/core/sm_srvcmds.cpp index ae3a54c5..3c7d756a 100644 --- a/core/sm_srvcmds.cpp +++ b/core/sm_srvcmds.cpp @@ -192,6 +192,16 @@ CON_COMMAND(sm, "SourceMod Menu") META_CONPRINT(" unload - Unload a plugin\n"); META_CONPRINT(" info - Information about a plugin\n"); return; + } else if (!strcmp("credits", cmd)) { + META_CONPRINTF(" SourceMod was developed by AlliedModders, LLC.\n"); + META_CONPRINTF(" Development would not have been possible without the following people:\n"); + META_CONPRINTF(" David \"BAILOPAN\" Anderson, lead developer\n"); + META_CONPRINTF(" Borja \"faluco\" Ferrer, Core developer\n"); + META_CONPRINTF(" Scott \"Damaged Soul\" Ehlert, SourceMM developer\n"); + META_CONPRINTF(" Pavol \"PM OnoTo\" Marko, SourceHook developer\n"); + META_CONPRINTF(" Special thanks to Viper of GameConnect, and Mani\n"); + META_CONPRINTF(" http://www.sourcemod.net/\n"); + return; } else if (!strcmp("version", cmd)) { META_CONPRINT(" SourceMod Version Information:\n"); META_CONPRINTF(" SourceMod Version: \"%s\"\n", SOURCEMOD_VERSION); @@ -203,6 +213,7 @@ CON_COMMAND(sm, "SourceMod Menu") } META_CONPRINT(" SourceMod Menu:\n"); META_CONPRINT(" Usage: sm [arguments]\n"); + META_CONPRINT(" credits - Credits listing\n"); META_CONPRINT(" plugins - Plugins menu\n"); META_CONPRINT(" version - Display version information\n"); -} \ No newline at end of file +} diff --git a/core/smn_handles.cpp b/core/smn_handles.cpp index d7e5f1a1..875f8e4e 100644 --- a/core/smn_handles.cpp +++ b/core/smn_handles.cpp @@ -65,7 +65,7 @@ static cell_t sm_CloneHandle(IPluginContext *pContext, const cell_t *params) } else if (err == HandleError_None) { return new_hndl; } else { - return pContext->ThrowNativeError("Handle to clone %x is invalid (error %d)", hndl, err); + return pContext->ThrowNativeError("Handle %x cannot be cloned because it is invalid (error %d)", hndl, err); } } diff --git a/core/systems/ForwardSys.cpp b/core/systems/ForwardSys.cpp index 76acd0ab..e4c2ca18 100644 --- a/core/systems/ForwardSys.cpp +++ b/core/systems/ForwardSys.cpp @@ -8,24 +8,9 @@ CForwardManager g_Forwards; * Gensis turns to its source, reduction occurs stepwise although the essence is all one. * End of line. FTL system check. * - * :TODO: Implement the manager. ho ho ho - * - * :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST (X=done, -=TODO) - * NORMAL FUNCTIONS: - * X Push cells - * X Push cells byref (copyback tested = yes) - * X Push floats - * X Push floats byref (copyback tested = yes) - * X Push arrays (copyback tested = yes) - * X Push strings (copyback tested = yes) + * :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST * VARARG FUNCTIONS: * - Pushing no varargs - * X Push vararg cells (copyback should be verified to not happen = didnt happen) - * X Push vararg cells byref (copyback tested = yes) - * X Push vararg floats (copyback should be verified to not happen = didnt happen) - * X Push vararg floats byref (copyback tested = yes) - * X Push vararg arrays (copyback tested = yes) - * X Push vararg strings (copyback tested = :TODO:) */ // :TODO: IMPORTANT!!! The result pointer arg in the execute function maybe invalid if the forward fails @@ -646,7 +631,7 @@ bool CForward::AddFunction(IPluginFunction *func) return false; } - //:TODO: eventually we will tell the plugin we're using it [?] + //:IDEA: eventually we will tell the plugin we're using it [?] m_functions.push_back(func); return true; diff --git a/core/systems/PluginSys.cpp b/core/systems/PluginSys.cpp index 7432baaa..50fb4238 100644 --- a/core/systems/PluginSys.cpp +++ b/core/systems/PluginSys.cpp @@ -6,6 +6,7 @@ #include "sourcemm_api.h" #include "sourcemod.h" #include "CTextParsers.h" +#include "CLogger.h" CPluginManager g_PluginSys; HandleType_t g_PluginType = 0; @@ -186,7 +187,7 @@ bool CPlugin::FinishMyCompile(char *error, size_t maxlength) memset(&m_ctx, 0, sizeof(m_ctx)); if (!error) { - SetErrorState(Plugin_Error, "Failed to compile (error %d)", err); + SetErrorState(Plugin_Failed, "Failed to compile (error %d)", err); } else { snprintf(error, maxlength, "Failed to compile (error %d)", err); } @@ -341,7 +342,6 @@ void CPlugin::Call_OnPluginInit() m_status = Plugin_Running; - int err; cell_t result; IPluginFunction *pFunction = GetFunctionByName("OnPluginInit"); if (!pFunction) @@ -349,13 +349,25 @@ void CPlugin::Call_OnPluginInit() return; } - /* :TODO: push our own handle */ pFunction->PushCell(m_handle); - if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) + pFunction->Execute(&result); +} + +void CPlugin::Call_OnPluginUnload() +{ + if (m_status < Plugin_Paused) { - /* :TODO: log into debugger instead */ - SetErrorState(Plugin_Error, "Runtime error %d", err); + return; } + + cell_t result; + IPluginFunction *pFunction = GetFunctionByName("OnPluginUnload"); + if (!pFunction) + { + return; + } + + pFunction->Execute(&result); } bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) @@ -388,15 +400,13 @@ bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) pFunction->PushCell(maxlength); if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) { - /* :TODO: debugging system */ - snprintf(error, maxlength, "Plugin load returned run time error %d", err); - m_status = Plugin_Error; + m_status = Plugin_Failed; return false; } - if (!result) + if (!result || m_status != Plugin_Loaded) { - m_status = Plugin_Error; + m_status = Plugin_Failed; return false; } @@ -461,8 +471,14 @@ bool CPlugin::SetPauseState(bool paused) } else if (!paused && GetStatus() != Plugin_Running) { return false; } - - /* :TODO: execute some forwards or some crap */ + + IPluginFunction *pFunction = GetFunctionByName("OnPluginPauseChange"); + if (pFunction) + { + cell_t result; + pFunction->PushCell(paused ? 1 : 0); + pFunction->Execute(&result); + } return true; } @@ -524,7 +540,11 @@ CPluginManager::CPluginManager() CPluginManager::~CPluginManager() { - //:TODO: we need a good way to free what we're holding + /* :NOTICE: + * Ignore the fact that there might be plugins in the cache. + * This usually means that Core is not being unloaded properly, and everything + * will crash anyway. YAY + */ sm_trie_destroy(m_LoadLookup); } @@ -537,7 +557,12 @@ void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir) m_AllPluginsLoaded = false; if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay) { - /* :TODO: log the error, don't bail out though */ + g_Logger.LogError("[SOURCEMOD] Encountered fatal error parsing file \"%s\"", config); + const char *err_msg = g_TextParser.GetSMCErrorString(err); + if (err_msg) + { + g_Logger.LogError("[SOURCEMOD] Parse error encountered: \"%s\"", err_msg); + } } LoadPluginsFromDir(basedir, NULL); @@ -559,8 +584,10 @@ void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpa if (!dir) { - //:TODO: write a logger and LOG THIS UP, BABY - //g_LibSys.GetPlatformError(error, err_max); + char error[256]; + g_LibSys.GetPlatformError(error, sizeof(error)); + g_Logger.LogError("[SOURCEMOD] Failure reading from plugins path: %s", localpath); + g_Logger.LogError("[SOURCEMOD] Platform returned error: %s", error); return; } @@ -622,7 +649,8 @@ void CPluginManager::LoadAutoPlugin(const char *file) } /* Check to see if we should try reloading it */ if (pPlugin->GetStatus() == Plugin_BadLoad - || pPlugin->GetStatus() == Plugin_Error) + || pPlugin->GetStatus() == Plugin_Error + || pPlugin->GetStatus() == Plugin_Failed) { UnloadPlugin(pPlugin); } @@ -663,7 +691,7 @@ void CPluginManager::LoadAutoPlugin(const char *file) } if (!g_pVM->SetCompilationOption(co, key, val)) { - pPlugin->SetErrorState(Plugin_Error, "Unable to set option (key \"%s\") (value \"%s\")", key, val); + pPlugin->SetErrorState(Plugin_Failed, "Unable to set option (key \"%s\") (value \"%s\")", key, val); pPlugin->CancelMyCompile(); co = NULL; break; @@ -819,20 +847,33 @@ void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin) bool CPluginManager::UnloadPlugin(IPlugin *plugin) { CPlugin *pPlugin = (CPlugin *)plugin; - - /* :TODO: More */ - List::iterator iter; IPluginsListener *pListener; + + if (pPlugin->GetStatus() >= Plugin_Error) + { + /* Notify listeners of unloading */ + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginUnloaded(pPlugin); + } + /* Notify plugin */ + pPlugin->Call_OnPluginUnload(); + } + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) { + /* Notify listeners of destruction */ pListener = (*iter); pListener->OnPluginDestroyed(pPlugin); } + /* Remove us from the lookup table and linked list */ m_plugins.remove(pPlugin); sm_trie_delete(m_LoadLookup, pPlugin->m_filename); + /* Tell the plugin to delete itself */ delete pPlugin; return true; diff --git a/core/systems/PluginSys.h b/core/systems/PluginSys.h index 4f682973..7ac3d980 100644 --- a/core/systems/PluginSys.h +++ b/core/systems/PluginSys.h @@ -129,7 +129,7 @@ public: /** * Calls the OnPluginLoad function, and sets any failed states if necessary. * NOTE: Valid pre-states are: Plugin_Created - * If validated, plugin state is changed to Plugin_Loaded + * NOTE: If validated, plugin state is changed to Plugin_Loaded * * If the error buffer is NULL, the error message is cached locally. */ @@ -138,11 +138,21 @@ public: /** * Calls the OnPluginInit function. * NOTE: Valid pre-states are: Plugin_Created - * NOTE: Pre-state will be changed to Plugin_Running + * NOTE: Post-state will be Plugin_Running */ void Call_OnPluginInit(); + + /** + * Calls the OnPluginUnload function. + */ + void Call_OnPluginUnload(); public: time_t HasUpdatedFile(); + + /** + * Returns true if the plugin was running, but is now invalid. + */ + bool WasRunning(); protected: void UpdateInfo(); private: @@ -160,6 +170,7 @@ private: time_t m_LastAccess; IdentityToken_t *m_ident; Handle_t m_handle; + bool m_WasRunning; }; class CPluginManager : @@ -181,7 +192,7 @@ public: virtual bool MorePlugins(); virtual IPlugin *GetPlugin(); virtual void NextPlugin(); - virtual void Release(); + void Release(); public: void Reset(); private: @@ -190,22 +201,22 @@ public: }; friend class CPluginManager::CPluginIterator; public: //IPluginManager - virtual IPlugin *LoadPlugin(const char *path, + IPlugin *LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max); - virtual bool UnloadPlugin(IPlugin *plugin); - virtual IPlugin *FindPluginByContext(const sp_context_t *ctx); - virtual unsigned int GetPluginCount(); - virtual IPluginIterator *GetPluginIterator(); - virtual void AddPluginsListener(IPluginsListener *listener); - virtual void RemovePluginsListener(IPluginsListener *listener); + bool UnloadPlugin(IPlugin *plugin); + IPlugin *FindPluginByContext(const sp_context_t *ctx); + unsigned int GetPluginCount(); + IPluginIterator *GetPluginIterator(); + void AddPluginsListener(IPluginsListener *listener); + void RemovePluginsListener(IPluginsListener *listener); public: //SMGlobalClass - virtual void OnSourceModAllInitialized(); - virtual void OnSourceModShutdown(); + void OnSourceModAllInitialized(); + void OnSourceModShutdown(); public: //IHandleTypeDispatch - virtual void OnHandleDestroy(HandleType_t type, void *object); + void OnHandleDestroy(HandleType_t type, void *object); public: /** * Loads all plugins not yet loaded