Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.
(This day shall henceforth be known as the Eighty Column Massacre of '07)
3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
33 #include "PluginSys.h"
35 #include "LibrarySys.h"
36 #include "HandleSys.h"
37 #include "ForwardSys.h"
38 #include "sourcemm_api.h"
39 #include "sourcemod.h"
40 #include "TextParsers.h"
42 #include "ExtensionSys.h"
43 #include "sm_srvcmds.h"
44 #include "sm_stringutil.h"
45 #include "ConCmdManager.h"
46 #include "PlayerManager.h"
47 #include "CoreConfig.h"
49 CPluginManager g_PluginSys;
50 HandleType_t g_PluginType = 0;
51 IdentityType_t g_PluginIdent = 0;
53 CPlugin::CPlugin(const char *file)
55 static int MySerial = 0;
57 m_type = PluginType_Private;
58 m_status = Plugin_Uncompiled;
59 m_serial = ++MySerial;
61 m_errormsg[256] = '\0';
62 snprintf(m_filename, sizeof(m_filename), "%s", file);
65 m_pProps = sm_trie_create();
66 m_FakeNativesMissing = false;
67 m_LibraryMissing = false;
75 sec.pOwner = g_PluginSys.GetIdentity();
76 sec.pIdentity = sec.pOwner;
78 g_HandleSys.FreeHandle(m_handle, &sec);
79 g_ShareSys.DestroyIdentity(m_ident);
89 m_ctx.vm->FreeContext(m_ctx.ctx);
94 m_ctx.vm->AbortCompilation(m_ctx.co);
100 g_pSourcePawn->FreeFromMemory(m_plugin);
105 sm_trie_destroy(m_pProps);
107 for (size_t i=0; i<m_configs.size(); i++)
114 void CPlugin::InitIdentity()
118 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
119 m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
120 m_ctx.base->SetIdentity(m_ident);
124 Handle_t CPlugin::GetMyHandle()
129 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
131 char fullpath[PLATFORM_MAX_PATH];
132 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
133 FILE *fp = fopen(fullpath, "rb");
135 CPlugin *pPlugin = new CPlugin(file);
141 snprintf(error, maxlength, "Unable to open file");
143 pPlugin->m_status = Plugin_BadLoad;
148 sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
154 snprintf(error, maxlength, "Error %d while parsing plugin", err);
156 pPlugin->m_status = Plugin_BadLoad;
162 pPlugin->m_plugin = pl;
167 bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
169 bool exists = sm_trie_retrieve(m_pProps, prop, ptr);
171 if (exists && remove)
173 sm_trie_delete(m_pProps, prop);
179 bool CPlugin::SetProperty(const char *prop, void *ptr)
181 return sm_trie_insert(m_pProps, prop, ptr);
184 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
191 /* :NOTICE: We will eventually need to change these natives
192 * for swapping in new contexts
194 if (m_ctx.co || m_ctx.ctx)
199 m_status = Plugin_Uncompiled;
202 m_ctx.co = vm->StartCompilation(m_plugin);
207 void CPlugin::CancelMyCompile()
214 m_ctx.vm->AbortCompilation(m_ctx.co);
219 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
227 m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
230 memset(&m_ctx, 0, sizeof(m_ctx));
233 snprintf(error, maxlength, "JIT failed to compile (error %d)", err);
238 m_ctx.base = new BaseContext(m_ctx.ctx);
239 m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
241 m_status = Plugin_Created;
249 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
251 PluginStatus old_status = m_status;
254 if (old_status == Plugin_Running)
256 /* Tell everyone we're now paused */
257 g_PluginSys._SetPauseState(this, true);
261 va_start(ap, error_fmt);
262 vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
267 m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
271 void CPlugin::UpdateInfo()
273 /* Now grab the info */
275 IPluginContext *base = GetBaseContext();
276 int err = base->FindPubvarByName("myinfo", &idx);
278 memset(&m_info, 0, sizeof(m_info));
280 if (err == SP_ERROR_NONE)
282 struct sm_plugininfo_c_t
290 sm_plugininfo_c_t *cinfo;
293 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
294 base->LocalToString(cinfo->name, (char **)&m_info.name);
295 base->LocalToString(cinfo->description, (char **)&m_info.description);
296 base->LocalToString(cinfo->author, (char **)&m_info.author);
297 base->LocalToString(cinfo->url, (char **)&m_info.url);
298 base->LocalToString(cinfo->version, (char **)&m_info.version);
301 m_info.author = m_info.author ? m_info.author : "";
302 m_info.description = m_info.description ? m_info.description : "";
303 m_info.name = m_info.name ? m_info.name : "";
304 m_info.url = m_info.url ? m_info.url : "";
305 m_info.version = m_info.version ? m_info.version : "";
308 void CPlugin::Call_OnPluginStart()
310 if (m_status != Plugin_Loaded)
315 m_status = Plugin_Running;
318 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart");
325 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
327 SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
331 if ((pFunction = m_ctx.base->GetFunctionByName("OnMapStart")) != NULL)
333 pFunction->Execute(NULL);
336 if (SM_AreConfigsExecuted())
338 SM_ExecuteForPlugin(GetBaseContext());
343 void CPlugin::Call_OnPluginEnd()
345 if (m_status > Plugin_Paused)
351 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd");
357 pFunction->Execute(&result);
360 void CPlugin::Call_OnAllPluginsLoaded()
362 if (m_status > Plugin_Paused)
368 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnAllPluginsLoaded");
374 pFunction->Execute(&result);
377 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
379 if (m_status != Plugin_Created)
384 m_status = Plugin_Loaded;
388 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
395 pFunction->PushCell(m_handle);
396 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
397 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
398 pFunction->PushCell(maxlength);
399 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
404 if (!result || m_status != Plugin_Loaded)
412 const sp_plugin_t *CPlugin::GetPluginStructure()
417 IPluginContext *CPlugin::GetBaseContext()
422 sp_context_t *CPlugin::GetContext()
427 const char *CPlugin::GetFilename()
432 PluginType CPlugin::GetType()
437 const sm_plugininfo_t *CPlugin::GetPublicInfo()
442 unsigned int CPlugin::GetSerial()
447 PluginStatus CPlugin::GetStatus()
452 bool CPlugin::IsDebugging()
459 return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
462 void CPlugin::LibraryActions(bool dropping)
464 List<String>::iterator iter;
465 for (iter = m_Libraries.begin();
466 iter != m_Libraries.end();
469 g_PluginSys.OnLibraryAction((*iter).c_str(), dropping);
473 bool CPlugin::SetPauseState(bool paused)
475 if (paused && GetStatus() != Plugin_Running)
478 } else if (!paused && GetStatus() != Plugin_Paused) {
484 LibraryActions(true);
487 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
491 pFunction->PushCell(paused ? 1 : 0);
492 pFunction->Execute(&result);
497 m_status = Plugin_Paused;
498 m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
500 m_status = Plugin_Running;
501 m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
504 g_PluginSys._SetPauseState(this, paused);
508 LibraryActions(false);
514 IdentityToken_t *CPlugin::GetIdentity()
519 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
527 snprintf(error, maxlength, "Plugin is not runnable.");
532 if (debug && IsDebugging())
536 snprintf(error, maxlength, "Plugin is already in debug mode.");
539 } else if (!debug && !IsDebugging()) {
542 snprintf(error, maxlength, "Plugins is already in production mode.");
547 ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
548 if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
552 snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
557 sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
561 memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
562 new_ctx->hp = m_ctx.ctx->hp;
563 new_ctx->sp = m_ctx.ctx->sp;
564 new_ctx->frm = m_ctx.ctx->frm;
565 new_ctx->dbreak = m_ctx.ctx->dbreak;
566 new_ctx->context = m_ctx.ctx->context;
567 memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
569 uint32_t nativeCount = m_plugin->info.natives_num;
570 for (uint32_t i=0; i<nativeCount; i++)
572 new_ctx->natives[i].pfn = m_ctx.ctx->natives[i].pfn;
573 new_ctx->natives[i].status = m_ctx.ctx->natives[i].status;
576 g_pVM->FreeContext(m_ctx.ctx);
578 m_ctx.base->SetContext(new_ctx);
584 snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
592 bool CPlugin::IsRunnable()
594 return (m_status <= Plugin_Paused) ? true : false;
597 time_t CPlugin::GetFileTimeStamp()
599 char path[PLATFORM_MAX_PATH];
600 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
601 #ifdef PLATFORM_WINDOWS
603 if (_stat(path, &s) != 0)
604 #elif defined PLATFORM_POSIX
606 if (stat(path, &s) != 0)
615 time_t CPlugin::GetTimeStamp()
620 void CPlugin::SetTimeStamp(time_t t)
625 void CPlugin::AddLangFile(unsigned int index)
627 m_PhraseFiles.push_back(index);
630 size_t CPlugin::GetLangFileCount()
632 return m_PhraseFiles.size();
635 unsigned int CPlugin::GetLangFileByIndex(unsigned int index)
637 return m_PhraseFiles.at(index);
640 void CPlugin::DependencyDropped(CPlugin *pOwner)
647 List<String>::iterator reqlib_iter;
648 List<String>::iterator lib_iter;
649 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
651 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
653 if ((*reqlib_iter) == (*lib_iter))
655 m_LibraryMissing = true;
660 List<FakeNative *>::iterator iter;
664 unsigned int unbound = 0;
666 for (iter = pOwner->m_fakeNatives.begin();
667 iter != pOwner->m_fakeNatives.end();
671 /* Find this native! */
672 if (m_ctx.base->FindNativeByName(pNative->name.c_str(), &idx) != SP_ERROR_NONE)
677 m_ctx.base->GetNativeByIndex(idx, &native);
679 native->status = SP_NATIVE_UNBOUND;
685 m_FakeNativesMissing = true;
688 /* :IDEA: in the future, add native trapping? */
689 if (m_FakeNativesMissing || m_LibraryMissing)
691 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
694 m_dependsOn.remove(pOwner);
697 unsigned int CPlugin::GetConfigCount()
699 return (unsigned int)m_configs.size();
702 AutoConfig *CPlugin::GetConfig(unsigned int i)
704 if (i >= GetConfigCount())
712 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
714 AutoConfig *c = new AutoConfig;
718 c->create = autoCreate;
720 m_configs.push_back(c);
727 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
733 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
738 bool CPluginManager::CPluginIterator::MorePlugins()
740 return (current != mylist->end());
743 void CPluginManager::CPluginIterator::NextPlugin()
748 void CPluginManager::CPluginIterator::Release()
750 g_PluginSys.ReleaseIterator(this);
753 CPluginManager::CPluginIterator::~CPluginIterator()
757 void CPluginManager::CPluginIterator::Reset()
759 current = mylist->begin();
766 CPluginManager::CPluginManager()
768 m_LoadLookup = sm_trie_create();
769 m_AllPluginsLoaded = false;
771 m_pNativeLookup = sm_trie_create();
774 CPluginManager::~CPluginManager()
777 * Ignore the fact that there might be plugins in the cache.
778 * This usually means that Core is not being unloaded properly, and everything
779 * will crash anyway. YAY
781 sm_trie_destroy(m_LoadLookup);
782 sm_trie_destroy(m_pNativeLookup);
784 CStack<CPluginManager::CPluginIterator *>::iterator iter;
785 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
792 void CPluginManager::Shutdown()
794 List<CPlugin *>::iterator iter;
796 while ((iter = m_plugins.begin()) != m_plugins.end())
802 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
804 /* First read in the database of plugin settings */
806 unsigned int line, col;
807 m_AllPluginsLoaded = false;
808 if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
810 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
811 const char *err_msg = g_TextParser.GetSMCErrorString(err);
814 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
818 LoadPluginsFromDir(basedir, NULL);
821 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
823 char base_path[PLATFORM_MAX_PATH];
825 /* Form the current path to start reading from */
826 if (localpath == NULL)
828 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
830 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
833 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
838 g_LibSys.GetPlatformError(error, sizeof(error));
839 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
840 g_Logger.LogError("[SM] Platform returned error: %s", error);
844 while (dir->MoreFiles())
846 if (dir->IsEntryDirectory()
847 && (strcmp(dir->GetEntryName(), ".") != 0)
848 && (strcmp(dir->GetEntryName(), "..") != 0)
849 && (strcmp(dir->GetEntryName(), "disabled") != 0)
850 && (strcmp(dir->GetEntryName(), "optional") != 0))
852 char new_local[PLATFORM_MAX_PATH];
853 if (localpath == NULL)
855 /* If no path yet, don't add a former slash */
856 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
858 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
860 LoadPluginsFromDir(basedir, new_local);
861 } else if (dir->IsEntryFile()) {
862 const char *name = dir->GetEntryName();
863 size_t len = strlen(name);
865 && strcmp(&name[len-4], ".smx") == 0)
867 /* If the filename matches, load the plugin */
868 char plugin[PLATFORM_MAX_PATH];
869 if (localpath == NULL)
871 snprintf(plugin, sizeof(plugin), "%s", name);
873 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
875 LoadAutoPlugin(plugin);
880 g_LibSys.CloseDirectory(dir);
883 //well i have discovered that gabe newell is very fat, so i wrote this comment now
884 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
887 * Does this plugin already exist?
890 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
892 /* Check to see if we should try reloading it */
893 if (pPlugin->GetStatus() == Plugin_BadLoad
894 || pPlugin->GetStatus() == Plugin_Error
895 || pPlugin->GetStatus() == Plugin_Failed)
897 UnloadPlugin(pPlugin);
903 return LoadRes_AlreadyLoaded;
907 pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
909 assert(pPlugin != NULL);
911 pPlugin->m_type = PluginType_MapUpdated;
913 ICompilation *co = NULL;
915 if (pPlugin->m_status == Plugin_Uncompiled)
917 co = pPlugin->StartMyCompile(g_pVM);
920 PluginSettings *pset;
921 unsigned int setcount = m_PluginInfo.GetSettingsNum();
922 for (unsigned int i=0; i<setcount; i++)
924 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
928 pPlugin->m_type = pset->type_val;
931 for (unsigned int j=0; j<pset->opts_num; j++)
933 const char *key, *val;
934 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
939 if (!g_pVM->SetCompilationOption(co, key, val))
943 snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
945 pPlugin->CancelMyCompile();
953 /* Do the actual compiling */
956 pPlugin->FinishMyCompile(error, maxlength);
961 if (pPlugin->GetStatus() == Plugin_Created)
963 AddCoreNativesToPlugin(pPlugin);
964 pPlugin->InitIdentity();
965 if (pPlugin->Call_AskPluginLoad(error, maxlength))
967 /* Autoload any modules */
968 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
970 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
974 /* Save the time stamp */
975 time_t t = pPlugin->GetFileTimeStamp();
976 pPlugin->SetTimeStamp(t);
983 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
986 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
992 if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure)
998 if (res == LoadRes_AlreadyLoaded)
1006 /* Run second pass if we need to */
1007 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1009 if (!RunSecondPass(pl, error, maxlength))
1014 pl->Call_OnAllPluginsLoaded();
1020 void CPluginManager::LoadAutoPlugin(const char *plugin)
1024 char error[255] = "Unknown error";
1026 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1028 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
1029 pl->SetErrorState(Plugin_Failed, "%s", error);
1032 if (res == LoadRes_Successful)
1038 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1040 List<IPluginsListener *>::iterator iter;
1041 IPluginsListener *pListener;
1043 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1045 pListener = (*iter);
1046 pListener->OnPluginCreated(pPlugin);
1049 m_plugins.push_back(pPlugin);
1050 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
1053 void CPluginManager::LoadAll_SecondPass()
1055 List<CPlugin *>::iterator iter;
1059 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1062 if (pPlugin->GetStatus() == Plugin_Loaded)
1065 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1067 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1068 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1073 m_AllPluginsLoaded = true;
1076 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1085 IPluginContext *pBase = pPlugin->GetBaseContext();
1086 uint32_t num = pBase->GetPubVarsNum();
1087 sp_pubvar_t *pubvar;
1089 char pathfile[PLATFORM_MAX_PATH];
1091 for (uint32_t i=0; i<num; i++)
1093 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1097 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1099 pl = (_pl *)pubvar->offs;
1100 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1104 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1108 g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1109 if (strcmp(pathfile, file) == 0)
1113 if (pl->required == false)
1115 IPluginFunction *pFunc;
1117 UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1118 if ((pFunc=pBase->GetFunctionByName(buffer)))
1121 pFunc->Execute(&res);
1122 if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
1126 snprintf(error, maxlength, "Fatal error during initializing plugin load");
1132 /* Check that we aren't registering the same library twice */
1133 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1135 pPlugin->m_RequiredLibs.push_back(name);
1139 List<CPlugin *>::iterator iter;
1142 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1145 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1155 snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
1166 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1168 /* Find any extensions this plugin needs */
1177 IPluginContext *pBase = pPlugin->GetBaseContext();
1178 uint32_t num = pBase->GetPubVarsNum();
1179 sp_pubvar_t *pubvar;
1181 char path[PLATFORM_MAX_PATH];
1183 for (uint32_t i=0; i<num; i++)
1185 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1189 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1191 ext = (_ext *)pubvar->offs;
1192 if (!ext->required && !ext->autoload)
1196 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1200 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1206 /* Attempt to auto-load if necessary */
1209 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1210 g_Extensions.LoadAutoExtension(path);
1212 } else if (pass == 2) {
1213 /* Is this required? */
1216 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1217 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1219 pExt = g_Extensions.FindExtensionByName(name);
1221 /* :TODO: should we bind to unloaded extensions?
1222 * Currently the extension manager will ignore this.
1224 if (!pExt || !pExt->IsRunning(NULL, 0))
1228 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1232 g_Extensions.BindChildPlugin(pExt, pPlugin);
1242 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1244 /* Second pass for extension requirements */
1245 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1250 /* Bind all extra natives */
1251 g_Extensions.BindAllNativesToPlugin(pPlugin);
1252 AddFakeNativesToPlugin(pPlugin);
1254 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1259 /* Find any unbound natives
1260 * Right now, these are not allowed
1262 IPluginContext *pContext = pPlugin->GetBaseContext();
1263 uint32_t num = pContext->GetNativesNum();
1264 sp_native_t *native;
1265 for (unsigned int i=0; i<num; i++)
1267 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1271 if (native->status == SP_NATIVE_UNBOUND
1272 && native->name[0] != '@'
1273 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1277 snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
1283 /* Finish by telling all listeners */
1284 List<IPluginsListener *>::iterator iter;
1285 IPluginsListener *pListener;
1286 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1288 pListener = (*iter);
1289 pListener->OnPluginLoaded(pPlugin);
1292 /* Tell this plugin to finish initializing itself */
1293 pPlugin->Call_OnPluginStart();
1295 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1296 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_fakeNatives.size())
1298 List<CPlugin *>::iterator pl_iter;
1300 for (pl_iter = m_plugins.begin();
1301 pl_iter != m_plugins.end();
1304 pOther = (*pl_iter);
1305 if ((pOther->GetStatus() == Plugin_Error
1306 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1307 || pOther->m_FakeNativesMissing)
1309 TryRefreshDependencies(pOther);
1314 /* Go through our libraries and tell other plugins they're added */
1315 List<String>::iterator s_iter;
1316 for (s_iter = pPlugin->m_Libraries.begin();
1317 s_iter != pPlugin->m_Libraries.end();
1320 OnLibraryAction((*s_iter).c_str(), false);
1326 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
1328 List<sp_nativeinfo_t *>::iterator iter;
1330 for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
1332 sp_nativeinfo_t *natives = (*iter);
1333 IPluginContext *ctx = pPlugin->GetBaseContext();
1335 /* Attempt to bind every native! */
1336 while (natives[i].func != NULL)
1338 ctx->BindNative(&natives[i++]);
1343 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1345 assert(pPlugin->GetBaseContext() != NULL);
1347 AddFakeNativesToPlugin(pPlugin);
1349 List<String>::iterator lib_iter;
1350 List<String>::iterator req_iter;
1351 List<CPlugin *>::iterator pl_iter;
1353 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1356 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1359 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1361 if ((*req_iter) == (*lib_iter))
1369 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1374 /* Find any unbound natives
1375 * Right now, these are not allowed
1377 IPluginContext *pContext = pPlugin->GetBaseContext();
1378 uint32_t num = pContext->GetNativesNum();
1379 sp_native_t *native;
1380 for (unsigned int i=0; i<num; i++)
1382 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1386 if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
1388 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1393 if (pPlugin->GetStatus() == Plugin_Error)
1395 /* If we got here, all natives are okay again! */
1396 pPlugin->m_status = Plugin_Running;
1397 if ((pPlugin->m_ctx.ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
1399 pPlugin->m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
1400 _SetPauseState(pPlugin, false);
1405 void CPluginManager::AddFakeNativesToPlugin(CPlugin *pPlugin)
1407 IPluginContext *pContext = pPlugin->GetBaseContext();
1408 sp_nativeinfo_t native;
1410 List<FakeNative *>::iterator iter;
1411 FakeNative *pNative;
1413 for (iter = m_Natives.begin(); iter != m_Natives.end(); iter++)
1416 ctx = pNative->ctx->GetContext();
1417 if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
1419 /* Ignore natives in paused plugins */
1422 native.name = pNative->name.c_str();
1423 native.func = pNative->func;
1424 if (pContext->BindNative(&native) == SP_ERROR_NONE)
1427 pContext->FindNativeByName(native.name, &idx);
1428 if (pPlugin->GetContext()->natives[idx].flags & SP_NTVFLAG_OPTIONAL)
1430 WeakNative wkn(pPlugin, idx);
1431 GetPluginByCtx(ctx)->m_WeakNatives.push_back(wkn);
1434 /* Add us as a dependency, but we're careful not to do this circularly! */
1435 if (pNative->ctx != pContext)
1437 CPlugin *pOther = GetPluginByCtx(ctx);
1438 if (pOther->m_dependents.find(pPlugin) == pOther->m_dependents.end())
1440 pOther->m_dependents.push_back(pPlugin);
1442 if (pPlugin->m_dependsOn.find(pOther) == pPlugin->m_dependsOn.end())
1444 pPlugin->m_dependsOn.push_back(pOther);
1451 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1453 CPlugin *pPlugin = (CPlugin *)plugin;
1455 /* This prevents removal during insertion or anything else weird */
1456 if (m_plugins.find(pPlugin) == m_plugins.end())
1461 /* Remove us from the lookup table and linked list */
1462 m_plugins.remove(pPlugin);
1463 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
1465 /* Go through our libraries and tell other plugins they're gone */
1466 List<String>::iterator s_iter;
1467 for (s_iter = pPlugin->m_Libraries.begin();
1468 s_iter != pPlugin->m_Libraries.end();
1471 OnLibraryAction((*s_iter).c_str(), true);
1474 /* Go through all dependent plugins and tell them this plugin is now gone */
1475 List<CPlugin *>::iterator pl_iter;
1477 for (pl_iter = pPlugin->m_dependents.begin();
1478 pl_iter != pPlugin->m_dependents.end();
1481 pOther = (*pl_iter);
1482 pOther->DependencyDropped(pPlugin);
1485 /* Tell everyone we depend on that we no longer exist */
1486 for (pl_iter = pPlugin->m_dependsOn.begin();
1487 pl_iter != pPlugin->m_dependsOn.end();
1490 pOther = (*pl_iter);
1491 pOther->m_dependents.remove(pPlugin);
1494 /* Remove weak references to us */
1495 for (pl_iter = m_plugins.begin();
1496 pl_iter != m_plugins.end();
1499 pOther = (*pl_iter);
1500 List<WeakNative>::iterator wk_iter = pOther->m_WeakNatives.begin();
1501 while (wk_iter != pOther->m_WeakNatives.end())
1503 if ((*wk_iter).pl == pPlugin)
1505 wk_iter = pOther->m_WeakNatives.erase(wk_iter);
1512 List<IPluginsListener *>::iterator iter;
1513 IPluginsListener *pListener;
1515 if (pPlugin->GetStatus() <= Plugin_Error)
1517 /* Notify listeners of unloading */
1518 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1520 pListener = (*iter);
1521 pListener->OnPluginUnloaded(pPlugin);
1524 pPlugin->Call_OnPluginEnd();
1527 /* Unbound weak natives */
1528 List<WeakNative>::iterator wk_iter;
1529 for (wk_iter=pPlugin->m_WeakNatives.begin(); wk_iter!=pPlugin->m_WeakNatives.end(); wk_iter++)
1531 WeakNative & wkn = (*wk_iter);
1532 sp_context_t *ctx = wkn.pl->GetContext();
1533 ctx->natives[wkn.idx].status = SP_NATIVE_UNBOUND;
1534 wkn.pl->m_FakeNativesMissing = true;
1537 /* Remove all of our native functions */
1538 List<FakeNative *>::iterator fn_iter;
1539 FakeNative *pNative;
1540 for (fn_iter = pPlugin->m_fakeNatives.begin();
1541 fn_iter != pPlugin->m_fakeNatives.end();
1544 pNative = (*fn_iter);
1545 m_Natives.remove(pNative);
1546 sm_trie_delete(m_pNativeLookup, pNative->name.c_str());
1547 g_pVM->DestroyFakeNative(pNative->func);
1551 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1553 /* Notify listeners of destruction */
1554 pListener = (*iter);
1555 pListener->OnPluginDestroyed(pPlugin);
1558 /* Tell the plugin to delete itself */
1564 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1566 IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
1570 unsigned int CPluginManager::GetPluginCount()
1572 return m_plugins.size();
1575 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1577 m_listeners.push_back(listener);
1580 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1582 m_listeners.remove(listener);
1585 IPluginIterator *CPluginManager::GetPluginIterator()
1587 if (m_iters.empty())
1589 return new CPluginIterator(&m_plugins);
1591 CPluginIterator *iter = m_iters.front();
1598 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1603 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1605 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1606 size_t alias_len = 0;
1607 size_t local_len = 0;
1609 const char *ptr = alias;
1610 unsigned int alias_explicit_paths = 0;
1611 unsigned int alias_path_end = 0;
1612 while (*ptr != '\0')
1614 if (*ptr == '\\' || *ptr == '/')
1616 alias_explicit_paths++;
1617 alias_path_end = alias_len;
1623 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1625 /* Trailing slash is totally invalid here */
1630 unsigned int local_explicit_paths = 0;
1631 unsigned int local_path_end = 0;
1632 while (*ptr != '\0')
1634 if (*ptr == '\\' || *ptr == '/')
1636 local_explicit_paths++;
1637 local_path_end = local_len;
1643 /* If the alias has more explicit paths than the real path,
1644 * no match will be possible.
1646 if (alias_explicit_paths > local_explicit_paths)
1651 if (alias_explicit_paths)
1653 /* We need to find if the paths match now. For example, these should all match:
1655 * csdm optional/csdm
1656 * csdm/ban optional/crab/csdm/ban
1658 const char *aliasptr = alias;
1659 const char *localptr = localpath;
1663 if (*aliasptr != *localptr)
1665 /* We have to knock one path off */
1666 local_explicit_paths--;
1667 if (alias_explicit_paths > local_explicit_paths)
1669 /* Skip out if we're gonna have an impossible match */
1672 /* Eat up localptr tokens until we get a result */
1673 while (((localptr - localpath) < (int)local_path_end)
1675 && *localptr != '\\')
1679 /* Check if we hit the end of our searchable area.
1680 * This probably isn't possible because of the path
1681 * count check, but it's a good idea anyway.
1683 if ((localptr - localpath) >= (int)local_path_end)
1687 /* Consume the slash token */
1690 /* Reset the alias pointer so we can continue consuming */
1696 * This is safe because if localptr terminates early, aliasptr will too
1700 /* We should never reach the end of the string because of this check. */
1701 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1702 bool localend = (localptr - localpath) > (int)local_path_end;
1703 if (aliasend || localend)
1705 if (aliasend && localend)
1707 /* we matched, and we can break out now */
1711 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1716 /* If we got here, it's safe to compare the next two tokens */
1717 if (*localptr != *aliasptr)
1728 /* If we got here, it's time to compare filenames */
1729 const char *aliasptr = alias;
1730 const char *localptr = localpath;
1732 if (alias_explicit_paths)
1734 aliasptr = &alias[alias_path_end + 1];
1737 if (local_explicit_paths)
1739 localptr = &localpath[local_path_end + 1];
1744 if (*aliasptr == '*')
1746 /* First, see if this is the last character */
1747 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1749 /* If so, there's no need to match anything else */
1752 /* Otherwise, we need to search for an appropriate matching sequence in local.
1753 * Note that we only need to search up to the next asterisk.
1757 const char *local_orig = localptr;
1761 while (*aliasptr != '\0' && *aliasptr != '*')
1763 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1764 if (*aliasptr != *localptr)
1774 /* If we didn't get a match, we need to advance the search stream.
1775 * This will let us skip tokens while still searching for another match.
1777 localptr = ++local_orig;
1778 /* Make sure we don't go out of bounds */
1779 if (*localptr == '\0')
1790 /* If we got a match, move on to the next token */
1793 } else if (*aliasptr == '\0') {
1794 if (*localptr == '\0'
1796 strcmp(localptr, ".smx") == 0)
1802 } else if (*aliasptr != *localptr) {
1812 bool CPluginManager::IsLateLoadTime() const
1814 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1817 void CPluginManager::OnSourceModAllInitialized()
1819 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1822 g_HandleSys.InitAccessDefaults(NULL, &sec);
1824 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1825 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1827 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1828 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1830 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1832 g_ShareSys.AddInterface(NULL, this);
1835 void CPluginManager::OnSourceModShutdown()
1837 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1839 List<CPlugin *>::iterator iter;
1840 while ( (iter = m_plugins.begin()) != m_plugins.end() )
1842 UnloadPlugin((*iter));
1845 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1846 g_ShareSys.DestroyIdentType(g_PluginIdent);
1847 g_ShareSys.DestroyIdentity(m_MyIdent);
1850 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1852 /* We don't care about the internal object, actually */
1855 void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
1857 m_natives.push_back(natives);
1860 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1868 sec.pIdentity = m_MyIdent;
1870 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1883 CPlugin *CPluginManager::GetPluginByOrder(int num)
1885 if (num < 1 || num > (int)GetPluginCount())
1893 IPluginIterator *iter = GetPluginIterator();
1894 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
1896 pl = (CPlugin *)(iter->GetPlugin());
1902 const char *CPluginManager::GetStatusText(PluginStatus st)
1906 case Plugin_Running:
1912 case Plugin_Uncompiled:
1913 return "Uncompiled";
1914 case Plugin_BadLoad:
1924 void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1928 const char *cmd = g_RootMenu.GetArgument(2);
1929 if (strcmp(cmd, "list") == 0)
1932 unsigned int id = 1;
1933 int plnum = GetPluginCount();
1937 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
1940 g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1943 IPluginIterator *iter = GetPluginIterator();
1944 for (; iter->MorePlugins(); iter->NextPlugin(), id++)
1946 IPlugin *pl = iter->GetPlugin();
1947 assert(pl->GetStatus() != Plugin_Created);
1949 const sm_plugininfo_t *info = pl->GetPublicInfo();
1950 if (pl->GetStatus() != Plugin_Running)
1952 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1954 len += UTIL_Format(buffer, sizeof(buffer), " %02d", id);
1956 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1957 if (IS_STR_FILLED(info->version))
1959 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1961 if (IS_STR_FILLED(info->author))
1963 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
1965 g_RootMenu.ConsolePrint("%s", buffer);
1970 } else if (strcmp(cmd, "load") == 0) {
1973 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
1979 const char *filename = g_RootMenu.GetArgument(3);
1981 char pluginfile[256];
1982 const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
1983 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
1985 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
1989 g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
1995 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
1997 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
2001 } else if (strcmp(cmd, "unload") == 0) {
2004 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2010 const char *arg = g_RootMenu.GetArgument(3);
2011 int id = strtol(arg, &end, 10);
2015 pl = GetPluginByOrder(id);
2018 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2022 char pluginfile[256];
2023 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2024 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2026 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2028 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2033 char name[PLATFORM_MAX_PATH];
2034 const sm_plugininfo_t *info = pl->GetPublicInfo();
2035 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2037 if (UnloadPlugin(pl))
2039 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2041 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
2045 } else if (strcmp(cmd, "info") == 0) {
2048 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
2052 int num = atoi(g_RootMenu.GetArgument(3));
2053 if (num < 1 || num > (int)GetPluginCount())
2055 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
2059 CPlugin *pl = GetPluginByOrder(num);
2060 const sm_plugininfo_t *info = pl->GetPublicInfo();
2062 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
2063 if (pl->GetStatus() <= Plugin_Error)
2065 if (IS_STR_FILLED(info->name))
2067 if (IS_STR_FILLED(info->description))
2069 g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description);
2071 g_RootMenu.ConsolePrint(" Title: %s", info->name);
2074 if (IS_STR_FILLED(info->author))
2076 g_RootMenu.ConsolePrint(" Author: %s", info->author);
2078 if (IS_STR_FILLED(info->version))
2080 g_RootMenu.ConsolePrint(" Version: %s", info->version);
2082 if (IS_STR_FILLED(info->url))
2084 g_RootMenu.ConsolePrint(" URL: %s", info->url);
2086 if (pl->GetStatus() == Plugin_Error)
2088 g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg);
2090 g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "Yes" : "No");
2091 g_RootMenu.ConsolePrint(" Running: %s", pl->GetStatus() == Plugin_Running ? "Yes" : "No");
2093 const char *typestr = "";
2094 switch (pl->GetType())
2096 case PluginType_MapUpdated:
2097 typestr = "Map Change if Updated";
2099 case PluginType_MapOnly:
2100 typestr = "Map Change";
2102 case PluginType_Private:
2103 case PluginType_Global:
2108 g_RootMenu.ConsolePrint(" Reloads: %s", typestr);
2111 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
2112 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2113 info->name ? info->name : "<none>",
2114 info->version ? info->version : "<none>");
2115 if (IS_STR_FILLED(info->url))
2117 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
2122 } else if (strcmp(cmd, "debug") == 0) {
2125 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
2129 int num = atoi(g_RootMenu.GetArgument(3));
2130 if (num < 1 || num > (int)GetPluginCount())
2132 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
2137 const char *mode = g_RootMenu.GetArgument(4);
2138 if ((res=strcmp("on", mode)) && strcmp("off", mode))
2140 g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
2152 CPlugin *pl = GetPluginByOrder(num);
2153 if (debug && pl->IsDebugging())
2155 g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
2157 } else if (!debug && !pl->IsDebugging()) {
2158 g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
2163 if (pl->ToggleDebugMode(debug, error, sizeof(error)))
2165 g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
2168 g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
2169 g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
2175 /* Draw the main menu */
2176 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
2177 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
2178 g_RootMenu.DrawGenericOption("load", "Load a plugin");
2179 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
2180 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
2181 g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
2184 void CPluginManager::ReloadOrUnloadPlugins()
2186 List<CPlugin *>::iterator iter;
2187 List<CPlugin *> tmp_list = m_plugins;
2191 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2194 if (pl->GetType() == PluginType_MapOnly)
2196 UnloadPlugin((IPlugin *)pl);
2197 } else if (pl->GetType() == PluginType_MapUpdated) {
2198 t = pl->GetFileTimeStamp();
2199 if (!t || t > pl->GetTimeStamp())
2201 pl->SetTimeStamp(t);
2202 UnloadPlugin((IPlugin *)pl);
2208 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2210 List<IPluginsListener *>::iterator iter;
2211 IPluginsListener *pListener;
2212 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2214 pListener = (*iter);
2215 pListener->OnPluginPauseChange(pl, paused);
2219 bool CPluginManager::AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func)
2221 if (sm_trie_retrieve(m_pNativeLookup, name, NULL))
2226 FakeNative *pNative = new FakeNative;
2228 pNative->func = g_pVM->CreateFakeNative(func, pNative);
2235 pNative->call = pFunction;
2236 pNative->name.assign(name);
2237 pNative->ctx = pFunction->GetParentContext();
2239 m_Natives.push_back(pNative);
2240 sm_trie_insert(m_pNativeLookup, name,pNative);
2242 CPlugin *pPlugin = GetPluginByCtx(pNative->ctx->GetContext());
2243 pPlugin->m_fakeNatives.push_back(pNative);
2248 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2250 List<CPlugin *>::iterator iter;
2252 IPluginFunction *pFunction;
2254 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2258 if (pPlugin->GetStatus() <= Plugin_Paused)
2260 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2264 pForward->AddFunction(pFunction);
2270 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2272 if (pToken->type != g_PluginIdent)
2277 return (CPlugin *)(pToken->ptr);
2280 void CPluginManager::OnLibraryAction(const char *lib, bool drop)
2282 List<CPlugin *>::iterator iter;
2291 const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
2293 for (iter=m_plugins.begin();
2294 iter!=m_plugins.end();
2297 CPlugin *pl = (*iter);
2298 if (pl->GetStatus() != Plugin_Running)
2302 IPluginContext *pContext = pl->GetBaseContext();
2303 IPluginFunction *pf = pContext->GetFunctionByName(name);
2308 uint32_t num_vars = pContext->GetPubVarsNum();
2309 for (uint32_t i=0; i<num_vars; i++)
2311 sp_pubvar_t *pubvar;
2312 if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
2316 if (strncmp(pubvar->name, "__pl_", 5) != 0)
2320 plc = (_pl *)pubvar->offs;
2326 pContext->LocalToString(plc->name, &str);
2327 if (strcmp(str, lib) != 0)
2331 pf->PushString(lib);
2337 bool CPluginManager::LibraryExists(const char *lib)
2339 List<CPlugin *>::iterator iter;
2341 for (iter=m_plugins.begin();
2342 iter!=m_plugins.end();
2345 CPlugin *pl = (*iter);
2346 if (pl->GetStatus() != Plugin_Running)
2350 List<String>::iterator s_iter;
2351 for (s_iter = pl->m_Libraries.begin();
2352 s_iter != pl->m_Libraries.end();
2355 if ((*s_iter).compare(lib) == 0)
2365 void CPluginManager::AllPluginsLoaded()
2367 List<CPlugin *>::iterator iter;
2370 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2373 pl->Call_OnAllPluginsLoaded();