Fix build, again.
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>.
34 #include "PluginSys.h"
36 #include <ILibrarySys.h>
37 #include <ISourceMod.h>
38 #include <IHandleSys.h>
39 #include <IForwardSys.h>
40 #include <IPlayerHelpers.h>
41 #include "ExtensionSys.h"
42 #include "GameConfigs.h"
43 #include "common_logic.h"
44 #include "Translator.h"
46 CPluginManager g_PluginSys;
47 HandleType_t g_PluginType = 0;
48 IdentityType_t g_PluginIdent = 0;
50 CPlugin::CPlugin(const char *file)
52 static int MySerial = 0;
54 m_type = PluginType_Private;
55 m_status = Plugin_Uncompiled;
56 m_bSilentlyFailed = false;
57 m_serial = ++MySerial;
59 m_errormsg[sizeof(m_errormsg) - 1] = '\0';
60 smcore.Format(m_filename, sizeof(m_filename), "%s", file);
63 m_FakeNativesMissing = false;
64 m_LibraryMissing = false;
65 m_bGotAllLoaded = false;
66 m_pPhrases = g_Translator.CreatePhraseCollection();
67 m_MaxClientsVar = NULL;
75 sec.pOwner = g_PluginSys.GetIdentity();
76 sec.pIdentity = sec.pOwner;
78 handlesys->FreeHandle(m_handle, &sec);
79 g_ShareSys.DestroyIdentity(m_ident);
82 if (m_pRuntime != NULL)
88 for (size_t i=0; i<m_configs.size(); i++)
93 if (m_pPhrases != NULL)
95 m_pPhrases->Destroy();
100 void CPlugin::InitIdentity()
104 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
105 m_handle = handlesys->CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
106 m_pRuntime->GetDefaultContext()->SetKey(1, m_ident);
107 m_pRuntime->GetDefaultContext()->SetKey(2, (IPlugin *)this);
111 unsigned int CPlugin::CalcMemUsage()
113 unsigned int base_size =
115 + sizeof(IdentityToken_t)
116 + (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
117 + m_pProps.mem_usage();
119 for (unsigned int i = 0; i < m_configs.size(); i++)
121 base_size += m_configs[i]->autocfg.size();
122 base_size += m_configs[i]->folder.size();
125 for (List<String>::iterator i = m_Libraries.begin();
126 i != m_Libraries.end();
129 base_size += (*i).size();
132 for (List<String>::iterator i = m_RequiredLibs.begin();
133 i != m_RequiredLibs.end();
136 base_size += (*i).size();
142 Handle_t CPlugin::GetMyHandle()
147 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
149 char fullpath[PLATFORM_MAX_PATH];
150 g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
151 FILE *fp = fopen(fullpath, "rb");
153 CPlugin *pPlugin = new CPlugin(file);
159 smcore.Format(error, maxlength, "Unable to open file");
161 pPlugin->m_status = Plugin_BadLoad;
170 bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
172 void **ptrpp = m_pProps.retrieve(prop);
173 bool exists = !!ptrpp;
179 m_pProps.remove(prop);
185 bool CPlugin::SetProperty(const char *prop, void *ptr)
187 return m_pProps.insert(prop, ptr);
190 IPluginRuntime *CPlugin::GetRuntime()
195 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
197 PluginStatus old_status = m_status;
200 if (old_status == Plugin_Running)
202 /* Tell everyone we're now paused */
203 g_PluginSys._SetPauseState(this, true);
207 va_start(ap, error_fmt);
208 smcore.FormatArgs(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
211 if (m_pRuntime != NULL)
213 m_pRuntime->SetPauseState(true);
217 bool CPlugin::UpdateInfo()
219 /* Now grab the info */
221 IPluginContext *base = GetBaseContext();
222 int err = base->FindPubvarByName("myinfo", &idx);
224 memset(&m_info, 0, sizeof(m_info));
226 if (err == SP_ERROR_NONE)
228 struct sm_plugininfo_c_t
236 sm_plugininfo_c_t *cinfo;
239 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
240 base->LocalToString(cinfo->name, (char **)&m_info.name);
241 base->LocalToString(cinfo->description, (char **)&m_info.description);
242 base->LocalToString(cinfo->author, (char **)&m_info.author);
243 base->LocalToString(cinfo->url, (char **)&m_info.url);
244 base->LocalToString(cinfo->version, (char **)&m_info.version);
247 m_info.author = m_info.author ? m_info.author : "";
248 m_info.description = m_info.description ? m_info.description : "";
249 m_info.name = m_info.name ? m_info.name : "";
250 m_info.url = m_info.url ? m_info.url : "";
251 m_info.version = m_info.version ? m_info.version : "";
253 if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE)
255 struct __version_info
262 __version_info *info;
264 const char *pDate, *pTime, *pFileVers;
269 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
270 m_FileVersion = info->version;
271 if (m_FileVersion >= 4)
273 base->LocalToString(info->date, (char **)&pDate);
274 base->LocalToString(info->time, (char **)&pTime);
275 smcore.Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
277 if (m_FileVersion > 5)
279 base->LocalToString(info->filevers, (char **)&pFileVers);
280 SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers);
289 if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE)
291 base->GetPubvarByIndex(idx, &m_MaxClientsVar);
297 void CPlugin::SyncMaxClients(int max_clients)
299 if (m_MaxClientsVar == NULL)
304 *m_MaxClientsVar->offs = max_clients;
307 void CPlugin::Call_OnPluginStart()
309 if (m_status != Plugin_Loaded)
314 m_status = Plugin_Running;
316 SyncMaxClients(playerhelpers->GetMaxClients());
319 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart");
326 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
328 SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
332 void CPlugin::Call_OnPluginEnd()
334 if (m_status > Plugin_Paused)
340 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd");
346 pFunction->Execute(&result);
349 void CPlugin::Call_OnAllPluginsLoaded()
351 if (m_status > Plugin_Paused)
361 m_bGotAllLoaded = true;
364 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded");
365 if (pFunction != NULL)
367 pFunction->Execute(&result);
370 if (smcore.IsMapRunning())
372 if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL)
374 pFunction->Execute(NULL);
378 if (smcore.AreConfigsExecuted())
380 smcore.ExecuteConfigs(GetBaseContext());
384 APLRes CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
386 if (m_status != Plugin_Created)
388 return APLRes_Failure;
391 m_status = Plugin_Loaded;
395 bool haveNewAPL = false;
396 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad2");
402 else if (!(pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad")))
404 return APLRes_Success;
407 pFunction->PushCell(m_handle);
408 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
409 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
410 pFunction->PushCell(maxlength);
411 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
413 return APLRes_Failure;
418 return (APLRes)result;
422 return APLRes_Success;
426 return APLRes_Failure;
430 void *CPlugin::GetPluginStructure()
435 IPluginContext *CPlugin::GetBaseContext()
437 if (m_pRuntime == NULL)
442 return m_pRuntime->GetDefaultContext();
445 sp_context_t *CPlugin::GetContext()
450 const char *CPlugin::GetFilename()
455 PluginType CPlugin::GetType()
460 const sm_plugininfo_t *CPlugin::GetPublicInfo()
462 if (GetStatus() >= Plugin_Created)
469 unsigned int CPlugin::GetSerial()
474 PluginStatus CPlugin::GetStatus()
479 bool CPlugin::IsSilentlyFailed()
481 return m_bSilentlyFailed;
484 bool CPlugin::IsDebugging()
486 if (m_pRuntime == NULL)
494 void CPlugin::SetSilentlyFailed(bool sf)
496 m_bSilentlyFailed = sf;
499 void CPlugin::LibraryActions(LibraryAction action)
501 List<String>::iterator iter;
502 for (iter = m_Libraries.begin();
503 iter != m_Libraries.end();
506 g_PluginSys.OnLibraryAction((*iter).c_str(), action);
510 bool CPlugin::SetPauseState(bool paused)
512 if (paused && GetStatus() != Plugin_Running)
515 } else if (!paused && GetStatus() != Plugin_Paused) {
521 LibraryActions(LibraryAction_Removed);
524 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange");
528 pFunction->PushCell(paused ? 1 : 0);
529 pFunction->Execute(&result);
534 m_status = Plugin_Paused;
535 m_pRuntime->SetPauseState(true);
537 m_status = Plugin_Running;
538 m_pRuntime->SetPauseState(false);
541 g_PluginSys._SetPauseState(this, paused);
545 LibraryActions(LibraryAction_Added);
551 IdentityToken_t *CPlugin::GetIdentity()
556 bool CPlugin::IsRunnable()
558 return (m_status <= Plugin_Paused) ? true : false;
561 time_t CPlugin::GetFileTimeStamp()
563 char path[PLATFORM_MAX_PATH];
564 g_pSM->BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
565 #ifdef PLATFORM_WINDOWS
567 if (_stat(path, &s) != 0)
568 #elif defined PLATFORM_POSIX
570 if (stat(path, &s) != 0)
581 time_t CPlugin::GetTimeStamp()
586 void CPlugin::SetTimeStamp(time_t t)
591 IPhraseCollection *CPlugin::GetPhrases()
596 void CPlugin::DependencyDropped(CPlugin *pOwner)
603 List<String>::iterator reqlib_iter;
604 List<String>::iterator lib_iter;
605 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
607 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
609 if ((*reqlib_iter) == (*lib_iter))
611 m_LibraryMissing = true;
616 List<NativeEntry *>::iterator iter;
617 NativeEntry *pNative;
620 unsigned int unbound = 0;
622 for (iter = pOwner->m_Natives.begin();
623 iter != pOwner->m_Natives.end();
627 /* Find this native! */
628 if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
633 m_pRuntime->GetNativeByIndex(idx, &native);
635 native->status = SP_NATIVE_UNBOUND;
641 m_FakeNativesMissing = true;
644 /* :IDEA: in the future, add native trapping? */
645 if (m_FakeNativesMissing || m_LibraryMissing)
647 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
651 unsigned int CPlugin::GetConfigCount()
653 return (unsigned int)m_configs.size();
656 AutoConfig *CPlugin::GetConfig(unsigned int i)
658 if (i >= GetConfigCount())
666 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
668 /* Do a check for duplicates to prevent double-execution */
669 for (size_t i = 0; i < m_configs.size(); i++)
671 if (m_configs[i]->autocfg.compare(cfg) == 0
672 && m_configs[i]->folder.compare(folder) == 0
673 && m_configs[i]->create == autoCreate)
679 AutoConfig *c = new AutoConfig;
683 c->create = autoCreate;
685 m_configs.push_back(c);
688 void CPlugin::DropEverything()
691 List<WeakNative>::iterator wk_iter;
693 /* Tell everyone that depends on us that we're about to drop */
694 for (List<CPlugin *>::iterator iter = m_Dependents.begin();
695 iter != m_Dependents.end();
698 pOther = static_cast<CPlugin *>(*iter);
699 pOther->DependencyDropped(this);
702 /* Note: we don't care about things we depend on.
703 * The reason is that extensions have their own cleanup
704 * code for plugins. Although the "right" design would be
705 * to centralize that here, i'm omitting it for now. Thus,
706 * the code below to walk the plugins list will suffice.
709 /* Other plugins could be holding weak references that were
710 * added by us. We need to clean all of those up now.
712 for (List<CPlugin *>::iterator iter = g_PluginSys.m_plugins.begin();
713 iter != g_PluginSys.m_plugins.end();
716 (*iter)->ToNativeOwner()->DropRefsTo(this);
719 /* Proceed with the rest of the necessities. */
720 CNativeOwner::DropEverything();
723 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
727 if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
732 m_Natives.push_back(pEntry);
741 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
747 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
752 bool CPluginManager::CPluginIterator::MorePlugins()
754 return (current != mylist->end());
757 void CPluginManager::CPluginIterator::NextPlugin()
762 void CPluginManager::CPluginIterator::Release()
764 g_PluginSys.ReleaseIterator(this);
767 CPluginManager::CPluginIterator::~CPluginIterator()
771 void CPluginManager::CPluginIterator::Reset()
773 current = mylist->begin();
780 CPluginManager::CPluginManager()
782 m_AllPluginsLoaded = false;
784 m_LoadingLocked = false;
786 m_bBlockBadPlugins = true;
789 CPluginManager::~CPluginManager()
791 CStack<CPluginManager::CPluginIterator *>::iterator iter;
792 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
799 void CPluginManager::Shutdown()
801 List<CPlugin *>::iterator iter;
803 while ((iter = m_plugins.begin()) != m_plugins.end())
809 void CPluginManager::LoadAll(const char *config_path, const char *plugins_path)
811 LoadAll_FirstPass(config_path, plugins_path);
812 g_Extensions.MarkAllLoaded();
813 LoadAll_SecondPass();
814 g_Extensions.MarkAllLoaded();
818 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
820 /* First read in the database of plugin settings */
821 m_AllPluginsLoaded = false;
822 LoadPluginsFromDir(basedir, NULL);
825 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
827 char base_path[PLATFORM_MAX_PATH];
829 /* Form the current path to start reading from */
830 if (localpath == NULL)
832 libsys->PathFormat(base_path, sizeof(base_path), "%s", basedir);
834 libsys->PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
837 IDirectory *dir = libsys->OpenDirectory(base_path);
842 libsys->GetPlatformError(error, sizeof(error));
843 smcore.LogError("[SM] Failure reading from plugins path: %s", localpath);
844 smcore.LogError("[SM] Platform returned error: %s", error);
848 while (dir->MoreFiles())
850 if (dir->IsEntryDirectory()
851 && (strcmp(dir->GetEntryName(), ".") != 0)
852 && (strcmp(dir->GetEntryName(), "..") != 0)
853 && (strcmp(dir->GetEntryName(), "disabled") != 0)
854 && (strcmp(dir->GetEntryName(), "optional") != 0))
856 char new_local[PLATFORM_MAX_PATH];
857 if (localpath == NULL)
859 /* If no path yet, don't add a former slash */
860 smcore.Format(new_local, sizeof(new_local), "%s", dir->GetEntryName());
862 libsys->PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
864 LoadPluginsFromDir(basedir, new_local);
865 } else if (dir->IsEntryFile()) {
866 const char *name = dir->GetEntryName();
867 size_t len = strlen(name);
869 && strcmp(&name[len-4], ".smx") == 0)
871 /* If the filename matches, load the plugin */
872 char plugin[PLATFORM_MAX_PATH];
873 if (localpath == NULL)
875 smcore.Format(plugin, sizeof(plugin), "%s", name);
877 libsys->PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
879 LoadAutoPlugin(plugin);
884 libsys->CloseDirectory(dir);
887 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
891 return LoadRes_NeverLoad;
897 * Does this plugin already exist?
899 CPlugin **pluginpp = m_LoadLookup.retrieve(path);
900 if (pluginpp && *pluginpp)
902 CPlugin *pPlugin = *pluginpp;
903 /* Check to see if we should try reloading it */
904 if (pPlugin->GetStatus() == Plugin_BadLoad
905 || pPlugin->GetStatus() == Plugin_Error
906 || pPlugin->GetStatus() == Plugin_Failed)
908 UnloadPlugin(pPlugin);
916 return LoadRes_AlreadyLoaded;
920 CPlugin *pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
922 assert(pPlugin != NULL);
924 pPlugin->m_type = PluginType_MapUpdated;
926 ICompilation *co = NULL;
928 if (pPlugin->m_status == Plugin_Uncompiled)
930 co = g_pSourcePawn2->StartCompilation();
933 /* Do the actual compiling */
936 char fullpath[PLATFORM_MAX_PATH];
937 g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
939 pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
940 if (pPlugin->m_pRuntime == NULL)
946 "Unable to load plugin (error %d: %s)",
948 g_pSourcePawn2->GetErrorString(err));
950 pPlugin->m_status = Plugin_BadLoad;
954 if (pPlugin->UpdateInfo())
956 pPlugin->m_status = Plugin_Created;
962 smcore.Format(error, maxlength, "%s", pPlugin->m_errormsg);
968 if (pPlugin->GetStatus() == Plugin_Created)
970 unsigned char *pCodeHash = pPlugin->m_pRuntime->GetCodeHash();
972 char codeHashBuf[40];
973 smcore.Format(codeHashBuf, 40, "plugin_");
974 for (int i = 0; i < 16; i++)
975 smcore.Format(codeHashBuf + 7 + (i * 2), 3, "%02x", pCodeHash[i]);
977 const char *bulletinUrl = g_pGameConf->GetKeyValue(codeHashBuf);
978 if (bulletinUrl != NULL)
980 if (m_bBlockBadPlugins)
984 if (bulletinUrl[0] != '\0')
986 smcore.Format(error, maxlength, "Known malware detected and blocked. See %s for more info", bulletinUrl);
988 smcore.Format(error, maxlength, "Possible malware or illegal plugin detected and blocked");
991 pPlugin->m_status = Plugin_BadLoad;
993 if (bulletinUrl[0] != '\0')
995 smcore.Log("%s: Known malware detected. See %s for more info, blocking disabled in core.cfg", pPlugin->GetFilename(), bulletinUrl);
997 smcore.Log("%s: Possible malware or illegal plugin detected, blocking disabled in core.cfg", pPlugin->GetFilename());
1003 LoadRes loadFailure = LoadRes_Failure;
1004 /* Get the status */
1005 if (pPlugin->GetStatus() == Plugin_Created)
1007 /* First native pass - add anything from Core */
1008 g_ShareSys.BindNativesToPlugin(pPlugin, true);
1009 pPlugin->InitIdentity();
1010 APLRes result = pPlugin->Call_AskPluginLoad(error, maxlength);
1013 case APLRes_Success:
1014 /* Autoload any modules */
1015 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
1017 case APLRes_Failure:
1018 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1019 loadFailure = LoadRes_Failure;
1021 case APLRes_SilentFailure:
1022 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1023 loadFailure = LoadRes_SilentFailure;
1024 pPlugin->SetSilentlyFailed(true);
1031 /* Save the time stamp */
1032 time_t t = pPlugin->GetFileTimeStamp();
1033 pPlugin->SetTimeStamp(t);
1040 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : loadFailure;
1043 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
1049 if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure)
1055 if (res == LoadRes_AlreadyLoaded)
1061 if (res == LoadRes_NeverLoad)
1065 if (m_LoadingLocked)
1067 smcore.Format(error, maxlength, "There is a global plugin loading lock in effect");
1071 smcore.Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
1079 /* Run second pass if we need to */
1080 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1082 if (!RunSecondPass(pl, error, maxlength))
1087 pl->Call_OnAllPluginsLoaded();
1093 void CPluginManager::LoadAutoPlugin(const char *plugin)
1097 char error[255] = "Unknown error";
1099 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1101 smcore.LogError("[SM] Failed to load plugin \"%s\": %s.", plugin, error);
1103 pl->GetStatus() <= Plugin_Created ? Plugin_BadLoad : pl->GetStatus(),
1108 if (res == LoadRes_Successful || res == LoadRes_Failure || res == LoadRes_SilentFailure)
1114 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1116 List<IPluginsListener *>::iterator iter;
1117 IPluginsListener *pListener;
1119 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1121 pListener = (*iter);
1122 pListener->OnPluginCreated(pPlugin);
1125 m_plugins.push_back(pPlugin);
1126 m_LoadLookup.insert(pPlugin->m_filename, pPlugin);
1129 void CPluginManager::LoadAll_SecondPass()
1131 List<CPlugin *>::iterator iter;
1135 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1138 if (pPlugin->GetStatus() == Plugin_Loaded)
1141 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1143 smcore.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1144 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1149 m_AllPluginsLoaded = true;
1152 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1161 IPluginContext *pBase = pPlugin->GetBaseContext();
1162 uint32_t num = pBase->GetPubVarsNum();
1163 sp_pubvar_t *pubvar;
1165 char pathfile[PLATFORM_MAX_PATH];
1167 for (uint32_t i=0; i<num; i++)
1169 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1173 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1175 pl = (_pl *)pubvar->offs;
1176 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1180 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1184 libsys->GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1185 if (strcmp(pathfile, file) == 0)
1189 if (pl->required == false)
1191 IPluginFunction *pFunc;
1193 smcore.Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1194 if ((pFunc=pBase->GetFunctionByName(buffer)))
1197 pFunc->Execute(&res);
1198 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1202 smcore.Format(error, maxlength, "Fatal error during initializing plugin load");
1210 /* Check that we aren't registering the same library twice */
1211 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1213 pPlugin->m_RequiredLibs.push_back(name);
1219 List<CPlugin *>::iterator iter;
1222 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1225 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1235 smcore.Format(error, maxlength, "Could not find required plugin \"%s\"", name);
1246 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1248 /* Find any extensions this plugin needs */
1257 IPluginContext *pBase = pPlugin->GetBaseContext();
1258 uint32_t num = pBase->GetPubVarsNum();
1259 sp_pubvar_t *pubvar;
1261 char path[PLATFORM_MAX_PATH];
1263 for (uint32_t i=0; i<num; i++)
1265 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1269 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1271 ext = (_ext *)pubvar->offs;
1272 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1276 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1282 /* Attempt to auto-load if necessary */
1285 libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1286 bool bErrorOnMissing = ext->required ? true : false;
1287 g_Extensions.LoadAutoExtension(path);
1292 /* Is this required? */
1295 libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1296 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1298 pExt = g_Extensions.FindExtensionByName(name);
1300 /* :TODO: should we bind to unloaded extensions?
1301 * Currently the extension manager will ignore this.
1303 if (!pExt || !pExt->IsRunning(NULL, 0))
1307 smcore.Format(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1313 g_Extensions.BindChildPlugin(pExt, pPlugin);
1318 IPluginFunction *pFunc;
1320 smcore.Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
1322 if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
1325 pFunc->Execute(&res);
1326 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1330 smcore.Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
1343 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1345 /* Second pass for extension requirements */
1346 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1351 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1356 /* Run another binding pass */
1357 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1359 /* Find any unbound natives. Right now, these are not allowed. */
1360 IPluginContext *pContext = pPlugin->GetBaseContext();
1361 uint32_t num = pContext->GetNativesNum();
1362 sp_native_t *native;
1363 for (unsigned int i=0; i<num; i++)
1365 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1369 if (native->status == SP_NATIVE_UNBOUND
1370 && native->name[0] != '@'
1371 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1375 smcore.Format(error, maxlength, "Native \"%s\" was not found", native->name);
1381 /* Finish by telling all listeners */
1382 List<IPluginsListener *>::iterator iter;
1383 IPluginsListener *pListener;
1384 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1386 pListener = (*iter);
1387 pListener->OnPluginLoaded(pPlugin);
1390 /* Tell this plugin to finish initializing itself */
1391 pPlugin->Call_OnPluginStart();
1393 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1394 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
1396 List<CPlugin *>::iterator pl_iter;
1398 for (pl_iter = m_plugins.begin();
1399 pl_iter != m_plugins.end();
1402 pOther = (*pl_iter);
1403 if ((pOther->GetStatus() == Plugin_Error
1404 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1405 || pOther->m_FakeNativesMissing)
1407 TryRefreshDependencies(pOther);
1409 else if ((pOther->GetStatus() == Plugin_Running
1410 || pOther->GetStatus() == Plugin_Paused)
1411 && pOther != pPlugin)
1413 List<NativeEntry *>::iterator nv_iter;
1414 for (nv_iter = pPlugin->m_Natives.begin();
1415 nv_iter != pPlugin->m_Natives.end();
1418 g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
1424 /* Go through our libraries and tell other plugins they're added */
1425 List<String>::iterator s_iter;
1426 for (s_iter = pPlugin->m_Libraries.begin();
1427 s_iter != pPlugin->m_Libraries.end();
1430 OnLibraryAction((*s_iter).c_str(), LibraryAction_Added);
1433 /* :TODO: optimize? does this even matter? */
1434 pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
1439 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1441 assert(pPlugin->GetBaseContext() != NULL);
1443 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1445 List<String>::iterator lib_iter;
1446 List<String>::iterator req_iter;
1447 List<CPlugin *>::iterator pl_iter;
1449 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1452 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1455 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1457 if ((*req_iter) == (*lib_iter))
1465 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1470 /* Find any unbound natives
1471 * Right now, these are not allowed
1473 IPluginContext *pContext = pPlugin->GetBaseContext();
1474 uint32_t num = pContext->GetNativesNum();
1475 sp_native_t *native;
1476 for (unsigned int i=0; i<num; i++)
1478 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1482 if (native->status == SP_NATIVE_UNBOUND
1483 && native->name[0] != '@'
1484 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1486 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1491 if (pPlugin->GetStatus() == Plugin_Error)
1493 /* If we got here, all natives are okay again! */
1494 pPlugin->m_status = Plugin_Running;
1495 if (pPlugin->m_pRuntime->IsPaused())
1497 pPlugin->m_pRuntime->SetPauseState(false);
1498 _SetPauseState(pPlugin, false);
1503 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1505 CPlugin *pPlugin = (CPlugin *)plugin;
1507 /* This prevents removal during insertion or anything else weird */
1508 if (m_plugins.find(pPlugin) == m_plugins.end())
1513 IPluginContext *pContext = plugin->GetBaseContext();
1514 if (pContext != NULL && pContext->IsInExec())
1517 smcore.Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
1518 engine->ServerCommand(buffer);
1522 /* Remove us from the lookup table and linked list */
1523 m_plugins.remove(pPlugin);
1524 m_LoadLookup.remove(pPlugin->m_filename);
1526 /* Go through our libraries and tell other plugins they're gone */
1527 List<String>::iterator s_iter;
1528 for (s_iter = pPlugin->m_Libraries.begin();
1529 s_iter != pPlugin->m_Libraries.end();
1532 OnLibraryAction((*s_iter).c_str(), LibraryAction_Removed);
1535 List<IPluginsListener *>::iterator iter;
1536 IPluginsListener *pListener;
1538 if (pPlugin->GetStatus() <= Plugin_Error)
1541 pPlugin->Call_OnPluginEnd();
1543 /* Notify listeners of unloading */
1544 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1546 pListener = (*iter);
1547 pListener->OnPluginUnloaded(pPlugin);
1551 pPlugin->DropEverything();
1553 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1555 /* Notify listeners of destruction */
1556 pListener = (*iter);
1557 pListener->OnPluginDestroyed(pPlugin);
1560 /* Tell the plugin to delete itself */
1566 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1569 IPluginContext *pContext;
1571 pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
1573 if (pContext->GetKey(2, (void **)&pPlugin))
1581 CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
1583 return (CPlugin *)FindPluginByContext(ctx);
1586 unsigned int CPluginManager::GetPluginCount()
1588 return m_plugins.size();
1591 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1593 m_listeners.push_back(listener);
1596 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1598 m_listeners.remove(listener);
1601 IPluginIterator *CPluginManager::GetPluginIterator()
1603 if (m_iters.empty())
1605 return new CPluginIterator(&m_plugins);
1607 CPluginIterator *iter = m_iters.front();
1614 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1619 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1621 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1622 size_t alias_len = 0;
1623 size_t local_len = 0;
1625 const char *ptr = alias;
1626 unsigned int alias_explicit_paths = 0;
1627 unsigned int alias_path_end = 0;
1628 while (*ptr != '\0')
1630 if (*ptr == '\\' || *ptr == '/')
1632 alias_explicit_paths++;
1633 alias_path_end = alias_len;
1639 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1641 /* Trailing slash is totally invalid here */
1646 unsigned int local_explicit_paths = 0;
1647 unsigned int local_path_end = 0;
1648 while (*ptr != '\0')
1650 if (*ptr == '\\' || *ptr == '/')
1652 local_explicit_paths++;
1653 local_path_end = local_len;
1659 /* If the alias has more explicit paths than the real path,
1660 * no match will be possible.
1662 if (alias_explicit_paths > local_explicit_paths)
1667 if (alias_explicit_paths)
1669 /* We need to find if the paths match now. For example, these should all match:
1671 * csdm optional/csdm
1672 * csdm/ban optional/crab/csdm/ban
1674 const char *aliasptr = alias;
1675 const char *localptr = localpath;
1679 if (*aliasptr != *localptr)
1681 /* We have to knock one path off */
1682 local_explicit_paths--;
1683 if (alias_explicit_paths > local_explicit_paths)
1685 /* Skip out if we're gonna have an impossible match */
1688 /* Eat up localptr tokens until we get a result */
1689 while (((localptr - localpath) < (int)local_path_end)
1691 && *localptr != '\\')
1695 /* Check if we hit the end of our searchable area.
1696 * This probably isn't possible because of the path
1697 * count check, but it's a good idea anyway.
1699 if ((localptr - localpath) >= (int)local_path_end)
1703 /* Consume the slash token */
1706 /* Reset the alias pointer so we can continue consuming */
1712 * This is safe because if localptr terminates early, aliasptr will too
1716 /* We should never reach the end of the string because of this check. */
1717 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1718 bool localend = (localptr - localpath) > (int)local_path_end;
1719 if (aliasend || localend)
1721 if (aliasend && localend)
1723 /* we matched, and we can break out now */
1727 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1732 /* If we got here, it's safe to compare the next two tokens */
1733 if (*localptr != *aliasptr)
1744 /* If we got here, it's time to compare filenames */
1745 const char *aliasptr = alias;
1746 const char *localptr = localpath;
1748 if (alias_explicit_paths)
1750 aliasptr = &alias[alias_path_end + 1];
1753 if (local_explicit_paths)
1755 localptr = &localpath[local_path_end + 1];
1760 if (*aliasptr == '*')
1762 /* First, see if this is the last character */
1763 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1765 /* If so, there's no need to match anything else */
1768 /* Otherwise, we need to search for an appropriate matching sequence in local.
1769 * Note that we only need to search up to the next asterisk.
1773 const char *local_orig = localptr;
1777 while (*aliasptr != '\0' && *aliasptr != '*')
1779 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1780 if (*aliasptr != *localptr)
1790 /* If we didn't get a match, we need to advance the search stream.
1791 * This will let us skip tokens while still searching for another match.
1793 localptr = ++local_orig;
1794 /* Make sure we don't go out of bounds */
1795 if (*localptr == '\0')
1806 /* If we got a match, move on to the next token */
1809 } else if (*aliasptr == '\0') {
1810 if (*localptr == '\0'
1812 strcmp(localptr, ".smx") == 0)
1818 } else if (*aliasptr != *localptr) {
1828 bool CPluginManager::IsLateLoadTime() const
1830 return (m_AllPluginsLoaded || !smcore.IsMapLoading());
1833 void CPluginManager::OnSourceModAllInitialized()
1835 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1838 handlesys->InitAccessDefaults(NULL, &sec);
1840 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1841 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1843 g_PluginType = handlesys->CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1844 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1846 rootmenu->AddRootConsoleCommand("plugins", "Manage Plugins", this);
1848 g_ShareSys.AddInterface(NULL, GetOldAPI());
1850 m_pOnLibraryAdded = forwardsys->CreateForward("OnLibraryAdded", ET_Ignore, 1, NULL, Param_String);
1851 m_pOnLibraryRemoved = forwardsys->CreateForward("OnLibraryRemoved", ET_Ignore, 1, NULL, Param_String);
1854 void CPluginManager::OnSourceModShutdown()
1856 rootmenu->RemoveRootConsoleCommand("plugins", this);
1860 handlesys->RemoveType(g_PluginType, m_MyIdent);
1861 g_ShareSys.DestroyIdentType(g_PluginIdent);
1862 g_ShareSys.DestroyIdentity(m_MyIdent);
1864 forwardsys->ReleaseForward(m_pOnLibraryAdded);
1865 forwardsys->ReleaseForward(m_pOnLibraryRemoved);
1868 ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
1870 ConfigSource source,
1874 if (strcmp(key, "BlockBadPlugins") == 0) {
1875 if (strcasecmp(value, "yes") == 0)
1877 m_bBlockBadPlugins = true;
1878 } else if (strcasecmp(value, "no") == 0) {
1879 m_bBlockBadPlugins = false;
1881 smcore.Format(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
1882 return ConfigResult_Reject;
1884 return ConfigResult_Accept;
1886 return ConfigResult_Ignore;
1889 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1891 /* We don't care about the internal object, actually */
1894 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1896 *pSize = ((CPlugin *)object)->CalcMemUsage();
1900 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1908 sec.pIdentity = m_MyIdent;
1910 if ((_err=handlesys->ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1923 CPlugin *CPluginManager::GetPluginByOrder(int num)
1925 if (num < 1 || num > (int)GetPluginCount())
1933 SourceHook::List<CPlugin *>::iterator iter;
1934 for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
1941 const char *CPluginManager::GetStatusText(PluginStatus st)
1945 case Plugin_Running:
1951 case Plugin_Uncompiled:
1952 return "Uncompiled";
1953 case Plugin_BadLoad:
1963 static inline bool IS_STR_FILLED(const char *text)
1965 return text[0] != '\0';
1968 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1970 int argcount = smcore.Argc(command);
1973 const char *cmd = smcore.Arg(command, 2);
1974 if (strcmp(cmd, "list") == 0)
1977 unsigned int id = 1;
1978 int plnum = GetPluginCount();
1982 rootmenu->ConsolePrint("[SM] No plugins loaded");
1987 rootmenu->ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1991 SourceHook::List<CPlugin *>::iterator iter;
1992 SourceHook::List<CPlugin *> m_FailList;
1994 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
1997 assert(pl->GetStatus() != Plugin_Created);
1999 const sm_plugininfo_t *info = pl->GetPublicInfo();
2000 if (pl->GetStatus() != Plugin_Running && !pl->IsSilentlyFailed())
2002 len += smcore.Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
2004 if (pl->GetStatus() <= Plugin_Error)
2006 /* Plugin has failed to load. */
2007 m_FailList.push_back(pl);
2012 len += smcore.Format(buffer, sizeof(buffer), " %02d", id);
2014 if (pl->GetStatus() < Plugin_Created)
2016 if (pl->IsSilentlyFailed())
2017 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " Disabled:");
2018 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2019 if (IS_STR_FILLED(info->version))
2021 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
2023 if (IS_STR_FILLED(info->author))
2025 smcore.Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
2030 smcore.Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename);
2032 rootmenu->ConsolePrint("%s", buffer);
2035 if (!m_FailList.empty())
2037 rootmenu->ConsolePrint("Load Errors:");
2039 SourceHook::List<CPlugin *>::iterator _iter;
2043 for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
2045 pl = (CPlugin *)*_iter;
2046 rootmenu->ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
2052 else if (strcmp(cmd, "load") == 0)
2056 rootmenu->ConsolePrint("[SM] Usage: sm plugins load <file>");
2062 const char *filename = smcore.Arg(command, 3);
2064 char pluginfile[256];
2065 const char *ext = libsys->GetFileExtension(filename) ? "" : ".smx";
2066 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
2068 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
2072 rootmenu->ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
2078 rootmenu->ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
2082 rootmenu->ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
2087 else if (strcmp(cmd, "unload") == 0)
2091 rootmenu->ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2097 const char *arg = smcore.Arg(command, 3);
2098 int id = strtol(arg, &end, 10);
2102 pl = GetPluginByOrder(id);
2105 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2111 char pluginfile[256];
2112 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2113 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2115 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2116 if (!pluginpp || !*pluginpp)
2118 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2124 char name[PLATFORM_MAX_PATH];
2126 if (pl->GetStatus() < Plugin_Created)
2128 const sm_plugininfo_t *info = pl->GetPublicInfo();
2129 smcore.Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2133 smcore.Format(name, sizeof(name), "%s", pl->GetFilename());
2136 if (UnloadPlugin(pl))
2138 rootmenu->ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2142 rootmenu->ConsolePrint("[SM] Failed to unload plugin %s.", name);
2147 else if (strcmp(cmd, "unload_all") == 0)
2149 g_PluginSys.UnloadAll();
2150 rootmenu->ConsolePrint("[SM] All plugins have been unloaded.");
2153 else if (strcmp(cmd, "load_lock") == 0)
2155 if (m_LoadingLocked)
2157 rootmenu->ConsolePrint("[SM] There is already a loading lock in effect.");
2161 m_LoadingLocked = true;
2162 rootmenu->ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
2166 else if (strcmp(cmd, "load_unlock") == 0)
2168 if (m_LoadingLocked)
2170 m_LoadingLocked = false;
2171 rootmenu->ConsolePrint("[SM] The loading lock is no longer in effect.");
2175 rootmenu->ConsolePrint("[SM] There was no loading lock in effect.");
2179 else if (strcmp(cmd, "info") == 0)
2183 rootmenu->ConsolePrint("[SM] Usage: sm plugins info <#>");
2189 const char *arg = smcore.Arg(command, 3);
2190 int id = strtol(arg, &end, 10);
2194 pl = GetPluginByOrder(id);
2197 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2203 char pluginfile[256];
2204 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2205 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2207 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2208 if (!pluginpp || !*pluginpp)
2210 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2216 const sm_plugininfo_t *info = pl->GetPublicInfo();
2218 rootmenu->ConsolePrint(" Filename: %s", pl->GetFilename());
2219 if (pl->GetStatus() <= Plugin_Error)
2221 if (IS_STR_FILLED(info->name))
2223 if (IS_STR_FILLED(info->description))
2225 rootmenu->ConsolePrint(" Title: %s (%s)", info->name, info->description);
2227 rootmenu->ConsolePrint(" Title: %s", info->name);
2230 if (IS_STR_FILLED(info->author))
2232 rootmenu->ConsolePrint(" Author: %s", info->author);
2234 if (IS_STR_FILLED(info->version))
2236 rootmenu->ConsolePrint(" Version: %s", info->version);
2238 if (IS_STR_FILLED(info->url))
2240 rootmenu->ConsolePrint(" URL: %s", info->url);
2242 if (pl->GetStatus() == Plugin_Error)
2244 rootmenu->ConsolePrint(" Error: %s", pl->m_errormsg);
2248 if (pl->GetStatus() == Plugin_Running)
2250 rootmenu->ConsolePrint(" Status: running");
2254 rootmenu->ConsolePrint(" Status: not running");
2257 const char *typestr = "";
2258 switch (pl->GetType())
2260 case PluginType_MapUpdated:
2261 typestr = "Map Change if Updated";
2263 case PluginType_MapOnly:
2264 typestr = "Map Change";
2266 case PluginType_Private:
2267 case PluginType_Global:
2272 rootmenu->ConsolePrint(" Reloads: %s", typestr);
2274 if (pl->m_FileVersion >= 3)
2276 rootmenu->ConsolePrint(" Timestamp: %s", pl->m_DateTime);
2279 unsigned char *pCodeHash = pl->m_pRuntime->GetCodeHash();
2280 unsigned char *pDataHash = pl->m_pRuntime->GetDataHash();
2282 char combinedHash[33];
2283 for (int i = 0; i < 16; i++)
2284 smcore.Format(combinedHash + (i * 2), 3, "%02x", pCodeHash[i] ^ pDataHash[i]);
2286 rootmenu->ConsolePrint(" Hash: %s", combinedHash);
2290 rootmenu->ConsolePrint(" Load error: %s", pl->m_errormsg);
2291 if (pl->GetStatus() < Plugin_Created)
2293 rootmenu->ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2294 info->name ? info->name : "<none>",
2295 info->version ? info->version : "<none>");
2296 if (IS_STR_FILLED(info->url))
2298 rootmenu->ConsolePrint(" File URL: %s", info->url);
2305 else if (strcmp(cmd, "refresh") == 0)
2307 smcore.DoGlobalPluginLoads();
2308 rootmenu->ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
2311 else if (strcmp(cmd, "reload") == 0)
2315 rootmenu->ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
2321 const char *arg = smcore.Arg(command, 3);
2322 int id = strtol(arg, &end, 10);
2326 pl = GetPluginByOrder(id);
2329 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2335 char pluginfile[256];
2336 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2337 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2339 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2340 if (!pluginpp || !*pluginpp)
2342 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2348 char name[PLATFORM_MAX_PATH];
2349 const sm_plugininfo_t *info = pl->GetPublicInfo();
2350 if (pl->GetStatus() <= Plugin_Paused)
2351 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2353 strcpy(name, pl->GetFilename());
2355 if (ReloadPlugin(pl))
2357 rootmenu->ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
2361 rootmenu->ConsolePrint("[SM] Failed to reload plugin %s.", name);
2368 /* Draw the main menu */
2369 rootmenu->ConsolePrint("SourceMod Plugins Menu:");
2370 rootmenu->DrawGenericOption("info", "Information about a plugin");
2371 rootmenu->DrawGenericOption("list", "Show loaded plugins");
2372 rootmenu->DrawGenericOption("load", "Load a plugin");
2373 rootmenu->DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
2374 rootmenu->DrawGenericOption("load_unlock", "Re-enables plugin loading");
2375 rootmenu->DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
2376 rootmenu->DrawGenericOption("reload", "Reloads a plugin");
2377 rootmenu->DrawGenericOption("unload", "Unload a plugin");
2378 rootmenu->DrawGenericOption("unload_all", "Unloads all plugins");
2381 bool CPluginManager::ReloadPlugin(CPlugin *pl)
2383 List<CPlugin *>::iterator iter;
2384 char filename[PLATFORM_MAX_PATH];
2390 strcpy(filename, pl->m_filename);
2391 ptype = pl->GetType();
2393 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
2401 if (!UnloadPlugin(pl))
2405 if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded)))
2410 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2412 if ((*iter) == (CPlugin *)newpl)
2414 m_plugins.erase(iter);
2420 for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
2423 m_plugins.insert(iter, (CPlugin *)newpl);
2428 void CPluginManager::RefreshAll()
2430 /* If we're in a load lock, just skip this whole bit. */
2431 if (m_LoadingLocked)
2436 List<CPlugin *>::iterator iter;
2437 List<CPlugin *> tmp_list = m_plugins;
2441 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2444 if (pl->GetType() == PluginType_MapOnly)
2446 UnloadPlugin((IPlugin *)pl);
2448 else if (pl->GetType() == PluginType_MapUpdated)
2450 t = pl->GetFileTimeStamp();
2451 if (!t || t > pl->GetTimeStamp())
2453 pl->SetTimeStamp(t);
2454 UnloadPlugin((IPlugin *)pl);
2460 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2462 List<IPluginsListener *>::iterator iter;
2463 IPluginsListener *pListener;
2464 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2466 pListener = (*iter);
2467 pListener->OnPluginPauseChange(pl, paused);
2471 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2473 List<CPlugin *>::iterator iter;
2475 IPluginFunction *pFunction;
2477 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2481 if (pPlugin->GetStatus() <= Plugin_Paused)
2483 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2487 pForward->AddFunction(pFunction);
2493 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2495 if (pToken->type != g_PluginIdent)
2500 return (CPlugin *)(pToken->ptr);
2503 void CPluginManager::OnLibraryAction(const char *lib, LibraryAction action)
2507 case LibraryAction_Removed:
2508 m_pOnLibraryRemoved->PushString(lib);
2509 m_pOnLibraryRemoved->Execute(NULL);
2511 case LibraryAction_Added:
2512 m_pOnLibraryAdded->PushString(lib);
2513 m_pOnLibraryAdded->Execute(NULL);
2518 bool CPluginManager::LibraryExists(const char *lib)
2520 List<CPlugin *>::iterator iter;
2522 for (iter=m_plugins.begin();
2523 iter!=m_plugins.end();
2526 CPlugin *pl = (*iter);
2527 if (pl->GetStatus() != Plugin_Running)
2531 List<String>::iterator s_iter;
2532 for (s_iter = pl->m_Libraries.begin();
2533 s_iter != pl->m_Libraries.end();
2536 if ((*s_iter).compare(lib) == 0)
2546 void CPluginManager::AllPluginsLoaded()
2548 List<CPlugin *>::iterator iter;
2551 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2554 pl->Call_OnAllPluginsLoaded();
2558 void CPluginManager::UnloadAll()
2560 List<CPlugin *>::iterator iter;
2561 while ( (iter = m_plugins.begin()) != m_plugins.end() )
2563 UnloadPlugin((*iter));
2567 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
2570 List<CPlugin *>::iterator iter;
2572 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2583 SMPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
2589 id = strtol(arg, &end, 10);
2593 pl = GetPluginByOrder(id);
2601 char pluginfile[256];
2602 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2603 smcore.Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2605 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2606 if (!pluginpp || !*pluginpp)
2615 void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue)
2617 SyncMaxClients(newvalue);
2620 void CPluginManager::SyncMaxClients(int max_clients)
2622 List<CPlugin *>::iterator iter;
2624 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2626 (*iter)->SyncMaxClients(max_clients);
2630 void CPluginManager::ListPlugins(CVector<SMPlugin *> *list)
2632 List<CPlugin *>::iterator iter;
2634 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2636 list->push_back((*iter));
2640 class OldPluginAPI : public IPluginManager
2643 IPlugin *LoadPlugin(const char *path,
2650 return g_PluginSys.LoadPlugin(path, debug, type, error, maxlength, wasloaded);
2653 bool UnloadPlugin(IPlugin *plugin)
2655 return g_PluginSys.UnloadPlugin(plugin);
2658 IPlugin *FindPluginByContext(const sp_context_t *ctx)
2660 return g_PluginSys.FindPluginByContext(ctx);
2663 unsigned int GetPluginCount()
2665 return g_PluginSys.GetPluginCount();
2668 IPluginIterator *GetPluginIterator()
2670 return g_PluginSys.GetPluginIterator();
2673 void AddPluginsListener(IPluginsListener *listener)
2675 g_PluginSys.AddPluginsListener(listener);
2678 void RemovePluginsListener(IPluginsListener *listener)
2680 g_PluginSys.RemovePluginsListener(listener);
2683 IPlugin *PluginFromHandle(Handle_t handle, HandleError *err)
2685 return g_PluginSys.PluginFromHandle(handle, err);
2689 static OldPluginAPI sOldPluginAPI;
2691 IPluginManager *CPluginManager::GetOldAPI()
2693 return &sOldPluginAPI;