3 * =============================================================================
5 * Copyright (C) 2004-2008 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"
48 #include "Translator.h"
50 CPluginManager g_PluginSys;
51 HandleType_t g_PluginType = 0;
52 IdentityType_t g_PluginIdent = 0;
54 CPlugin::CPlugin(const char *file)
56 static int MySerial = 0;
58 m_type = PluginType_Private;
59 m_status = Plugin_Uncompiled;
60 m_serial = ++MySerial;
62 m_errormsg[256] = '\0';
63 snprintf(m_filename, sizeof(m_filename), "%s", file);
66 m_pProps = sm_trie_create();
67 m_FakeNativesMissing = false;
68 m_LibraryMissing = false;
69 m_bGotAllLoaded = false;
70 m_pPhrases = g_Translator.CreatePhraseCollection();
78 sec.pOwner = g_PluginSys.GetIdentity();
79 sec.pIdentity = sec.pOwner;
81 g_HandleSys.FreeHandle(m_handle, &sec);
82 g_ShareSys.DestroyIdentity(m_ident);
85 if (m_pRuntime != NULL)
93 sm_trie_destroy(m_pProps);
95 for (size_t i=0; i<m_configs.size(); i++)
100 if (m_pPhrases != NULL)
102 m_pPhrases->Destroy();
107 void CPlugin::InitIdentity()
111 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
112 m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
113 m_pRuntime->GetDefaultContext()->SetKey(1, m_ident);
114 m_pRuntime->GetDefaultContext()->SetKey(2, (IPlugin *)this);
118 unsigned int CPlugin::CalcMemUsage()
120 unsigned int base_size =
122 + sizeof(IdentityToken_t)
123 + (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
124 + sm_trie_mem_usage(m_pProps);
126 for (unsigned int i = 0; i < m_configs.size(); i++)
128 base_size += m_configs[i]->autocfg.size();
129 base_size += m_configs[i]->folder.size();
132 for (List<String>::iterator i = m_Libraries.begin();
133 i != m_Libraries.end();
136 base_size += (*i).size();
139 for (List<String>::iterator i = m_RequiredLibs.begin();
140 i != m_RequiredLibs.end();
143 base_size += (*i).size();
149 Handle_t CPlugin::GetMyHandle()
154 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
156 char fullpath[PLATFORM_MAX_PATH];
157 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
158 FILE *fp = fopen(fullpath, "rb");
160 CPlugin *pPlugin = new CPlugin(file);
166 snprintf(error, maxlength, "Unable to open file");
168 pPlugin->m_status = Plugin_BadLoad;
177 bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
179 bool exists = sm_trie_retrieve(m_pProps, prop, ptr);
181 if (exists && remove)
183 sm_trie_delete(m_pProps, prop);
189 bool CPlugin::SetProperty(const char *prop, void *ptr)
191 return sm_trie_insert(m_pProps, prop, ptr);
194 IPluginRuntime *CPlugin::GetRuntime()
199 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
201 PluginStatus old_status = m_status;
204 if (old_status == Plugin_Running)
206 /* Tell everyone we're now paused */
207 g_PluginSys._SetPauseState(this, true);
211 va_start(ap, error_fmt);
212 vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
215 if (m_pRuntime != NULL)
217 m_pRuntime->SetPauseState(true);
221 void CPlugin::UpdateInfo()
223 /* Now grab the info */
225 IPluginContext *base = GetBaseContext();
226 int err = base->FindPubvarByName("myinfo", &idx);
228 memset(&m_info, 0, sizeof(m_info));
230 if (err == SP_ERROR_NONE)
232 struct sm_plugininfo_c_t
240 sm_plugininfo_c_t *cinfo;
243 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
244 base->LocalToString(cinfo->name, (char **)&m_info.name);
245 base->LocalToString(cinfo->description, (char **)&m_info.description);
246 base->LocalToString(cinfo->author, (char **)&m_info.author);
247 base->LocalToString(cinfo->url, (char **)&m_info.url);
248 base->LocalToString(cinfo->version, (char **)&m_info.version);
251 m_info.author = m_info.author ? m_info.author : "";
252 m_info.description = m_info.description ? m_info.description : "";
253 m_info.name = m_info.name ? m_info.name : "";
254 m_info.url = m_info.url ? m_info.url : "";
255 m_info.version = m_info.version ? m_info.version : "";
257 if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE)
259 struct __version_info
266 __version_info *info;
268 const char *pDate, *pTime;
273 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
274 m_FileVersion = info->version;
275 if (m_FileVersion >= 3)
277 base->LocalToString(info->date, (char **)&pDate);
278 base->LocalToString(info->time, (char **)&pTime);
279 UTIL_Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
288 void CPlugin::Call_OnPluginStart()
290 if (m_status != Plugin_Loaded)
295 m_status = Plugin_Running;
298 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart");
305 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
307 SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
311 void CPlugin::Call_OnPluginEnd()
313 if (m_status > Plugin_Paused)
319 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd");
325 pFunction->Execute(&result);
328 void CPlugin::Call_OnAllPluginsLoaded()
330 if (m_status > Plugin_Paused)
340 m_bGotAllLoaded = true;
343 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded");
344 if (pFunction != NULL)
346 pFunction->Execute(&result);
351 if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL)
353 pFunction->Execute(NULL);
357 if (SM_AreConfigsExecuted())
359 SM_ExecuteForPlugin(GetBaseContext());
363 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
365 if (m_status != Plugin_Created)
370 m_status = Plugin_Loaded;
374 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad");
381 pFunction->PushCell(m_handle);
382 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
383 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
384 pFunction->PushCell(maxlength);
385 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
390 if (!result || m_status != Plugin_Loaded)
398 void *CPlugin::GetPluginStructure()
403 IPluginContext *CPlugin::GetBaseContext()
405 if (m_pRuntime == NULL)
410 return m_pRuntime->GetDefaultContext();
413 sp_context_t *CPlugin::GetContext()
418 const char *CPlugin::GetFilename()
423 PluginType CPlugin::GetType()
428 const sm_plugininfo_t *CPlugin::GetPublicInfo()
433 unsigned int CPlugin::GetSerial()
438 PluginStatus CPlugin::GetStatus()
443 bool CPlugin::IsDebugging()
445 if (m_pRuntime == NULL)
450 return m_pRuntime->IsDebugging();
453 void CPlugin::LibraryActions(bool dropping)
455 List<String>::iterator iter;
456 for (iter = m_Libraries.begin();
457 iter != m_Libraries.end();
460 g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping);
464 bool CPlugin::SetPauseState(bool paused)
466 if (paused && GetStatus() != Plugin_Running)
469 } else if (!paused && GetStatus() != Plugin_Paused) {
475 LibraryActions(true);
478 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange");
482 pFunction->PushCell(paused ? 1 : 0);
483 pFunction->Execute(&result);
488 m_status = Plugin_Paused;
489 m_pRuntime->SetPauseState(true);
491 m_status = Plugin_Running;
492 m_pRuntime->SetPauseState(false);
495 g_PluginSys._SetPauseState(this, paused);
499 LibraryActions(false);
505 IdentityToken_t *CPlugin::GetIdentity()
510 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
518 snprintf(error, maxlength, "Plugin is not runnable.");
523 if (debug && IsDebugging())
527 snprintf(error, maxlength, "Plugin is already in debug mode.");
531 else if (!debug && !IsDebugging())
535 snprintf(error, maxlength, "Plugins is already in production mode.");
540 ICompilation *co = g_pSourcePawn2->StartCompilation();
542 if (!co->SetOption("debug", (debug) ? "1" : "0"))
546 snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
551 if ((err = m_pRuntime->ApplyCompilationOptions(co)) != SP_ERROR_NONE)
555 snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
565 bool CPlugin::IsRunnable()
567 return (m_status <= Plugin_Paused) ? true : false;
570 time_t CPlugin::GetFileTimeStamp()
572 char path[PLATFORM_MAX_PATH];
573 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
574 #ifdef PLATFORM_WINDOWS
576 if (_stat(path, &s) != 0)
577 #elif defined PLATFORM_POSIX
579 if (stat(path, &s) != 0)
590 time_t CPlugin::GetTimeStamp()
595 void CPlugin::SetTimeStamp(time_t t)
600 IPhraseCollection *CPlugin::GetPhrases()
605 void CPlugin::DependencyDropped(CPlugin *pOwner)
612 List<String>::iterator reqlib_iter;
613 List<String>::iterator lib_iter;
614 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
616 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
618 if ((*reqlib_iter) == (*lib_iter))
620 m_LibraryMissing = true;
625 List<NativeEntry *>::iterator iter;
626 NativeEntry *pNative;
629 unsigned int unbound = 0;
631 for (iter = pOwner->m_Natives.begin();
632 iter != pOwner->m_Natives.end();
636 /* Find this native! */
637 if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
642 m_pRuntime->GetNativeByIndex(idx, &native);
644 native->status = SP_NATIVE_UNBOUND;
650 m_FakeNativesMissing = true;
653 /* :IDEA: in the future, add native trapping? */
654 if (m_FakeNativesMissing || m_LibraryMissing)
656 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
660 unsigned int CPlugin::GetConfigCount()
662 return (unsigned int)m_configs.size();
665 AutoConfig *CPlugin::GetConfig(unsigned int i)
667 if (i >= GetConfigCount())
675 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
677 /* Do a check for duplicates to prevent double-execution */
678 for (size_t i = 0; i < m_configs.size(); i++)
680 if (m_configs[i]->autocfg.compare(cfg) == 0
681 && m_configs[i]->folder.compare(folder) == 0
682 && m_configs[i]->create == autoCreate)
688 AutoConfig *c = new AutoConfig;
692 c->create = autoCreate;
694 m_configs.push_back(c);
697 void CPlugin::DropEverything()
700 List<CPlugin *>::iterator iter;
701 List<WeakNative>::iterator wk_iter;
703 /* Tell everyone that depends on us that we're about to drop */
704 for (iter = m_Dependents.begin();
705 iter != m_Dependents.end();
709 pOther->DependencyDropped(this);
712 /* Note: we don't care about things we depend on.
713 * The reason is that extensions have their own cleanup
714 * code for plugins. Although the "right" design would be
715 * to centralize that here, i'm omitting it for now. Thus,
716 * the code below to walk the plugins list will suffice.
719 /* Other plugins could be holding weak references that were
720 * added by us. We need to clean all of those up now.
722 for (iter = g_PluginSys.m_plugins.begin();
723 iter != g_PluginSys.m_plugins.end();
726 (*iter)->DropRefsTo(this);
729 /* Proceed with the rest of the necessities. */
730 CNativeOwner::DropEverything();
733 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
737 if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
742 m_Natives.push_back(pEntry);
751 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
757 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
762 bool CPluginManager::CPluginIterator::MorePlugins()
764 return (current != mylist->end());
767 void CPluginManager::CPluginIterator::NextPlugin()
772 void CPluginManager::CPluginIterator::Release()
774 g_PluginSys.ReleaseIterator(this);
777 CPluginManager::CPluginIterator::~CPluginIterator()
781 void CPluginManager::CPluginIterator::Reset()
783 current = mylist->begin();
790 CPluginManager::CPluginManager()
792 m_LoadLookup = sm_trie_create();
793 m_AllPluginsLoaded = false;
795 m_LoadingLocked = false;
798 CPluginManager::~CPluginManager()
801 * Ignore the fact that there might be plugins in the cache.
802 * This usually means that Core is not being unloaded properly, and everything
803 * will crash anyway. YAY
805 sm_trie_destroy(m_LoadLookup);
807 CStack<CPluginManager::CPluginIterator *>::iterator iter;
808 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
815 void CPluginManager::Shutdown()
817 List<CPlugin *>::iterator iter;
819 while ((iter = m_plugins.begin()) != m_plugins.end())
825 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
827 /* First read in the database of plugin settings */
830 m_AllPluginsLoaded = false;
831 if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
833 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
834 const char *err_msg = textparsers->GetSMCErrorString(err);
837 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
841 LoadPluginsFromDir(basedir, NULL);
844 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
846 char base_path[PLATFORM_MAX_PATH];
848 /* Form the current path to start reading from */
849 if (localpath == NULL)
851 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
853 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
856 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
861 g_LibSys.GetPlatformError(error, sizeof(error));
862 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
863 g_Logger.LogError("[SM] Platform returned error: %s", error);
867 while (dir->MoreFiles())
869 if (dir->IsEntryDirectory()
870 && (strcmp(dir->GetEntryName(), ".") != 0)
871 && (strcmp(dir->GetEntryName(), "..") != 0)
872 && (strcmp(dir->GetEntryName(), "disabled") != 0)
873 && (strcmp(dir->GetEntryName(), "optional") != 0))
875 char new_local[PLATFORM_MAX_PATH];
876 if (localpath == NULL)
878 /* If no path yet, don't add a former slash */
879 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
881 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
883 LoadPluginsFromDir(basedir, new_local);
884 } else if (dir->IsEntryFile()) {
885 const char *name = dir->GetEntryName();
886 size_t len = strlen(name);
888 && strcmp(&name[len-4], ".smx") == 0)
890 /* If the filename matches, load the plugin */
891 char plugin[PLATFORM_MAX_PATH];
892 if (localpath == NULL)
894 snprintf(plugin, sizeof(plugin), "%s", name);
896 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
898 LoadAutoPlugin(plugin);
903 g_LibSys.CloseDirectory(dir);
906 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
910 return LoadRes_NeverLoad;
914 bool no_load = false;
915 PluginSettings *pset;
916 unsigned int setcount = m_PluginInfo.GetSettingsNum();
917 for (unsigned int i=0; i<setcount; i++)
919 if ((pset = m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
923 if (pset->blockload_val)
931 * Does this plugin already exist?
934 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
936 /* Check to see if we should try reloading it */
937 if (pPlugin->GetStatus() == Plugin_BadLoad
938 || pPlugin->GetStatus() == Plugin_Error
939 || pPlugin->GetStatus() == Plugin_Failed
942 UnloadPlugin(pPlugin);
950 return LoadRes_AlreadyLoaded;
956 return LoadRes_NeverLoad;
959 pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
961 assert(pPlugin != NULL);
963 pPlugin->m_type = PluginType_MapUpdated;
965 ICompilation *co = NULL;
967 if (pPlugin->m_status == Plugin_Uncompiled)
969 co = g_pSourcePawn2->StartCompilation();
972 for (unsigned int i=0; i<setcount; i++)
974 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
978 pPlugin->m_type = pset->type_val;
981 for (unsigned int j=0; j<pset->opts_num; j++)
983 const char *key, *val;
984 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
989 if ((err = co->SetOption(key, val)) == SP_ERROR_NONE)
993 snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
1003 /* Do the actual compiling */
1006 char fullpath[PLATFORM_MAX_PATH];
1007 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
1009 pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
1010 if (pPlugin->m_pRuntime == NULL)
1014 "Unable to load plugin (error %d: %s)",
1016 g_pSourcePawn2->GetErrorString(err));
1020 pPlugin->UpdateInfo();
1021 pPlugin->m_status = Plugin_Created;
1025 /* Get the status */
1026 if (pPlugin->GetStatus() == Plugin_Created)
1028 /* First native pass - add anything from Core */
1029 g_ShareSys.BindNativesToPlugin(pPlugin, true);
1030 pPlugin->InitIdentity();
1031 if (pPlugin->Call_AskPluginLoad(error, maxlength))
1033 /* Autoload any modules */
1034 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
1038 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1042 /* Save the time stamp */
1043 time_t t = pPlugin->GetFileTimeStamp();
1044 pPlugin->SetTimeStamp(t);
1051 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
1054 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
1060 if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure)
1066 if (res == LoadRes_AlreadyLoaded)
1072 if (res == LoadRes_NeverLoad)
1074 if (m_LoadingLocked)
1076 UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect");
1080 UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
1087 /* Run second pass if we need to */
1088 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1090 if (!RunSecondPass(pl, error, maxlength))
1095 pl->Call_OnAllPluginsLoaded();
1101 void CPluginManager::LoadAutoPlugin(const char *plugin)
1105 char error[255] = "Unknown error";
1107 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1109 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
1110 pl->SetErrorState(Plugin_Failed, "%s", error);
1113 if (res == LoadRes_Successful)
1119 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1121 List<IPluginsListener *>::iterator iter;
1122 IPluginsListener *pListener;
1124 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1126 pListener = (*iter);
1127 pListener->OnPluginCreated(pPlugin);
1130 m_plugins.push_back(pPlugin);
1131 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
1134 void CPluginManager::LoadAll_SecondPass()
1136 List<CPlugin *>::iterator iter;
1140 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1143 if (pPlugin->GetStatus() == Plugin_Loaded)
1146 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1148 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1149 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1154 m_AllPluginsLoaded = true;
1157 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1166 IPluginContext *pBase = pPlugin->GetBaseContext();
1167 uint32_t num = pBase->GetPubVarsNum();
1168 sp_pubvar_t *pubvar;
1170 char pathfile[PLATFORM_MAX_PATH];
1172 for (uint32_t i=0; i<num; i++)
1174 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1178 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1180 pl = (_pl *)pubvar->offs;
1181 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1185 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1189 g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1190 if (strcmp(pathfile, file) == 0)
1194 if (pl->required == false)
1196 IPluginFunction *pFunc;
1198 UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1199 if ((pFunc=pBase->GetFunctionByName(buffer)))
1202 pFunc->Execute(&res);
1203 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1207 snprintf(error, maxlength, "Fatal error during initializing plugin load");
1215 /* Check that we aren't registering the same library twice */
1216 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1218 pPlugin->m_RequiredLibs.push_back(name);
1224 List<CPlugin *>::iterator iter;
1227 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1230 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1240 snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
1251 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1253 /* Find any extensions this plugin needs */
1262 IPluginContext *pBase = pPlugin->GetBaseContext();
1263 uint32_t num = pBase->GetPubVarsNum();
1264 sp_pubvar_t *pubvar;
1266 char path[PLATFORM_MAX_PATH];
1268 for (uint32_t i=0; i<num; i++)
1270 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1274 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1276 ext = (_ext *)pubvar->offs;
1277 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1281 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1287 /* Attempt to auto-load if necessary */
1290 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1291 g_Extensions.LoadAutoExtension(path);
1296 /* Is this required? */
1299 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1300 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1302 pExt = g_Extensions.FindExtensionByName(name);
1304 /* :TODO: should we bind to unloaded extensions?
1305 * Currently the extension manager will ignore this.
1307 if (!pExt || !pExt->IsRunning(NULL, 0))
1311 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1317 g_Extensions.BindChildPlugin(pExt, pPlugin);
1322 IPluginFunction *pFunc;
1324 UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
1326 if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
1329 pFunc->Execute(&res);
1330 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1334 UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
1347 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1349 /* Second pass for extension requirements */
1350 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1355 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1360 /* Run another binding pass */
1361 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1363 /* Find any unbound natives. Right now, these are not allowed. */
1364 IPluginContext *pContext = pPlugin->GetBaseContext();
1365 uint32_t num = pContext->GetNativesNum();
1366 sp_native_t *native;
1367 for (unsigned int i=0; i<num; i++)
1369 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1373 if (native->status == SP_NATIVE_UNBOUND
1374 && native->name[0] != '@'
1375 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1379 snprintf(error, maxlength, "Native \"%s\" was not found", native->name);
1385 /* Finish by telling all listeners */
1386 List<IPluginsListener *>::iterator iter;
1387 IPluginsListener *pListener;
1388 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1390 pListener = (*iter);
1391 pListener->OnPluginLoaded(pPlugin);
1394 /* Tell this plugin to finish initializing itself */
1395 pPlugin->Call_OnPluginStart();
1397 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1398 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
1400 List<CPlugin *>::iterator pl_iter;
1402 for (pl_iter = m_plugins.begin();
1403 pl_iter != m_plugins.end();
1406 pOther = (*pl_iter);
1407 if ((pOther->GetStatus() == Plugin_Error
1408 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1409 || pOther->m_FakeNativesMissing)
1411 TryRefreshDependencies(pOther);
1413 else if ((pOther->GetStatus() == Plugin_Running
1414 || pOther->GetStatus() == Plugin_Paused)
1415 && pOther != pPlugin)
1417 List<NativeEntry *>::iterator nv_iter;
1418 for (nv_iter = pPlugin->m_Natives.begin();
1419 nv_iter != pPlugin->m_Natives.end();
1422 g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
1428 /* Go through our libraries and tell other plugins they're added */
1429 List<String>::iterator s_iter;
1430 for (s_iter = pPlugin->m_Libraries.begin();
1431 s_iter != pPlugin->m_Libraries.end();
1434 OnLibraryAction((*s_iter).c_str(), true, false);
1437 /* :TODO: optimize? does this even matter? */
1438 pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
1443 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1445 assert(pPlugin->GetBaseContext() != NULL);
1447 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1449 List<String>::iterator lib_iter;
1450 List<String>::iterator req_iter;
1451 List<CPlugin *>::iterator pl_iter;
1453 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1456 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1459 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1461 if ((*req_iter) == (*lib_iter))
1469 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1474 /* Find any unbound natives
1475 * Right now, these are not allowed
1477 IPluginContext *pContext = pPlugin->GetBaseContext();
1478 uint32_t num = pContext->GetNativesNum();
1479 sp_native_t *native;
1480 for (unsigned int i=0; i<num; i++)
1482 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1486 if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
1488 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1493 if (pPlugin->GetStatus() == Plugin_Error)
1495 /* If we got here, all natives are okay again! */
1496 pPlugin->m_status = Plugin_Running;
1497 if (pPlugin->m_pRuntime->IsPaused())
1499 pPlugin->m_pRuntime->SetPauseState(false);
1500 _SetPauseState(pPlugin, false);
1505 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1507 CPlugin *pPlugin = (CPlugin *)plugin;
1509 /* This prevents removal during insertion or anything else weird */
1510 if (m_plugins.find(pPlugin) == m_plugins.end())
1515 IPluginContext *pContext = plugin->GetBaseContext();
1516 if (pContext->IsInExec())
1519 UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
1520 engine->ServerCommand(buffer);
1524 /* Remove us from the lookup table and linked list */
1525 m_plugins.remove(pPlugin);
1526 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
1528 /* Go through our libraries and tell other plugins they're gone */
1529 List<String>::iterator s_iter;
1530 for (s_iter = pPlugin->m_Libraries.begin();
1531 s_iter != pPlugin->m_Libraries.end();
1534 OnLibraryAction((*s_iter).c_str(), true, true);
1537 List<IPluginsListener *>::iterator iter;
1538 IPluginsListener *pListener;
1540 if (pPlugin->GetStatus() <= Plugin_Error)
1542 /* Notify listeners of unloading */
1543 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1545 pListener = (*iter);
1546 pListener->OnPluginUnloaded(pPlugin);
1549 pPlugin->Call_OnPluginEnd();
1552 pPlugin->DropEverything();
1554 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1556 /* Notify listeners of destruction */
1557 pListener = (*iter);
1558 pListener->OnPluginDestroyed(pPlugin);
1561 /* Tell the plugin to delete itself */
1567 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1570 IPluginContext *pContext;
1572 pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
1574 if (pContext->GetKey(2, (void **)&pPlugin))
1582 CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
1584 return (CPlugin *)FindPluginByContext(ctx);
1587 unsigned int CPluginManager::GetPluginCount()
1589 return m_plugins.size();
1592 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1594 m_listeners.push_back(listener);
1597 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1599 m_listeners.remove(listener);
1602 IPluginIterator *CPluginManager::GetPluginIterator()
1604 if (m_iters.empty())
1606 return new CPluginIterator(&m_plugins);
1608 CPluginIterator *iter = m_iters.front();
1615 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1620 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1622 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1623 size_t alias_len = 0;
1624 size_t local_len = 0;
1626 const char *ptr = alias;
1627 unsigned int alias_explicit_paths = 0;
1628 unsigned int alias_path_end = 0;
1629 while (*ptr != '\0')
1631 if (*ptr == '\\' || *ptr == '/')
1633 alias_explicit_paths++;
1634 alias_path_end = alias_len;
1640 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1642 /* Trailing slash is totally invalid here */
1647 unsigned int local_explicit_paths = 0;
1648 unsigned int local_path_end = 0;
1649 while (*ptr != '\0')
1651 if (*ptr == '\\' || *ptr == '/')
1653 local_explicit_paths++;
1654 local_path_end = local_len;
1660 /* If the alias has more explicit paths than the real path,
1661 * no match will be possible.
1663 if (alias_explicit_paths > local_explicit_paths)
1668 if (alias_explicit_paths)
1670 /* We need to find if the paths match now. For example, these should all match:
1672 * csdm optional/csdm
1673 * csdm/ban optional/crab/csdm/ban
1675 const char *aliasptr = alias;
1676 const char *localptr = localpath;
1680 if (*aliasptr != *localptr)
1682 /* We have to knock one path off */
1683 local_explicit_paths--;
1684 if (alias_explicit_paths > local_explicit_paths)
1686 /* Skip out if we're gonna have an impossible match */
1689 /* Eat up localptr tokens until we get a result */
1690 while (((localptr - localpath) < (int)local_path_end)
1692 && *localptr != '\\')
1696 /* Check if we hit the end of our searchable area.
1697 * This probably isn't possible because of the path
1698 * count check, but it's a good idea anyway.
1700 if ((localptr - localpath) >= (int)local_path_end)
1704 /* Consume the slash token */
1707 /* Reset the alias pointer so we can continue consuming */
1713 * This is safe because if localptr terminates early, aliasptr will too
1717 /* We should never reach the end of the string because of this check. */
1718 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1719 bool localend = (localptr - localpath) > (int)local_path_end;
1720 if (aliasend || localend)
1722 if (aliasend && localend)
1724 /* we matched, and we can break out now */
1728 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1733 /* If we got here, it's safe to compare the next two tokens */
1734 if (*localptr != *aliasptr)
1745 /* If we got here, it's time to compare filenames */
1746 const char *aliasptr = alias;
1747 const char *localptr = localpath;
1749 if (alias_explicit_paths)
1751 aliasptr = &alias[alias_path_end + 1];
1754 if (local_explicit_paths)
1756 localptr = &localpath[local_path_end + 1];
1761 if (*aliasptr == '*')
1763 /* First, see if this is the last character */
1764 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1766 /* If so, there's no need to match anything else */
1769 /* Otherwise, we need to search for an appropriate matching sequence in local.
1770 * Note that we only need to search up to the next asterisk.
1774 const char *local_orig = localptr;
1778 while (*aliasptr != '\0' && *aliasptr != '*')
1780 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1781 if (*aliasptr != *localptr)
1791 /* If we didn't get a match, we need to advance the search stream.
1792 * This will let us skip tokens while still searching for another match.
1794 localptr = ++local_orig;
1795 /* Make sure we don't go out of bounds */
1796 if (*localptr == '\0')
1807 /* If we got a match, move on to the next token */
1810 } else if (*aliasptr == '\0') {
1811 if (*localptr == '\0'
1813 strcmp(localptr, ".smx") == 0)
1819 } else if (*aliasptr != *localptr) {
1829 bool CPluginManager::IsLateLoadTime() const
1831 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1834 void CPluginManager::OnSourceModAllInitialized()
1836 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1839 g_HandleSys.InitAccessDefaults(NULL, &sec);
1841 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1842 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1844 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1845 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1847 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1849 g_ShareSys.AddInterface(NULL, this);
1852 void CPluginManager::OnSourceModShutdown()
1854 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1858 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1859 g_ShareSys.DestroyIdentType(g_PluginIdent);
1860 g_ShareSys.DestroyIdentity(m_MyIdent);
1863 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1865 /* We don't care about the internal object, actually */
1868 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1870 *pSize = ((CPlugin *)object)->CalcMemUsage();
1874 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1882 sec.pIdentity = m_MyIdent;
1884 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1897 CPlugin *CPluginManager::GetPluginByOrder(int num)
1899 if (num < 1 || num > (int)GetPluginCount())
1907 SourceHook::List<CPlugin *>::iterator iter;
1908 for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
1915 const char *CPluginManager::GetStatusText(PluginStatus st)
1919 case Plugin_Running:
1925 case Plugin_Uncompiled:
1926 return "Uncompiled";
1927 case Plugin_BadLoad:
1937 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1939 int argcount = command.ArgC();
1942 const char *cmd = command.Arg(2);
1943 if (strcmp(cmd, "list") == 0)
1946 unsigned int id = 1;
1947 int plnum = GetPluginCount();
1951 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
1956 g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1960 SourceHook::List<CPlugin *>::iterator iter;
1961 SourceHook::List<CPlugin *> m_FailList;
1963 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
1966 assert(pl->GetStatus() != Plugin_Created);
1968 const sm_plugininfo_t *info = pl->GetPublicInfo();
1969 if (pl->GetStatus() != Plugin_Running)
1971 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1973 if (pl->GetStatus() <= Plugin_Error)
1975 /* Plugin has failed to load. */
1976 m_FailList.push_back(pl);
1981 len += UTIL_Format(buffer, sizeof(buffer), " %02d", id);
1983 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1984 if (IS_STR_FILLED(info->version))
1986 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1988 if (IS_STR_FILLED(info->author))
1990 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
1992 g_RootMenu.ConsolePrint("%s", buffer);
1995 if (!m_FailList.empty())
1997 g_RootMenu.ConsolePrint("Load Errors:");
1999 SourceHook::List<CPlugin *>::iterator _iter;
2003 for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
2005 pl = (CPlugin *)*_iter;
2006 g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
2012 else if (strcmp(cmd, "load") == 0)
2016 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
2022 const char *filename = command.Arg(3);
2024 char pluginfile[256];
2025 const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
2026 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
2028 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
2032 g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
2038 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
2042 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
2047 else if (strcmp(cmd, "unload") == 0)
2051 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2057 const char *arg = command.Arg(3);
2058 int id = strtol(arg, &end, 10);
2062 pl = GetPluginByOrder(id);
2065 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2071 char pluginfile[256];
2072 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2073 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2075 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2077 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2082 char name[PLATFORM_MAX_PATH];
2083 const sm_plugininfo_t *info = pl->GetPublicInfo();
2084 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2086 if (UnloadPlugin(pl))
2088 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2092 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
2097 else if (strcmp(cmd, "unload_all") == 0)
2099 g_PluginSys.UnloadAll();
2100 g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded.");
2103 else if (strcmp(cmd, "load_lock") == 0)
2105 if (m_LoadingLocked)
2107 g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect.");
2111 m_LoadingLocked = true;
2112 g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
2116 else if (strcmp(cmd, "load_unlock") == 0)
2118 if (m_LoadingLocked)
2120 m_LoadingLocked = false;
2121 g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect.");
2125 g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect.");
2129 else if (strcmp(cmd, "info") == 0)
2133 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
2139 const char *arg = command.Arg(3);
2140 int id = strtol(arg, &end, 10);
2144 pl = GetPluginByOrder(id);
2147 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2153 char pluginfile[256];
2154 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2155 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2157 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2159 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2164 const sm_plugininfo_t *info = pl->GetPublicInfo();
2166 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
2167 if (pl->GetStatus() <= Plugin_Error)
2169 if (IS_STR_FILLED(info->name))
2171 if (IS_STR_FILLED(info->description))
2173 g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description);
2175 g_RootMenu.ConsolePrint(" Title: %s", info->name);
2178 if (IS_STR_FILLED(info->author))
2180 g_RootMenu.ConsolePrint(" Author: %s", info->author);
2182 if (IS_STR_FILLED(info->version))
2184 g_RootMenu.ConsolePrint(" Version: %s", info->version);
2186 if (IS_STR_FILLED(info->url))
2188 g_RootMenu.ConsolePrint(" URL: %s", info->url);
2190 if (pl->GetStatus() == Plugin_Error)
2192 g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg);
2196 if (pl->GetStatus() == Plugin_Running)
2198 if (pl->IsDebugging())
2200 g_RootMenu.ConsolePrint(" Status: running, debugging");
2204 g_RootMenu.ConsolePrint(" Status: running");
2209 g_RootMenu.ConsolePrint(" Status: not running");
2212 const char *typestr = "";
2213 switch (pl->GetType())
2215 case PluginType_MapUpdated:
2216 typestr = "Map Change if Updated";
2218 case PluginType_MapOnly:
2219 typestr = "Map Change";
2221 case PluginType_Private:
2222 case PluginType_Global:
2227 g_RootMenu.ConsolePrint(" Reloads: %s", typestr);
2229 if (pl->m_FileVersion >= 3)
2231 g_RootMenu.ConsolePrint(" Timestamp: %s", pl->m_DateTime);
2236 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
2237 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2238 info->name ? info->name : "<none>",
2239 info->version ? info->version : "<none>");
2240 if (IS_STR_FILLED(info->url))
2242 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
2248 else if (strcmp(cmd, "debug") == 0)
2252 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
2258 const char *arg = command.Arg(3);
2259 int id = strtol(arg, &end, 10);
2263 pl = GetPluginByOrder(id);
2266 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2272 char pluginfile[256];
2273 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2274 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2276 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2278 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2284 const char *mode = command.Arg(4);
2285 if ((res=strcmp("on", mode)) && strcmp("off", mode))
2287 g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
2301 if (debug && pl->IsDebugging())
2303 g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
2306 else if (!debug && !pl->IsDebugging())
2308 g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
2313 if (pl->ToggleDebugMode(debug, error, sizeof(error)))
2315 g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
2320 g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
2321 g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
2325 else if (strcmp(cmd, "refresh") == 0)
2327 g_SourceMod.DoGlobalPluginLoads();
2328 g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
2331 else if (strcmp(cmd, "reload") == 0)
2335 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
2341 const char *arg = command.Arg(3);
2342 int id = strtol(arg, &end, 10);
2346 pl = GetPluginByOrder(id);
2349 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2355 char pluginfile[256];
2356 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2357 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2359 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2361 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2366 char name[PLATFORM_MAX_PATH];
2367 const sm_plugininfo_t *info = pl->GetPublicInfo();
2368 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2370 if (ReloadPlugin(pl))
2372 g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
2376 g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name);
2383 /* Draw the main menu */
2384 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
2385 g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
2386 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
2387 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
2388 g_RootMenu.DrawGenericOption("load", "Load a plugin");
2389 g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
2390 g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading");
2391 g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
2392 g_RootMenu.DrawGenericOption("reload", "Reloads a plugin");
2393 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
2394 g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins");
2397 bool CPluginManager::ReloadPlugin(CPlugin *pl)
2399 List<CPlugin *>::iterator iter;
2400 char filename[PLATFORM_MAX_PATH];
2401 bool debug, wasloaded;
2406 strcpy(filename, pl->m_filename);
2407 debug = pl->IsDebugging();
2408 ptype = pl->GetType();
2410 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
2418 if (!UnloadPlugin(pl))
2422 if (!(newpl=LoadPlugin(filename, debug, ptype, NULL, 0, &wasloaded)))
2427 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2429 if ((*iter) == (CPlugin *)newpl)
2431 m_plugins.erase(iter);
2437 for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
2440 m_plugins.insert(iter, (CPlugin *)newpl);
2445 void CPluginManager::ReloadOrUnloadPlugins()
2447 /* If we're in a load lock, just skip this whole bit. */
2448 if (m_LoadingLocked)
2453 List<CPlugin *>::iterator iter;
2454 List<CPlugin *> tmp_list = m_plugins;
2458 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2461 if (pl->GetType() == PluginType_MapOnly)
2463 UnloadPlugin((IPlugin *)pl);
2465 else if (pl->GetType() == PluginType_MapUpdated)
2467 t = pl->GetFileTimeStamp();
2468 if (!t || t > pl->GetTimeStamp())
2470 pl->SetTimeStamp(t);
2471 UnloadPlugin((IPlugin *)pl);
2477 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2479 List<IPluginsListener *>::iterator iter;
2480 IPluginsListener *pListener;
2481 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2483 pListener = (*iter);
2484 pListener->OnPluginPauseChange(pl, paused);
2488 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2490 List<CPlugin *>::iterator iter;
2492 IPluginFunction *pFunction;
2494 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2498 if (pPlugin->GetStatus() <= Plugin_Paused)
2500 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2504 pForward->AddFunction(pFunction);
2510 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2512 if (pToken->type != g_PluginIdent)
2517 return (CPlugin *)(pToken->ptr);
2520 void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop)
2522 List<CPlugin *>::iterator iter;
2539 const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
2541 for (iter=m_plugins.begin();
2542 iter!=m_plugins.end();
2545 CPlugin *pl = (*iter);
2546 if (pl->GetStatus() != Plugin_Running)
2550 IPluginContext *pContext = pl->GetBaseContext();
2551 IPluginFunction *pf = pContext->GetFunctionByName(name);
2556 uint32_t num_vars = pContext->GetPubVarsNum();
2557 for (uint32_t i=0; i<num_vars; i++)
2559 sp_pubvar_t *pubvar;
2560 if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
2564 if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0)
2566 plc = (_pl *)pubvar->offs;
2572 pContext->LocalToString(plc->name, &str);
2573 if (strcmp(str, lib) != 0)
2577 pf->PushString(lib);
2580 else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0)
2582 ext = (_ext *)pubvar->offs;
2588 pContext->LocalToString(ext->name, &str);
2589 if (strcmp(str, lib) != 0)
2593 pf->PushString(lib);
2600 bool CPluginManager::LibraryExists(const char *lib)
2602 List<CPlugin *>::iterator iter;
2604 for (iter=m_plugins.begin();
2605 iter!=m_plugins.end();
2608 CPlugin *pl = (*iter);
2609 if (pl->GetStatus() != Plugin_Running)
2613 List<String>::iterator s_iter;
2614 for (s_iter = pl->m_Libraries.begin();
2615 s_iter != pl->m_Libraries.end();
2618 if ((*s_iter).compare(lib) == 0)
2628 void CPluginManager::AllPluginsLoaded()
2630 List<CPlugin *>::iterator iter;
2633 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2636 pl->Call_OnAllPluginsLoaded();
2640 void CPluginManager::UnloadAll()
2642 List<CPlugin *>::iterator iter;
2643 while ( (iter = m_plugins.begin()) != m_plugins.end() )
2645 UnloadPlugin((*iter));
2649 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
2652 List<CPlugin *>::iterator iter;
2654 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2665 CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
2671 id = strtol(arg, &end, 10);
2675 pl = GetPluginByOrder(id);
2683 char pluginfile[256];
2684 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2685 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2687 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))