Landed sourcepawn-1.2. The big changes:
1) JIT compilation/optimization now occurs per-function, and only when functions are first used. We're now officially a whole-method JIT rather than an AOT compiler (albiet, still a simple JIT). This has two implications: Functions are now much better abstracted internally, and loading a plugin is now much less expensive. If a function contains calls to other functions, THOSE functions are only compiled when they're invoked as well.
2) I've removed debug mode. We always show full backtraces now, as there was a very cheap way to implement this which really cleaned up everything. This is great for a number of reasons -- there's less code, the JIT is better designed, we don't need to relocate debug tables, and best of all we no longer have to tell users to enable debug mode at their own expense.
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)
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::IsRunnable()
512 return (m_status <= Plugin_Paused) ? true : false;
515 time_t CPlugin::GetFileTimeStamp()
517 char path[PLATFORM_MAX_PATH];
518 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
519 #ifdef PLATFORM_WINDOWS
521 if (_stat(path, &s) != 0)
522 #elif defined PLATFORM_POSIX
524 if (stat(path, &s) != 0)
535 time_t CPlugin::GetTimeStamp()
540 void CPlugin::SetTimeStamp(time_t t)
545 IPhraseCollection *CPlugin::GetPhrases()
550 void CPlugin::DependencyDropped(CPlugin *pOwner)
557 List<String>::iterator reqlib_iter;
558 List<String>::iterator lib_iter;
559 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
561 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
563 if ((*reqlib_iter) == (*lib_iter))
565 m_LibraryMissing = true;
570 List<NativeEntry *>::iterator iter;
571 NativeEntry *pNative;
574 unsigned int unbound = 0;
576 for (iter = pOwner->m_Natives.begin();
577 iter != pOwner->m_Natives.end();
581 /* Find this native! */
582 if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
587 m_pRuntime->GetNativeByIndex(idx, &native);
589 native->status = SP_NATIVE_UNBOUND;
595 m_FakeNativesMissing = true;
598 /* :IDEA: in the future, add native trapping? */
599 if (m_FakeNativesMissing || m_LibraryMissing)
601 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
605 unsigned int CPlugin::GetConfigCount()
607 return (unsigned int)m_configs.size();
610 AutoConfig *CPlugin::GetConfig(unsigned int i)
612 if (i >= GetConfigCount())
620 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
622 /* Do a check for duplicates to prevent double-execution */
623 for (size_t i = 0; i < m_configs.size(); i++)
625 if (m_configs[i]->autocfg.compare(cfg) == 0
626 && m_configs[i]->folder.compare(folder) == 0
627 && m_configs[i]->create == autoCreate)
633 AutoConfig *c = new AutoConfig;
637 c->create = autoCreate;
639 m_configs.push_back(c);
642 void CPlugin::DropEverything()
645 List<CPlugin *>::iterator iter;
646 List<WeakNative>::iterator wk_iter;
648 /* Tell everyone that depends on us that we're about to drop */
649 for (iter = m_Dependents.begin();
650 iter != m_Dependents.end();
654 pOther->DependencyDropped(this);
657 /* Note: we don't care about things we depend on.
658 * The reason is that extensions have their own cleanup
659 * code for plugins. Although the "right" design would be
660 * to centralize that here, i'm omitting it for now. Thus,
661 * the code below to walk the plugins list will suffice.
664 /* Other plugins could be holding weak references that were
665 * added by us. We need to clean all of those up now.
667 for (iter = g_PluginSys.m_plugins.begin();
668 iter != g_PluginSys.m_plugins.end();
671 (*iter)->DropRefsTo(this);
674 /* Proceed with the rest of the necessities. */
675 CNativeOwner::DropEverything();
678 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
682 if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
687 m_Natives.push_back(pEntry);
696 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
702 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
707 bool CPluginManager::CPluginIterator::MorePlugins()
709 return (current != mylist->end());
712 void CPluginManager::CPluginIterator::NextPlugin()
717 void CPluginManager::CPluginIterator::Release()
719 g_PluginSys.ReleaseIterator(this);
722 CPluginManager::CPluginIterator::~CPluginIterator()
726 void CPluginManager::CPluginIterator::Reset()
728 current = mylist->begin();
735 CPluginManager::CPluginManager()
737 m_LoadLookup = sm_trie_create();
738 m_AllPluginsLoaded = false;
740 m_LoadingLocked = false;
743 CPluginManager::~CPluginManager()
746 * Ignore the fact that there might be plugins in the cache.
747 * This usually means that Core is not being unloaded properly, and everything
748 * will crash anyway. YAY
750 sm_trie_destroy(m_LoadLookup);
752 CStack<CPluginManager::CPluginIterator *>::iterator iter;
753 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
760 void CPluginManager::Shutdown()
762 List<CPlugin *>::iterator iter;
764 while ((iter = m_plugins.begin()) != m_plugins.end())
770 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
772 /* First read in the database of plugin settings */
775 m_AllPluginsLoaded = false;
776 if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
778 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
779 const char *err_msg = textparsers->GetSMCErrorString(err);
782 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
786 LoadPluginsFromDir(basedir, NULL);
789 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
791 char base_path[PLATFORM_MAX_PATH];
793 /* Form the current path to start reading from */
794 if (localpath == NULL)
796 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
798 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
801 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
806 g_LibSys.GetPlatformError(error, sizeof(error));
807 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
808 g_Logger.LogError("[SM] Platform returned error: %s", error);
812 while (dir->MoreFiles())
814 if (dir->IsEntryDirectory()
815 && (strcmp(dir->GetEntryName(), ".") != 0)
816 && (strcmp(dir->GetEntryName(), "..") != 0)
817 && (strcmp(dir->GetEntryName(), "disabled") != 0)
818 && (strcmp(dir->GetEntryName(), "optional") != 0))
820 char new_local[PLATFORM_MAX_PATH];
821 if (localpath == NULL)
823 /* If no path yet, don't add a former slash */
824 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
826 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
828 LoadPluginsFromDir(basedir, new_local);
829 } else if (dir->IsEntryFile()) {
830 const char *name = dir->GetEntryName();
831 size_t len = strlen(name);
833 && strcmp(&name[len-4], ".smx") == 0)
835 /* If the filename matches, load the plugin */
836 char plugin[PLATFORM_MAX_PATH];
837 if (localpath == NULL)
839 snprintf(plugin, sizeof(plugin), "%s", name);
841 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
843 LoadAutoPlugin(plugin);
848 g_LibSys.CloseDirectory(dir);
851 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
855 return LoadRes_NeverLoad;
859 bool no_load = false;
860 PluginSettings *pset;
861 unsigned int setcount = m_PluginInfo.GetSettingsNum();
862 for (unsigned int i=0; i<setcount; i++)
864 if ((pset = m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
868 if (pset->blockload_val)
876 * Does this plugin already exist?
879 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
881 /* Check to see if we should try reloading it */
882 if (pPlugin->GetStatus() == Plugin_BadLoad
883 || pPlugin->GetStatus() == Plugin_Error
884 || pPlugin->GetStatus() == Plugin_Failed
887 UnloadPlugin(pPlugin);
895 return LoadRes_AlreadyLoaded;
901 return LoadRes_NeverLoad;
904 pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
906 assert(pPlugin != NULL);
908 pPlugin->m_type = PluginType_MapUpdated;
910 ICompilation *co = NULL;
912 if (pPlugin->m_status == Plugin_Uncompiled)
914 co = g_pSourcePawn2->StartCompilation();
917 for (unsigned int i=0; i<setcount; i++)
919 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
923 pPlugin->m_type = pset->type_val;
926 for (unsigned int j=0; j<pset->opts_num; j++)
928 const char *key, *val;
929 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
934 if ((err = co->SetOption(key, val)) == SP_ERROR_NONE)
938 snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
948 /* Do the actual compiling */
951 char fullpath[PLATFORM_MAX_PATH];
952 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
954 pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
955 if (pPlugin->m_pRuntime == NULL)
959 "Unable to load plugin (error %d: %s)",
961 g_pSourcePawn2->GetErrorString(err));
965 pPlugin->UpdateInfo();
966 pPlugin->m_status = Plugin_Created;
971 if (pPlugin->GetStatus() == Plugin_Created)
973 /* First native pass - add anything from Core */
974 g_ShareSys.BindNativesToPlugin(pPlugin, true);
975 pPlugin->InitIdentity();
976 if (pPlugin->Call_AskPluginLoad(error, maxlength))
978 /* Autoload any modules */
979 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
983 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
987 /* Save the time stamp */
988 time_t t = pPlugin->GetFileTimeStamp();
989 pPlugin->SetTimeStamp(t);
996 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
999 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
1005 if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure)
1011 if (res == LoadRes_AlreadyLoaded)
1017 if (res == LoadRes_NeverLoad)
1019 if (m_LoadingLocked)
1021 UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect");
1025 UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
1032 /* Run second pass if we need to */
1033 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1035 if (!RunSecondPass(pl, error, maxlength))
1040 pl->Call_OnAllPluginsLoaded();
1046 void CPluginManager::LoadAutoPlugin(const char *plugin)
1050 char error[255] = "Unknown error";
1052 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1054 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
1055 pl->SetErrorState(Plugin_Failed, "%s", error);
1058 if (res == LoadRes_Successful)
1064 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1066 List<IPluginsListener *>::iterator iter;
1067 IPluginsListener *pListener;
1069 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1071 pListener = (*iter);
1072 pListener->OnPluginCreated(pPlugin);
1075 m_plugins.push_back(pPlugin);
1076 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
1079 void CPluginManager::LoadAll_SecondPass()
1081 List<CPlugin *>::iterator iter;
1085 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1088 if (pPlugin->GetStatus() == Plugin_Loaded)
1091 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1093 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1094 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1099 m_AllPluginsLoaded = true;
1102 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1111 IPluginContext *pBase = pPlugin->GetBaseContext();
1112 uint32_t num = pBase->GetPubVarsNum();
1113 sp_pubvar_t *pubvar;
1115 char pathfile[PLATFORM_MAX_PATH];
1117 for (uint32_t i=0; i<num; i++)
1119 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1123 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1125 pl = (_pl *)pubvar->offs;
1126 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1130 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1134 g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1135 if (strcmp(pathfile, file) == 0)
1139 if (pl->required == false)
1141 IPluginFunction *pFunc;
1143 UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1144 if ((pFunc=pBase->GetFunctionByName(buffer)))
1147 pFunc->Execute(&res);
1148 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1152 snprintf(error, maxlength, "Fatal error during initializing plugin load");
1160 /* Check that we aren't registering the same library twice */
1161 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1163 pPlugin->m_RequiredLibs.push_back(name);
1169 List<CPlugin *>::iterator iter;
1172 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1175 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1185 snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
1196 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1198 /* Find any extensions this plugin needs */
1207 IPluginContext *pBase = pPlugin->GetBaseContext();
1208 uint32_t num = pBase->GetPubVarsNum();
1209 sp_pubvar_t *pubvar;
1211 char path[PLATFORM_MAX_PATH];
1213 for (uint32_t i=0; i<num; i++)
1215 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1219 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1221 ext = (_ext *)pubvar->offs;
1222 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1226 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1232 /* Attempt to auto-load if necessary */
1235 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1236 g_Extensions.LoadAutoExtension(path);
1241 /* Is this required? */
1244 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1245 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1247 pExt = g_Extensions.FindExtensionByName(name);
1249 /* :TODO: should we bind to unloaded extensions?
1250 * Currently the extension manager will ignore this.
1252 if (!pExt || !pExt->IsRunning(NULL, 0))
1256 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1262 g_Extensions.BindChildPlugin(pExt, pPlugin);
1267 IPluginFunction *pFunc;
1269 UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
1271 if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
1274 pFunc->Execute(&res);
1275 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1279 UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
1292 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1294 /* Second pass for extension requirements */
1295 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1300 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1305 /* Run another binding pass */
1306 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1308 /* Find any unbound natives. Right now, these are not allowed. */
1309 IPluginContext *pContext = pPlugin->GetBaseContext();
1310 uint32_t num = pContext->GetNativesNum();
1311 sp_native_t *native;
1312 for (unsigned int i=0; i<num; i++)
1314 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1318 if (native->status == SP_NATIVE_UNBOUND
1319 && native->name[0] != '@'
1320 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1324 snprintf(error, maxlength, "Native \"%s\" was not found", native->name);
1330 /* Finish by telling all listeners */
1331 List<IPluginsListener *>::iterator iter;
1332 IPluginsListener *pListener;
1333 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1335 pListener = (*iter);
1336 pListener->OnPluginLoaded(pPlugin);
1339 /* Tell this plugin to finish initializing itself */
1340 pPlugin->Call_OnPluginStart();
1342 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1343 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
1345 List<CPlugin *>::iterator pl_iter;
1347 for (pl_iter = m_plugins.begin();
1348 pl_iter != m_plugins.end();
1351 pOther = (*pl_iter);
1352 if ((pOther->GetStatus() == Plugin_Error
1353 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1354 || pOther->m_FakeNativesMissing)
1356 TryRefreshDependencies(pOther);
1358 else if ((pOther->GetStatus() == Plugin_Running
1359 || pOther->GetStatus() == Plugin_Paused)
1360 && pOther != pPlugin)
1362 List<NativeEntry *>::iterator nv_iter;
1363 for (nv_iter = pPlugin->m_Natives.begin();
1364 nv_iter != pPlugin->m_Natives.end();
1367 g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
1373 /* Go through our libraries and tell other plugins they're added */
1374 List<String>::iterator s_iter;
1375 for (s_iter = pPlugin->m_Libraries.begin();
1376 s_iter != pPlugin->m_Libraries.end();
1379 OnLibraryAction((*s_iter).c_str(), true, false);
1382 /* :TODO: optimize? does this even matter? */
1383 pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
1388 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1390 assert(pPlugin->GetBaseContext() != NULL);
1392 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1394 List<String>::iterator lib_iter;
1395 List<String>::iterator req_iter;
1396 List<CPlugin *>::iterator pl_iter;
1398 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1401 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1404 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1406 if ((*req_iter) == (*lib_iter))
1414 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1419 /* Find any unbound natives
1420 * Right now, these are not allowed
1422 IPluginContext *pContext = pPlugin->GetBaseContext();
1423 uint32_t num = pContext->GetNativesNum();
1424 sp_native_t *native;
1425 for (unsigned int i=0; i<num; i++)
1427 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1431 if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
1433 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1438 if (pPlugin->GetStatus() == Plugin_Error)
1440 /* If we got here, all natives are okay again! */
1441 pPlugin->m_status = Plugin_Running;
1442 if (pPlugin->m_pRuntime->IsPaused())
1444 pPlugin->m_pRuntime->SetPauseState(false);
1445 _SetPauseState(pPlugin, false);
1450 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1452 CPlugin *pPlugin = (CPlugin *)plugin;
1454 /* This prevents removal during insertion or anything else weird */
1455 if (m_plugins.find(pPlugin) == m_plugins.end())
1460 IPluginContext *pContext = plugin->GetBaseContext();
1461 if (pContext->IsInExec())
1464 UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
1465 engine->ServerCommand(buffer);
1469 /* Remove us from the lookup table and linked list */
1470 m_plugins.remove(pPlugin);
1471 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
1473 /* Go through our libraries and tell other plugins they're gone */
1474 List<String>::iterator s_iter;
1475 for (s_iter = pPlugin->m_Libraries.begin();
1476 s_iter != pPlugin->m_Libraries.end();
1479 OnLibraryAction((*s_iter).c_str(), true, true);
1482 List<IPluginsListener *>::iterator iter;
1483 IPluginsListener *pListener;
1485 if (pPlugin->GetStatus() <= Plugin_Error)
1487 /* Notify listeners of unloading */
1488 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1490 pListener = (*iter);
1491 pListener->OnPluginUnloaded(pPlugin);
1494 pPlugin->Call_OnPluginEnd();
1497 pPlugin->DropEverything();
1499 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1501 /* Notify listeners of destruction */
1502 pListener = (*iter);
1503 pListener->OnPluginDestroyed(pPlugin);
1506 /* Tell the plugin to delete itself */
1512 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1515 IPluginContext *pContext;
1517 pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
1519 if (pContext->GetKey(2, (void **)&pPlugin))
1527 CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
1529 return (CPlugin *)FindPluginByContext(ctx);
1532 unsigned int CPluginManager::GetPluginCount()
1534 return m_plugins.size();
1537 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1539 m_listeners.push_back(listener);
1542 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1544 m_listeners.remove(listener);
1547 IPluginIterator *CPluginManager::GetPluginIterator()
1549 if (m_iters.empty())
1551 return new CPluginIterator(&m_plugins);
1553 CPluginIterator *iter = m_iters.front();
1560 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1565 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1567 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1568 size_t alias_len = 0;
1569 size_t local_len = 0;
1571 const char *ptr = alias;
1572 unsigned int alias_explicit_paths = 0;
1573 unsigned int alias_path_end = 0;
1574 while (*ptr != '\0')
1576 if (*ptr == '\\' || *ptr == '/')
1578 alias_explicit_paths++;
1579 alias_path_end = alias_len;
1585 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1587 /* Trailing slash is totally invalid here */
1592 unsigned int local_explicit_paths = 0;
1593 unsigned int local_path_end = 0;
1594 while (*ptr != '\0')
1596 if (*ptr == '\\' || *ptr == '/')
1598 local_explicit_paths++;
1599 local_path_end = local_len;
1605 /* If the alias has more explicit paths than the real path,
1606 * no match will be possible.
1608 if (alias_explicit_paths > local_explicit_paths)
1613 if (alias_explicit_paths)
1615 /* We need to find if the paths match now. For example, these should all match:
1617 * csdm optional/csdm
1618 * csdm/ban optional/crab/csdm/ban
1620 const char *aliasptr = alias;
1621 const char *localptr = localpath;
1625 if (*aliasptr != *localptr)
1627 /* We have to knock one path off */
1628 local_explicit_paths--;
1629 if (alias_explicit_paths > local_explicit_paths)
1631 /* Skip out if we're gonna have an impossible match */
1634 /* Eat up localptr tokens until we get a result */
1635 while (((localptr - localpath) < (int)local_path_end)
1637 && *localptr != '\\')
1641 /* Check if we hit the end of our searchable area.
1642 * This probably isn't possible because of the path
1643 * count check, but it's a good idea anyway.
1645 if ((localptr - localpath) >= (int)local_path_end)
1649 /* Consume the slash token */
1652 /* Reset the alias pointer so we can continue consuming */
1658 * This is safe because if localptr terminates early, aliasptr will too
1662 /* We should never reach the end of the string because of this check. */
1663 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1664 bool localend = (localptr - localpath) > (int)local_path_end;
1665 if (aliasend || localend)
1667 if (aliasend && localend)
1669 /* we matched, and we can break out now */
1673 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1678 /* If we got here, it's safe to compare the next two tokens */
1679 if (*localptr != *aliasptr)
1690 /* If we got here, it's time to compare filenames */
1691 const char *aliasptr = alias;
1692 const char *localptr = localpath;
1694 if (alias_explicit_paths)
1696 aliasptr = &alias[alias_path_end + 1];
1699 if (local_explicit_paths)
1701 localptr = &localpath[local_path_end + 1];
1706 if (*aliasptr == '*')
1708 /* First, see if this is the last character */
1709 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1711 /* If so, there's no need to match anything else */
1714 /* Otherwise, we need to search for an appropriate matching sequence in local.
1715 * Note that we only need to search up to the next asterisk.
1719 const char *local_orig = localptr;
1723 while (*aliasptr != '\0' && *aliasptr != '*')
1725 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1726 if (*aliasptr != *localptr)
1736 /* If we didn't get a match, we need to advance the search stream.
1737 * This will let us skip tokens while still searching for another match.
1739 localptr = ++local_orig;
1740 /* Make sure we don't go out of bounds */
1741 if (*localptr == '\0')
1752 /* If we got a match, move on to the next token */
1755 } else if (*aliasptr == '\0') {
1756 if (*localptr == '\0'
1758 strcmp(localptr, ".smx") == 0)
1764 } else if (*aliasptr != *localptr) {
1774 bool CPluginManager::IsLateLoadTime() const
1776 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1779 void CPluginManager::OnSourceModAllInitialized()
1781 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1784 g_HandleSys.InitAccessDefaults(NULL, &sec);
1786 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1787 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1789 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1790 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1792 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1794 g_ShareSys.AddInterface(NULL, this);
1797 void CPluginManager::OnSourceModShutdown()
1799 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1803 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1804 g_ShareSys.DestroyIdentType(g_PluginIdent);
1805 g_ShareSys.DestroyIdentity(m_MyIdent);
1808 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1810 /* We don't care about the internal object, actually */
1813 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1815 *pSize = ((CPlugin *)object)->CalcMemUsage();
1819 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1827 sec.pIdentity = m_MyIdent;
1829 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1842 CPlugin *CPluginManager::GetPluginByOrder(int num)
1844 if (num < 1 || num > (int)GetPluginCount())
1852 SourceHook::List<CPlugin *>::iterator iter;
1853 for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
1860 const char *CPluginManager::GetStatusText(PluginStatus st)
1864 case Plugin_Running:
1870 case Plugin_Uncompiled:
1871 return "Uncompiled";
1872 case Plugin_BadLoad:
1882 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1884 int argcount = command.ArgC();
1887 const char *cmd = command.Arg(2);
1888 if (strcmp(cmd, "list") == 0)
1891 unsigned int id = 1;
1892 int plnum = GetPluginCount();
1896 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
1901 g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1905 SourceHook::List<CPlugin *>::iterator iter;
1906 SourceHook::List<CPlugin *> m_FailList;
1908 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
1911 assert(pl->GetStatus() != Plugin_Created);
1913 const sm_plugininfo_t *info = pl->GetPublicInfo();
1914 if (pl->GetStatus() != Plugin_Running)
1916 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1918 if (pl->GetStatus() <= Plugin_Error)
1920 /* Plugin has failed to load. */
1921 m_FailList.push_back(pl);
1926 len += UTIL_Format(buffer, sizeof(buffer), " %02d", id);
1928 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1929 if (IS_STR_FILLED(info->version))
1931 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1933 if (IS_STR_FILLED(info->author))
1935 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
1937 g_RootMenu.ConsolePrint("%s", buffer);
1940 if (!m_FailList.empty())
1942 g_RootMenu.ConsolePrint("Load Errors:");
1944 SourceHook::List<CPlugin *>::iterator _iter;
1948 for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
1950 pl = (CPlugin *)*_iter;
1951 g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
1957 else if (strcmp(cmd, "load") == 0)
1961 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
1967 const char *filename = command.Arg(3);
1969 char pluginfile[256];
1970 const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
1971 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
1973 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
1977 g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
1983 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
1987 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
1992 else if (strcmp(cmd, "unload") == 0)
1996 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2002 const char *arg = command.Arg(3);
2003 int id = strtol(arg, &end, 10);
2007 pl = GetPluginByOrder(id);
2010 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2016 char pluginfile[256];
2017 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2018 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2020 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2022 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2027 char name[PLATFORM_MAX_PATH];
2028 const sm_plugininfo_t *info = pl->GetPublicInfo();
2029 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2031 if (UnloadPlugin(pl))
2033 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2037 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
2042 else if (strcmp(cmd, "unload_all") == 0)
2044 g_PluginSys.UnloadAll();
2045 g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded.");
2048 else if (strcmp(cmd, "load_lock") == 0)
2050 if (m_LoadingLocked)
2052 g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect.");
2056 m_LoadingLocked = true;
2057 g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
2061 else if (strcmp(cmd, "load_unlock") == 0)
2063 if (m_LoadingLocked)
2065 m_LoadingLocked = false;
2066 g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect.");
2070 g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect.");
2074 else if (strcmp(cmd, "info") == 0)
2078 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
2084 const char *arg = command.Arg(3);
2085 int id = strtol(arg, &end, 10);
2089 pl = GetPluginByOrder(id);
2092 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2098 char pluginfile[256];
2099 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2100 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2102 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2104 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2109 const sm_plugininfo_t *info = pl->GetPublicInfo();
2111 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
2112 if (pl->GetStatus() <= Plugin_Error)
2114 if (IS_STR_FILLED(info->name))
2116 if (IS_STR_FILLED(info->description))
2118 g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description);
2120 g_RootMenu.ConsolePrint(" Title: %s", info->name);
2123 if (IS_STR_FILLED(info->author))
2125 g_RootMenu.ConsolePrint(" Author: %s", info->author);
2127 if (IS_STR_FILLED(info->version))
2129 g_RootMenu.ConsolePrint(" Version: %s", info->version);
2131 if (IS_STR_FILLED(info->url))
2133 g_RootMenu.ConsolePrint(" URL: %s", info->url);
2135 if (pl->GetStatus() == Plugin_Error)
2137 g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg);
2141 if (pl->GetStatus() == Plugin_Running)
2143 g_RootMenu.ConsolePrint(" Status: running");
2147 g_RootMenu.ConsolePrint(" Status: not running");
2150 const char *typestr = "";
2151 switch (pl->GetType())
2153 case PluginType_MapUpdated:
2154 typestr = "Map Change if Updated";
2156 case PluginType_MapOnly:
2157 typestr = "Map Change";
2159 case PluginType_Private:
2160 case PluginType_Global:
2165 g_RootMenu.ConsolePrint(" Reloads: %s", typestr);
2167 if (pl->m_FileVersion >= 3)
2169 g_RootMenu.ConsolePrint(" Timestamp: %s", pl->m_DateTime);
2174 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
2175 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2176 info->name ? info->name : "<none>",
2177 info->version ? info->version : "<none>");
2178 if (IS_STR_FILLED(info->url))
2180 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
2186 else if (strcmp(cmd, "refresh") == 0)
2188 g_SourceMod.DoGlobalPluginLoads();
2189 g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
2192 else if (strcmp(cmd, "reload") == 0)
2196 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
2202 const char *arg = command.Arg(3);
2203 int id = strtol(arg, &end, 10);
2207 pl = GetPluginByOrder(id);
2210 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2216 char pluginfile[256];
2217 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2218 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2220 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2222 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2227 char name[PLATFORM_MAX_PATH];
2228 const sm_plugininfo_t *info = pl->GetPublicInfo();
2229 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2231 if (ReloadPlugin(pl))
2233 g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
2237 g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name);
2244 /* Draw the main menu */
2245 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
2246 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
2247 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
2248 g_RootMenu.DrawGenericOption("load", "Load a plugin");
2249 g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
2250 g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading");
2251 g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
2252 g_RootMenu.DrawGenericOption("reload", "Reloads a plugin");
2253 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
2254 g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins");
2257 bool CPluginManager::ReloadPlugin(CPlugin *pl)
2259 List<CPlugin *>::iterator iter;
2260 char filename[PLATFORM_MAX_PATH];
2266 strcpy(filename, pl->m_filename);
2267 ptype = pl->GetType();
2269 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
2277 if (!UnloadPlugin(pl))
2281 if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded)))
2286 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2288 if ((*iter) == (CPlugin *)newpl)
2290 m_plugins.erase(iter);
2296 for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
2299 m_plugins.insert(iter, (CPlugin *)newpl);
2304 void CPluginManager::ReloadOrUnloadPlugins()
2306 /* If we're in a load lock, just skip this whole bit. */
2307 if (m_LoadingLocked)
2312 List<CPlugin *>::iterator iter;
2313 List<CPlugin *> tmp_list = m_plugins;
2317 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2320 if (pl->GetType() == PluginType_MapOnly)
2322 UnloadPlugin((IPlugin *)pl);
2324 else if (pl->GetType() == PluginType_MapUpdated)
2326 t = pl->GetFileTimeStamp();
2327 if (!t || t > pl->GetTimeStamp())
2329 pl->SetTimeStamp(t);
2330 UnloadPlugin((IPlugin *)pl);
2336 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2338 List<IPluginsListener *>::iterator iter;
2339 IPluginsListener *pListener;
2340 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2342 pListener = (*iter);
2343 pListener->OnPluginPauseChange(pl, paused);
2347 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2349 List<CPlugin *>::iterator iter;
2351 IPluginFunction *pFunction;
2353 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2357 if (pPlugin->GetStatus() <= Plugin_Paused)
2359 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2363 pForward->AddFunction(pFunction);
2369 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2371 if (pToken->type != g_PluginIdent)
2376 return (CPlugin *)(pToken->ptr);
2379 void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop)
2381 List<CPlugin *>::iterator iter;
2398 const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
2400 for (iter=m_plugins.begin();
2401 iter!=m_plugins.end();
2404 CPlugin *pl = (*iter);
2405 if (pl->GetStatus() != Plugin_Running)
2409 IPluginContext *pContext = pl->GetBaseContext();
2410 IPluginFunction *pf = pContext->GetFunctionByName(name);
2415 uint32_t num_vars = pContext->GetPubVarsNum();
2416 for (uint32_t i=0; i<num_vars; i++)
2418 sp_pubvar_t *pubvar;
2419 if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
2423 if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0)
2425 plc = (_pl *)pubvar->offs;
2431 pContext->LocalToString(plc->name, &str);
2432 if (strcmp(str, lib) != 0)
2436 pf->PushString(lib);
2439 else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0)
2441 ext = (_ext *)pubvar->offs;
2447 pContext->LocalToString(ext->name, &str);
2448 if (strcmp(str, lib) != 0)
2452 pf->PushString(lib);
2459 bool CPluginManager::LibraryExists(const char *lib)
2461 List<CPlugin *>::iterator iter;
2463 for (iter=m_plugins.begin();
2464 iter!=m_plugins.end();
2467 CPlugin *pl = (*iter);
2468 if (pl->GetStatus() != Plugin_Running)
2472 List<String>::iterator s_iter;
2473 for (s_iter = pl->m_Libraries.begin();
2474 s_iter != pl->m_Libraries.end();
2477 if ((*s_iter).compare(lib) == 0)
2487 void CPluginManager::AllPluginsLoaded()
2489 List<CPlugin *>::iterator iter;
2492 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2495 pl->Call_OnAllPluginsLoaded();
2499 void CPluginManager::UnloadAll()
2501 List<CPlugin *>::iterator iter;
2502 while ( (iter = m_plugins.begin()) != m_plugins.end() )
2504 UnloadPlugin((*iter));
2508 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
2511 List<CPlugin *>::iterator iter;
2513 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2524 CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
2530 id = strtol(arg, &end, 10);
2534 pl = GetPluginByOrder(id);
2542 char pluginfile[256];
2543 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2544 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2546 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))