Fixed copy-paste error in 116256b519b5.
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;
181 m_pProps.remove(prop);
187 bool CPlugin::SetProperty(const char *prop, void *ptr)
189 return m_pProps.insert(prop, ptr);
192 IPluginRuntime *CPlugin::GetRuntime()
197 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
199 PluginStatus old_status = m_status;
202 if (old_status == Plugin_Running)
204 /* Tell everyone we're now paused */
205 g_PluginSys._SetPauseState(this, true);
209 va_start(ap, error_fmt);
210 smcore.FormatArgs(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
213 if (m_pRuntime != NULL)
215 m_pRuntime->SetPauseState(true);
219 bool CPlugin::UpdateInfo()
221 /* Now grab the info */
223 IPluginContext *base = GetBaseContext();
224 int err = base->FindPubvarByName("myinfo", &idx);
226 memset(&m_info, 0, sizeof(m_info));
228 if (err == SP_ERROR_NONE)
230 struct sm_plugininfo_c_t
238 sm_plugininfo_c_t *cinfo;
241 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
242 base->LocalToString(cinfo->name, (char **)&m_info.name);
243 base->LocalToString(cinfo->description, (char **)&m_info.description);
244 base->LocalToString(cinfo->author, (char **)&m_info.author);
245 base->LocalToString(cinfo->url, (char **)&m_info.url);
246 base->LocalToString(cinfo->version, (char **)&m_info.version);
249 m_info.author = m_info.author ? m_info.author : "";
250 m_info.description = m_info.description ? m_info.description : "";
251 m_info.name = m_info.name ? m_info.name : "";
252 m_info.url = m_info.url ? m_info.url : "";
253 m_info.version = m_info.version ? m_info.version : "";
255 if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE)
257 struct __version_info
264 __version_info *info;
266 const char *pDate, *pTime, *pFileVers;
271 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
272 m_FileVersion = info->version;
273 if (m_FileVersion >= 4)
275 base->LocalToString(info->date, (char **)&pDate);
276 base->LocalToString(info->time, (char **)&pTime);
277 smcore.Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
279 if (m_FileVersion > 5)
281 base->LocalToString(info->filevers, (char **)&pFileVers);
282 SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers);
291 if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE)
293 base->GetPubvarByIndex(idx, &m_MaxClientsVar);
299 void CPlugin::SyncMaxClients(int max_clients)
301 if (m_MaxClientsVar == NULL)
306 *m_MaxClientsVar->offs = max_clients;
309 void CPlugin::Call_OnPluginStart()
311 if (m_status != Plugin_Loaded)
316 m_status = Plugin_Running;
318 SyncMaxClients(playerhelpers->GetMaxClients());
321 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart");
328 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
330 SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
334 void CPlugin::Call_OnPluginEnd()
336 if (m_status > Plugin_Paused)
342 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd");
348 pFunction->Execute(&result);
351 void CPlugin::Call_OnAllPluginsLoaded()
353 if (m_status > Plugin_Paused)
363 m_bGotAllLoaded = true;
366 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded");
367 if (pFunction != NULL)
369 pFunction->Execute(&result);
372 if (smcore.IsMapRunning())
374 if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL)
376 pFunction->Execute(NULL);
380 if (smcore.AreConfigsExecuted())
382 smcore.ExecuteConfigs(GetBaseContext());
386 APLRes CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
388 if (m_status != Plugin_Created)
390 return APLRes_Failure;
393 m_status = Plugin_Loaded;
397 bool haveNewAPL = false;
398 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad2");
404 else if (!(pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad")))
406 return APLRes_Success;
409 pFunction->PushCell(m_handle);
410 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
411 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
412 pFunction->PushCell(maxlength);
413 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
415 return APLRes_Failure;
420 return (APLRes)result;
424 return APLRes_Success;
428 return APLRes_Failure;
432 void *CPlugin::GetPluginStructure()
437 IPluginContext *CPlugin::GetBaseContext()
439 if (m_pRuntime == NULL)
444 return m_pRuntime->GetDefaultContext();
447 sp_context_t *CPlugin::GetContext()
452 const char *CPlugin::GetFilename()
457 PluginType CPlugin::GetType()
462 const sm_plugininfo_t *CPlugin::GetPublicInfo()
464 if (GetStatus() >= Plugin_Created)
471 unsigned int CPlugin::GetSerial()
476 PluginStatus CPlugin::GetStatus()
481 bool CPlugin::IsSilentlyFailed()
483 return m_bSilentlyFailed;
486 bool CPlugin::IsDebugging()
488 if (m_pRuntime == NULL)
496 void CPlugin::SetSilentlyFailed(bool sf)
498 m_bSilentlyFailed = sf;
501 void CPlugin::LibraryActions(LibraryAction action)
503 List<String>::iterator iter;
504 for (iter = m_Libraries.begin();
505 iter != m_Libraries.end();
508 g_PluginSys.OnLibraryAction((*iter).c_str(), action);
512 bool CPlugin::SetPauseState(bool paused)
514 if (paused && GetStatus() != Plugin_Running)
517 } else if (!paused && GetStatus() != Plugin_Paused) {
523 LibraryActions(LibraryAction_Removed);
526 IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange");
530 pFunction->PushCell(paused ? 1 : 0);
531 pFunction->Execute(&result);
536 m_status = Plugin_Paused;
537 m_pRuntime->SetPauseState(true);
539 m_status = Plugin_Running;
540 m_pRuntime->SetPauseState(false);
543 g_PluginSys._SetPauseState(this, paused);
547 LibraryActions(LibraryAction_Added);
553 IdentityToken_t *CPlugin::GetIdentity()
558 bool CPlugin::IsRunnable()
560 return (m_status <= Plugin_Paused) ? true : false;
563 time_t CPlugin::GetFileTimeStamp()
565 char path[PLATFORM_MAX_PATH];
566 g_pSM->BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
567 #ifdef PLATFORM_WINDOWS
569 if (_stat(path, &s) != 0)
570 #elif defined PLATFORM_POSIX
572 if (stat(path, &s) != 0)
583 time_t CPlugin::GetTimeStamp()
588 void CPlugin::SetTimeStamp(time_t t)
593 IPhraseCollection *CPlugin::GetPhrases()
598 void CPlugin::DependencyDropped(CPlugin *pOwner)
605 List<String>::iterator reqlib_iter;
606 List<String>::iterator lib_iter;
607 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
609 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
611 if ((*reqlib_iter) == (*lib_iter))
613 m_LibraryMissing = true;
618 List<NativeEntry *>::iterator iter;
619 NativeEntry *pNative;
622 unsigned int unbound = 0;
624 for (iter = pOwner->m_Natives.begin();
625 iter != pOwner->m_Natives.end();
629 /* Find this native! */
630 if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
635 m_pRuntime->GetNativeByIndex(idx, &native);
637 native->status = SP_NATIVE_UNBOUND;
643 m_FakeNativesMissing = true;
646 /* :IDEA: in the future, add native trapping? */
647 if (m_FakeNativesMissing || m_LibraryMissing)
649 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
653 size_t CPlugin::GetConfigCount()
655 return (unsigned int)m_configs.size();
658 AutoConfig *CPlugin::GetConfig(size_t i)
660 if (i >= GetConfigCount())
668 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
670 /* Do a check for duplicates to prevent double-execution */
671 for (size_t i = 0; i < m_configs.size(); i++)
673 if (m_configs[i]->autocfg.compare(cfg) == 0
674 && m_configs[i]->folder.compare(folder) == 0
675 && m_configs[i]->create == autoCreate)
681 AutoConfig *c = new AutoConfig;
685 c->create = autoCreate;
687 m_configs.push_back(c);
690 void CPlugin::DropEverything()
693 List<WeakNative>::iterator wk_iter;
695 /* Tell everyone that depends on us that we're about to drop */
696 for (List<CPlugin *>::iterator iter = m_Dependents.begin();
697 iter != m_Dependents.end();
700 pOther = static_cast<CPlugin *>(*iter);
701 pOther->DependencyDropped(this);
704 /* Note: we don't care about things we depend on.
705 * The reason is that extensions have their own cleanup
706 * code for plugins. Although the "right" design would be
707 * to centralize that here, i'm omitting it for now. Thus,
708 * the code below to walk the plugins list will suffice.
711 /* Other plugins could be holding weak references that were
712 * added by us. We need to clean all of those up now.
714 for (List<CPlugin *>::iterator iter = g_PluginSys.m_plugins.begin();
715 iter != g_PluginSys.m_plugins.end();
718 (*iter)->ToNativeOwner()->DropRefsTo(this);
721 /* Proceed with the rest of the necessities. */
722 CNativeOwner::DropEverything();
725 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
729 if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
734 m_Natives.push_back(pEntry);
743 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
749 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
754 bool CPluginManager::CPluginIterator::MorePlugins()
756 return (current != mylist->end());
759 void CPluginManager::CPluginIterator::NextPlugin()
764 void CPluginManager::CPluginIterator::Release()
766 g_PluginSys.ReleaseIterator(this);
769 CPluginManager::CPluginIterator::~CPluginIterator()
773 void CPluginManager::CPluginIterator::Reset()
775 current = mylist->begin();
782 CPluginManager::CPluginManager()
784 m_AllPluginsLoaded = false;
786 m_LoadingLocked = false;
788 m_bBlockBadPlugins = true;
791 CPluginManager::~CPluginManager()
793 CStack<CPluginManager::CPluginIterator *>::iterator iter;
794 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
801 void CPluginManager::Shutdown()
803 List<CPlugin *>::iterator iter;
805 while ((iter = m_plugins.begin()) != m_plugins.end())
811 void CPluginManager::LoadAll(const char *config_path, const char *plugins_path)
813 LoadAll_FirstPass(config_path, plugins_path);
814 g_Extensions.MarkAllLoaded();
815 LoadAll_SecondPass();
816 g_Extensions.MarkAllLoaded();
820 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
822 /* First read in the database of plugin settings */
823 m_AllPluginsLoaded = false;
824 LoadPluginsFromDir(basedir, NULL);
827 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
829 char base_path[PLATFORM_MAX_PATH];
831 /* Form the current path to start reading from */
832 if (localpath == NULL)
834 libsys->PathFormat(base_path, sizeof(base_path), "%s", basedir);
836 libsys->PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
839 IDirectory *dir = libsys->OpenDirectory(base_path);
844 libsys->GetPlatformError(error, sizeof(error));
845 smcore.LogError("[SM] Failure reading from plugins path: %s", localpath);
846 smcore.LogError("[SM] Platform returned error: %s", error);
850 while (dir->MoreFiles())
852 if (dir->IsEntryDirectory()
853 && (strcmp(dir->GetEntryName(), ".") != 0)
854 && (strcmp(dir->GetEntryName(), "..") != 0)
855 && (strcmp(dir->GetEntryName(), "disabled") != 0)
856 && (strcmp(dir->GetEntryName(), "optional") != 0))
858 char new_local[PLATFORM_MAX_PATH];
859 if (localpath == NULL)
861 /* If no path yet, don't add a former slash */
862 smcore.Format(new_local, sizeof(new_local), "%s", dir->GetEntryName());
864 libsys->PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
866 LoadPluginsFromDir(basedir, new_local);
867 } else if (dir->IsEntryFile()) {
868 const char *name = dir->GetEntryName();
869 size_t len = strlen(name);
871 && strcmp(&name[len-4], ".smx") == 0)
873 /* If the filename matches, load the plugin */
874 char plugin[PLATFORM_MAX_PATH];
875 if (localpath == NULL)
877 smcore.Format(plugin, sizeof(plugin), "%s", name);
879 libsys->PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
881 LoadAutoPlugin(plugin);
886 libsys->CloseDirectory(dir);
889 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
893 return LoadRes_NeverLoad;
899 * Does this plugin already exist?
901 CPlugin **pluginpp = m_LoadLookup.retrieve(path);
902 if (pluginpp && *pluginpp)
904 CPlugin *pPlugin = *pluginpp;
905 /* Check to see if we should try reloading it */
906 if (pPlugin->GetStatus() == Plugin_BadLoad
907 || pPlugin->GetStatus() == Plugin_Error
908 || pPlugin->GetStatus() == Plugin_Failed)
910 UnloadPlugin(pPlugin);
918 return LoadRes_AlreadyLoaded;
922 CPlugin *pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
924 assert(pPlugin != NULL);
926 pPlugin->m_type = PluginType_MapUpdated;
928 ICompilation *co = NULL;
930 if (pPlugin->m_status == Plugin_Uncompiled)
932 co = g_pSourcePawn2->StartCompilation();
935 /* Do the actual compiling */
938 char fullpath[PLATFORM_MAX_PATH];
939 g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
941 pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
942 if (pPlugin->m_pRuntime == NULL)
948 "Unable to load plugin (error %d: %s)",
950 g_pSourcePawn2->GetErrorString(err));
952 pPlugin->m_status = Plugin_BadLoad;
956 if (pPlugin->UpdateInfo())
958 pPlugin->m_status = Plugin_Created;
964 smcore.Format(error, maxlength, "%s", pPlugin->m_errormsg);
970 if (pPlugin->GetStatus() == Plugin_Created)
972 unsigned char *pCodeHash = pPlugin->m_pRuntime->GetCodeHash();
974 char codeHashBuf[40];
975 smcore.Format(codeHashBuf, 40, "plugin_");
976 for (int i = 0; i < 16; i++)
977 smcore.Format(codeHashBuf + 7 + (i * 2), 3, "%02x", pCodeHash[i]);
979 const char *bulletinUrl = g_pGameConf->GetKeyValue(codeHashBuf);
980 if (bulletinUrl != NULL)
982 if (m_bBlockBadPlugins)
986 if (bulletinUrl[0] != '\0')
988 smcore.Format(error, maxlength, "Known malware detected and blocked. See %s for more info", bulletinUrl);
990 smcore.Format(error, maxlength, "Possible malware or illegal plugin detected and blocked");
993 pPlugin->m_status = Plugin_BadLoad;
995 if (bulletinUrl[0] != '\0')
997 smcore.Log("%s: Known malware detected. See %s for more info, blocking disabled in core.cfg", pPlugin->GetFilename(), bulletinUrl);
999 smcore.Log("%s: Possible malware or illegal plugin detected, blocking disabled in core.cfg", pPlugin->GetFilename());
1005 LoadRes loadFailure = LoadRes_Failure;
1006 /* Get the status */
1007 if (pPlugin->GetStatus() == Plugin_Created)
1009 /* First native pass - add anything from Core */
1010 g_ShareSys.BindNativesToPlugin(pPlugin, true);
1011 pPlugin->InitIdentity();
1012 APLRes result = pPlugin->Call_AskPluginLoad(error, maxlength);
1015 case APLRes_Success:
1016 /* Autoload any modules */
1017 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
1019 case APLRes_Failure:
1020 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1021 loadFailure = LoadRes_Failure;
1023 case APLRes_SilentFailure:
1024 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1025 loadFailure = LoadRes_SilentFailure;
1026 pPlugin->SetSilentlyFailed(true);
1033 /* Save the time stamp */
1034 time_t t = pPlugin->GetFileTimeStamp();
1035 pPlugin->SetTimeStamp(t);
1042 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : loadFailure;
1045 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
1051 if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure)
1057 if (res == LoadRes_AlreadyLoaded)
1063 if (res == LoadRes_NeverLoad)
1067 if (m_LoadingLocked)
1069 smcore.Format(error, maxlength, "There is a global plugin loading lock in effect");
1073 smcore.Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
1081 /* Run second pass if we need to */
1082 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1084 if (!RunSecondPass(pl, error, maxlength))
1089 pl->Call_OnAllPluginsLoaded();
1095 void CPluginManager::LoadAutoPlugin(const char *plugin)
1099 char error[255] = "Unknown error";
1101 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1103 smcore.LogError("[SM] Failed to load plugin \"%s\": %s.", plugin, error);
1105 pl->GetStatus() <= Plugin_Created ? Plugin_BadLoad : pl->GetStatus(),
1110 if (res == LoadRes_Successful || res == LoadRes_Failure || res == LoadRes_SilentFailure)
1116 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1118 List<IPluginsListener *>::iterator iter;
1119 IPluginsListener *pListener;
1121 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1123 pListener = (*iter);
1124 pListener->OnPluginCreated(pPlugin);
1127 m_plugins.push_back(pPlugin);
1128 m_LoadLookup.insert(pPlugin->m_filename, pPlugin);
1131 void CPluginManager::LoadAll_SecondPass()
1133 List<CPlugin *>::iterator iter;
1137 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1140 if (pPlugin->GetStatus() == Plugin_Loaded)
1143 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1145 smcore.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1146 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1151 m_AllPluginsLoaded = true;
1154 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1163 IPluginContext *pBase = pPlugin->GetBaseContext();
1164 uint32_t num = pBase->GetPubVarsNum();
1165 sp_pubvar_t *pubvar;
1167 char pathfile[PLATFORM_MAX_PATH];
1169 for (uint32_t i=0; i<num; i++)
1171 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1175 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1177 pl = (_pl *)pubvar->offs;
1178 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1182 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1186 libsys->GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1187 if (strcmp(pathfile, file) == 0)
1191 if (pl->required == false)
1193 IPluginFunction *pFunc;
1195 smcore.Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1196 if ((pFunc=pBase->GetFunctionByName(buffer)))
1199 pFunc->Execute(&res);
1200 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1204 smcore.Format(error, maxlength, "Fatal error during initializing plugin load");
1212 /* Check that we aren't registering the same library twice */
1213 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1215 pPlugin->m_RequiredLibs.push_back(name);
1221 List<CPlugin *>::iterator iter;
1224 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1227 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1237 smcore.Format(error, maxlength, "Could not find required plugin \"%s\"", name);
1248 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1250 /* Find any extensions this plugin needs */
1259 IPluginContext *pBase = pPlugin->GetBaseContext();
1260 uint32_t num = pBase->GetPubVarsNum();
1261 sp_pubvar_t *pubvar;
1263 char path[PLATFORM_MAX_PATH];
1265 for (uint32_t i=0; i<num; i++)
1267 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1271 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1273 ext = (_ext *)pubvar->offs;
1274 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1278 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1284 /* Attempt to auto-load if necessary */
1287 libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1288 bool bErrorOnMissing = ext->required ? true : false;
1289 g_Extensions.LoadAutoExtension(path);
1294 /* Is this required? */
1297 libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1298 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1300 pExt = g_Extensions.FindExtensionByName(name);
1302 /* :TODO: should we bind to unloaded extensions?
1303 * Currently the extension manager will ignore this.
1305 if (!pExt || !pExt->IsRunning(NULL, 0))
1309 smcore.Format(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1315 g_Extensions.BindChildPlugin(pExt, pPlugin);
1320 IPluginFunction *pFunc;
1322 smcore.Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
1324 if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
1327 pFunc->Execute(&res);
1328 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1332 smcore.Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
1345 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1347 /* Second pass for extension requirements */
1348 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1353 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1358 /* Run another binding pass */
1359 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1361 /* Find any unbound natives. Right now, these are not allowed. */
1362 IPluginContext *pContext = pPlugin->GetBaseContext();
1363 uint32_t num = pContext->GetNativesNum();
1364 sp_native_t *native;
1365 for (unsigned int i=0; i<num; i++)
1367 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1371 if (native->status == SP_NATIVE_UNBOUND
1372 && native->name[0] != '@'
1373 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1377 smcore.Format(error, maxlength, "Native \"%s\" was not found", native->name);
1383 /* Finish by telling all listeners */
1384 List<IPluginsListener *>::iterator iter;
1385 IPluginsListener *pListener;
1386 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1388 pListener = (*iter);
1389 pListener->OnPluginLoaded(pPlugin);
1392 /* Tell this plugin to finish initializing itself */
1393 pPlugin->Call_OnPluginStart();
1395 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1396 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
1398 List<CPlugin *>::iterator pl_iter;
1400 for (pl_iter = m_plugins.begin();
1401 pl_iter != m_plugins.end();
1404 pOther = (*pl_iter);
1405 if ((pOther->GetStatus() == Plugin_Error
1406 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1407 || pOther->m_FakeNativesMissing)
1409 TryRefreshDependencies(pOther);
1411 else if ((pOther->GetStatus() == Plugin_Running
1412 || pOther->GetStatus() == Plugin_Paused)
1413 && pOther != pPlugin)
1415 List<NativeEntry *>::iterator nv_iter;
1416 for (nv_iter = pPlugin->m_Natives.begin();
1417 nv_iter != pPlugin->m_Natives.end();
1420 g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
1426 /* Go through our libraries and tell other plugins they're added */
1427 List<String>::iterator s_iter;
1428 for (s_iter = pPlugin->m_Libraries.begin();
1429 s_iter != pPlugin->m_Libraries.end();
1432 OnLibraryAction((*s_iter).c_str(), LibraryAction_Added);
1435 /* :TODO: optimize? does this even matter? */
1436 pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
1441 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1443 assert(pPlugin->GetBaseContext() != NULL);
1445 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1447 List<String>::iterator lib_iter;
1448 List<String>::iterator req_iter;
1449 List<CPlugin *>::iterator pl_iter;
1451 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1454 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1457 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1459 if ((*req_iter) == (*lib_iter))
1467 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1472 /* Find any unbound natives
1473 * Right now, these are not allowed
1475 IPluginContext *pContext = pPlugin->GetBaseContext();
1476 uint32_t num = pContext->GetNativesNum();
1477 sp_native_t *native;
1478 for (unsigned int i=0; i<num; i++)
1480 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1484 if (native->status == SP_NATIVE_UNBOUND
1485 && native->name[0] != '@'
1486 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1488 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1493 if (pPlugin->GetStatus() == Plugin_Error)
1495 /* If we got here, all natives are okay again! */
1496 pPlugin->m_status = Plugin_Running;
1497 if (pPlugin->m_pRuntime->IsPaused())
1499 pPlugin->m_pRuntime->SetPauseState(false);
1500 _SetPauseState(pPlugin, false);
1505 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1507 CPlugin *pPlugin = (CPlugin *)plugin;
1509 /* This prevents removal during insertion or anything else weird */
1510 if (m_plugins.find(pPlugin) == m_plugins.end())
1515 IPluginContext *pContext = plugin->GetBaseContext();
1516 if (pContext != NULL && pContext->IsInExec())
1519 smcore.Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
1520 engine->ServerCommand(buffer);
1524 /* Remove us from the lookup table and linked list */
1525 m_plugins.remove(pPlugin);
1526 m_LoadLookup.remove(pPlugin->m_filename);
1528 /* Go through our libraries and tell other plugins they're gone */
1529 List<String>::iterator s_iter;
1530 for (s_iter = pPlugin->m_Libraries.begin();
1531 s_iter != pPlugin->m_Libraries.end();
1534 OnLibraryAction((*s_iter).c_str(), LibraryAction_Removed);
1537 List<IPluginsListener *>::iterator iter;
1538 IPluginsListener *pListener;
1540 if (pPlugin->GetStatus() <= Plugin_Error)
1543 pPlugin->Call_OnPluginEnd();
1545 /* Notify listeners of unloading */
1546 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1548 pListener = (*iter);
1549 pListener->OnPluginUnloaded(pPlugin);
1553 pPlugin->DropEverything();
1555 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1557 /* Notify listeners of destruction */
1558 pListener = (*iter);
1559 pListener->OnPluginDestroyed(pPlugin);
1562 /* Tell the plugin to delete itself */
1568 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1571 IPluginContext *pContext;
1573 pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
1575 if (pContext->GetKey(2, (void **)&pPlugin))
1583 CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
1585 return (CPlugin *)FindPluginByContext(ctx);
1588 unsigned int CPluginManager::GetPluginCount()
1590 return m_plugins.size();
1593 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1595 m_listeners.push_back(listener);
1598 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1600 m_listeners.remove(listener);
1603 IPluginIterator *CPluginManager::GetPluginIterator()
1605 if (m_iters.empty())
1607 return new CPluginIterator(&m_plugins);
1609 CPluginIterator *iter = m_iters.front();
1616 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1621 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1623 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1624 size_t alias_len = 0;
1625 size_t local_len = 0;
1627 const char *ptr = alias;
1628 unsigned int alias_explicit_paths = 0;
1629 unsigned int alias_path_end = 0;
1630 while (*ptr != '\0')
1632 if (*ptr == '\\' || *ptr == '/')
1634 alias_explicit_paths++;
1635 alias_path_end = alias_len;
1641 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1643 /* Trailing slash is totally invalid here */
1648 unsigned int local_explicit_paths = 0;
1649 unsigned int local_path_end = 0;
1650 while (*ptr != '\0')
1652 if (*ptr == '\\' || *ptr == '/')
1654 local_explicit_paths++;
1655 local_path_end = local_len;
1661 /* If the alias has more explicit paths than the real path,
1662 * no match will be possible.
1664 if (alias_explicit_paths > local_explicit_paths)
1669 if (alias_explicit_paths)
1671 /* We need to find if the paths match now. For example, these should all match:
1673 * csdm optional/csdm
1674 * csdm/ban optional/crab/csdm/ban
1676 const char *aliasptr = alias;
1677 const char *localptr = localpath;
1681 if (*aliasptr != *localptr)
1683 /* We have to knock one path off */
1684 local_explicit_paths--;
1685 if (alias_explicit_paths > local_explicit_paths)
1687 /* Skip out if we're gonna have an impossible match */
1690 /* Eat up localptr tokens until we get a result */
1691 while (((localptr - localpath) < (int)local_path_end)
1693 && *localptr != '\\')
1697 /* Check if we hit the end of our searchable area.
1698 * This probably isn't possible because of the path
1699 * count check, but it's a good idea anyway.
1701 if ((localptr - localpath) >= (int)local_path_end)
1705 /* Consume the slash token */
1708 /* Reset the alias pointer so we can continue consuming */
1714 * This is safe because if localptr terminates early, aliasptr will too
1718 /* We should never reach the end of the string because of this check. */
1719 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1720 bool localend = (localptr - localpath) > (int)local_path_end;
1721 if (aliasend || localend)
1723 if (aliasend && localend)
1725 /* we matched, and we can break out now */
1729 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1734 /* If we got here, it's safe to compare the next two tokens */
1735 if (*localptr != *aliasptr)
1746 /* If we got here, it's time to compare filenames */
1747 const char *aliasptr = alias;
1748 const char *localptr = localpath;
1750 if (alias_explicit_paths)
1752 aliasptr = &alias[alias_path_end + 1];
1755 if (local_explicit_paths)
1757 localptr = &localpath[local_path_end + 1];
1762 if (*aliasptr == '*')
1764 /* First, see if this is the last character */
1765 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1767 /* If so, there's no need to match anything else */
1770 /* Otherwise, we need to search for an appropriate matching sequence in local.
1771 * Note that we only need to search up to the next asterisk.
1775 const char *local_orig = localptr;
1779 while (*aliasptr != '\0' && *aliasptr != '*')
1781 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1782 if (*aliasptr != *localptr)
1792 /* If we didn't get a match, we need to advance the search stream.
1793 * This will let us skip tokens while still searching for another match.
1795 localptr = ++local_orig;
1796 /* Make sure we don't go out of bounds */
1797 if (*localptr == '\0')
1808 /* If we got a match, move on to the next token */
1811 } else if (*aliasptr == '\0') {
1812 if (*localptr == '\0'
1814 strcmp(localptr, ".smx") == 0)
1820 } else if (*aliasptr != *localptr) {
1830 bool CPluginManager::IsLateLoadTime() const
1832 return (m_AllPluginsLoaded || !smcore.IsMapLoading());
1835 void CPluginManager::OnSourceModAllInitialized()
1837 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1840 handlesys->InitAccessDefaults(NULL, &sec);
1842 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1843 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1845 g_PluginType = handlesys->CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1846 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1848 rootmenu->AddRootConsoleCommand("plugins", "Manage Plugins", this);
1850 g_ShareSys.AddInterface(NULL, GetOldAPI());
1852 m_pOnLibraryAdded = forwardsys->CreateForward("OnLibraryAdded", ET_Ignore, 1, NULL, Param_String);
1853 m_pOnLibraryRemoved = forwardsys->CreateForward("OnLibraryRemoved", ET_Ignore, 1, NULL, Param_String);
1856 void CPluginManager::OnSourceModShutdown()
1858 rootmenu->RemoveRootConsoleCommand("plugins", this);
1862 handlesys->RemoveType(g_PluginType, m_MyIdent);
1863 g_ShareSys.DestroyIdentType(g_PluginIdent);
1864 g_ShareSys.DestroyIdentity(m_MyIdent);
1866 forwardsys->ReleaseForward(m_pOnLibraryAdded);
1867 forwardsys->ReleaseForward(m_pOnLibraryRemoved);
1870 ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
1872 ConfigSource source,
1876 if (strcmp(key, "BlockBadPlugins") == 0) {
1877 if (strcasecmp(value, "yes") == 0)
1879 m_bBlockBadPlugins = true;
1880 } else if (strcasecmp(value, "no") == 0) {
1881 m_bBlockBadPlugins = false;
1883 smcore.Format(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
1884 return ConfigResult_Reject;
1886 return ConfigResult_Accept;
1888 return ConfigResult_Ignore;
1891 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1893 /* We don't care about the internal object, actually */
1896 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1898 *pSize = ((CPlugin *)object)->CalcMemUsage();
1902 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1910 sec.pIdentity = m_MyIdent;
1912 if ((_err=handlesys->ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1925 CPlugin *CPluginManager::GetPluginByOrder(int num)
1927 if (num < 1 || num > (int)GetPluginCount())
1935 SourceHook::List<CPlugin *>::iterator iter;
1936 for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
1943 const char *CPluginManager::GetStatusText(PluginStatus st)
1947 case Plugin_Running:
1953 case Plugin_Uncompiled:
1954 return "Uncompiled";
1955 case Plugin_BadLoad:
1965 static inline bool IS_STR_FILLED(const char *text)
1967 return text[0] != '\0';
1970 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1972 int argcount = smcore.Argc(command);
1975 const char *cmd = smcore.Arg(command, 2);
1976 if (strcmp(cmd, "list") == 0)
1979 unsigned int id = 1;
1980 int plnum = GetPluginCount();
1984 rootmenu->ConsolePrint("[SM] No plugins loaded");
1989 rootmenu->ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1993 SourceHook::List<CPlugin *>::iterator iter;
1994 SourceHook::List<CPlugin *> m_FailList;
1996 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
1999 assert(pl->GetStatus() != Plugin_Created);
2001 const sm_plugininfo_t *info = pl->GetPublicInfo();
2002 if (pl->GetStatus() != Plugin_Running && !pl->IsSilentlyFailed())
2004 len += smcore.Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
2006 if (pl->GetStatus() <= Plugin_Error)
2008 /* Plugin has failed to load. */
2009 m_FailList.push_back(pl);
2014 len += smcore.Format(buffer, sizeof(buffer), " %02d", id);
2016 if (pl->GetStatus() < Plugin_Created)
2018 if (pl->IsSilentlyFailed())
2019 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " Disabled:");
2020 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2021 if (IS_STR_FILLED(info->version))
2023 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
2025 if (IS_STR_FILLED(info->author))
2027 smcore.Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
2032 smcore.Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename);
2034 rootmenu->ConsolePrint("%s", buffer);
2037 if (!m_FailList.empty())
2039 rootmenu->ConsolePrint("Load Errors:");
2041 SourceHook::List<CPlugin *>::iterator _iter;
2045 for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
2047 pl = (CPlugin *)*_iter;
2048 rootmenu->ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
2054 else if (strcmp(cmd, "load") == 0)
2058 rootmenu->ConsolePrint("[SM] Usage: sm plugins load <file>");
2064 const char *filename = smcore.Arg(command, 3);
2066 char pluginfile[256];
2067 const char *ext = libsys->GetFileExtension(filename) ? "" : ".smx";
2068 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
2070 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
2074 rootmenu->ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
2080 rootmenu->ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
2084 rootmenu->ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
2089 else if (strcmp(cmd, "unload") == 0)
2093 rootmenu->ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2099 const char *arg = smcore.Arg(command, 3);
2100 int id = strtol(arg, &end, 10);
2104 pl = GetPluginByOrder(id);
2107 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2113 char pluginfile[256];
2114 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2115 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2117 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2118 if (!pluginpp || !*pluginpp)
2120 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2126 char name[PLATFORM_MAX_PATH];
2128 if (pl->GetStatus() < Plugin_Created)
2130 const sm_plugininfo_t *info = pl->GetPublicInfo();
2131 smcore.Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2135 smcore.Format(name, sizeof(name), "%s", pl->GetFilename());
2138 if (UnloadPlugin(pl))
2140 rootmenu->ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2144 rootmenu->ConsolePrint("[SM] Failed to unload plugin %s.", name);
2149 else if (strcmp(cmd, "unload_all") == 0)
2151 g_PluginSys.UnloadAll();
2152 rootmenu->ConsolePrint("[SM] All plugins have been unloaded.");
2155 else if (strcmp(cmd, "load_lock") == 0)
2157 if (m_LoadingLocked)
2159 rootmenu->ConsolePrint("[SM] There is already a loading lock in effect.");
2163 m_LoadingLocked = true;
2164 rootmenu->ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
2168 else if (strcmp(cmd, "load_unlock") == 0)
2170 if (m_LoadingLocked)
2172 m_LoadingLocked = false;
2173 rootmenu->ConsolePrint("[SM] The loading lock is no longer in effect.");
2177 rootmenu->ConsolePrint("[SM] There was no loading lock in effect.");
2181 else if (strcmp(cmd, "info") == 0)
2185 rootmenu->ConsolePrint("[SM] Usage: sm plugins info <#>");
2191 const char *arg = smcore.Arg(command, 3);
2192 int id = strtol(arg, &end, 10);
2196 pl = GetPluginByOrder(id);
2199 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2205 char pluginfile[256];
2206 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2207 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2209 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2210 if (!pluginpp || !*pluginpp)
2212 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2218 const sm_plugininfo_t *info = pl->GetPublicInfo();
2220 rootmenu->ConsolePrint(" Filename: %s", pl->GetFilename());
2221 if (pl->GetStatus() <= Plugin_Error)
2223 if (IS_STR_FILLED(info->name))
2225 if (IS_STR_FILLED(info->description))
2227 rootmenu->ConsolePrint(" Title: %s (%s)", info->name, info->description);
2229 rootmenu->ConsolePrint(" Title: %s", info->name);
2232 if (IS_STR_FILLED(info->author))
2234 rootmenu->ConsolePrint(" Author: %s", info->author);
2236 if (IS_STR_FILLED(info->version))
2238 rootmenu->ConsolePrint(" Version: %s", info->version);
2240 if (IS_STR_FILLED(info->url))
2242 rootmenu->ConsolePrint(" URL: %s", info->url);
2244 if (pl->GetStatus() == Plugin_Error)
2246 rootmenu->ConsolePrint(" Error: %s", pl->m_errormsg);
2250 if (pl->GetStatus() == Plugin_Running)
2252 rootmenu->ConsolePrint(" Status: running");
2256 rootmenu->ConsolePrint(" Status: not running");
2259 const char *typestr = "";
2260 switch (pl->GetType())
2262 case PluginType_MapUpdated:
2263 typestr = "Map Change if Updated";
2265 case PluginType_MapOnly:
2266 typestr = "Map Change";
2268 case PluginType_Private:
2269 case PluginType_Global:
2274 rootmenu->ConsolePrint(" Reloads: %s", typestr);
2276 if (pl->m_FileVersion >= 3)
2278 rootmenu->ConsolePrint(" Timestamp: %s", pl->m_DateTime);
2281 unsigned char *pCodeHash = pl->m_pRuntime->GetCodeHash();
2282 unsigned char *pDataHash = pl->m_pRuntime->GetDataHash();
2284 char combinedHash[33];
2285 for (int i = 0; i < 16; i++)
2286 smcore.Format(combinedHash + (i * 2), 3, "%02x", pCodeHash[i] ^ pDataHash[i]);
2288 rootmenu->ConsolePrint(" Hash: %s", combinedHash);
2292 rootmenu->ConsolePrint(" Load error: %s", pl->m_errormsg);
2293 if (pl->GetStatus() < Plugin_Created)
2295 rootmenu->ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2296 info->name ? info->name : "<none>",
2297 info->version ? info->version : "<none>");
2298 if (IS_STR_FILLED(info->url))
2300 rootmenu->ConsolePrint(" File URL: %s", info->url);
2307 else if (strcmp(cmd, "refresh") == 0)
2309 smcore.DoGlobalPluginLoads();
2310 rootmenu->ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
2313 else if (strcmp(cmd, "reload") == 0)
2317 rootmenu->ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
2323 const char *arg = smcore.Arg(command, 3);
2324 int id = strtol(arg, &end, 10);
2328 pl = GetPluginByOrder(id);
2331 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2337 char pluginfile[256];
2338 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2339 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2341 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2342 if (!pluginpp || !*pluginpp)
2344 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2350 char name[PLATFORM_MAX_PATH];
2351 const sm_plugininfo_t *info = pl->GetPublicInfo();
2352 if (pl->GetStatus() <= Plugin_Paused)
2353 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2355 strcpy(name, pl->GetFilename());
2357 if (ReloadPlugin(pl))
2359 rootmenu->ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
2363 rootmenu->ConsolePrint("[SM] Failed to reload plugin %s.", name);
2370 /* Draw the main menu */
2371 rootmenu->ConsolePrint("SourceMod Plugins Menu:");
2372 rootmenu->DrawGenericOption("info", "Information about a plugin");
2373 rootmenu->DrawGenericOption("list", "Show loaded plugins");
2374 rootmenu->DrawGenericOption("load", "Load a plugin");
2375 rootmenu->DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
2376 rootmenu->DrawGenericOption("load_unlock", "Re-enables plugin loading");
2377 rootmenu->DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
2378 rootmenu->DrawGenericOption("reload", "Reloads a plugin");
2379 rootmenu->DrawGenericOption("unload", "Unload a plugin");
2380 rootmenu->DrawGenericOption("unload_all", "Unloads all plugins");
2383 bool CPluginManager::ReloadPlugin(CPlugin *pl)
2385 List<CPlugin *>::iterator iter;
2386 char filename[PLATFORM_MAX_PATH];
2392 strcpy(filename, pl->m_filename);
2393 ptype = pl->GetType();
2395 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
2403 if (!UnloadPlugin(pl))
2407 if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded)))
2412 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2414 if ((*iter) == (CPlugin *)newpl)
2416 m_plugins.erase(iter);
2422 for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
2425 m_plugins.insert(iter, (CPlugin *)newpl);
2430 void CPluginManager::RefreshAll()
2432 /* If we're in a load lock, just skip this whole bit. */
2433 if (m_LoadingLocked)
2438 List<CPlugin *>::iterator iter;
2439 List<CPlugin *> tmp_list = m_plugins;
2443 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2446 if (pl->GetType() == PluginType_MapOnly)
2448 UnloadPlugin((IPlugin *)pl);
2450 else if (pl->GetType() == PluginType_MapUpdated)
2452 t = pl->GetFileTimeStamp();
2453 if (!t || t > pl->GetTimeStamp())
2455 pl->SetTimeStamp(t);
2456 UnloadPlugin((IPlugin *)pl);
2462 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2464 List<IPluginsListener *>::iterator iter;
2465 IPluginsListener *pListener;
2466 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2468 pListener = (*iter);
2469 pListener->OnPluginPauseChange(pl, paused);
2473 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2475 List<CPlugin *>::iterator iter;
2477 IPluginFunction *pFunction;
2479 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2483 if (pPlugin->GetStatus() <= Plugin_Paused)
2485 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2489 pForward->AddFunction(pFunction);
2495 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2497 if (pToken->type != g_PluginIdent)
2502 return (CPlugin *)(pToken->ptr);
2505 void CPluginManager::OnLibraryAction(const char *lib, LibraryAction action)
2509 case LibraryAction_Removed:
2510 m_pOnLibraryRemoved->PushString(lib);
2511 m_pOnLibraryRemoved->Execute(NULL);
2513 case LibraryAction_Added:
2514 m_pOnLibraryAdded->PushString(lib);
2515 m_pOnLibraryAdded->Execute(NULL);
2520 bool CPluginManager::LibraryExists(const char *lib)
2522 List<CPlugin *>::iterator iter;
2524 for (iter=m_plugins.begin();
2525 iter!=m_plugins.end();
2528 CPlugin *pl = (*iter);
2529 if (pl->GetStatus() != Plugin_Running)
2533 List<String>::iterator s_iter;
2534 for (s_iter = pl->m_Libraries.begin();
2535 s_iter != pl->m_Libraries.end();
2538 if ((*s_iter).compare(lib) == 0)
2548 void CPluginManager::AllPluginsLoaded()
2550 List<CPlugin *>::iterator iter;
2553 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2556 pl->Call_OnAllPluginsLoaded();
2560 void CPluginManager::UnloadAll()
2562 List<CPlugin *>::iterator iter;
2563 while ( (iter = m_plugins.begin()) != m_plugins.end() )
2565 UnloadPlugin((*iter));
2569 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
2572 List<CPlugin *>::iterator iter;
2574 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2585 SMPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
2591 id = strtol(arg, &end, 10);
2595 pl = GetPluginByOrder(id);
2603 char pluginfile[256];
2604 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2605 smcore.Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2607 CPlugin **pluginpp = m_LoadLookup.retrieve(pluginfile);
2608 if (!pluginpp || !*pluginpp)
2617 void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue)
2619 SyncMaxClients(newvalue);
2622 void CPluginManager::SyncMaxClients(int max_clients)
2624 List<CPlugin *>::iterator iter;
2626 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2628 (*iter)->SyncMaxClients(max_clients);
2632 const CVector<SMPlugin *> *CPluginManager::ListPlugins()
2634 CVector<SMPlugin *> *list = new CVector<SMPlugin *>();
2636 for (List<CPlugin *>::iterator iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2637 list->push_back((*iter));
2642 void CPluginManager::FreePluginList(const CVector<SMPlugin *> *list)
2644 delete const_cast<CVector<SMPlugin *> *>(list);
2647 class OldPluginAPI : public IPluginManager
2650 IPlugin *LoadPlugin(const char *path,
2657 return g_PluginSys.LoadPlugin(path, debug, type, error, maxlength, wasloaded);
2660 bool UnloadPlugin(IPlugin *plugin)
2662 return g_PluginSys.UnloadPlugin(plugin);
2665 IPlugin *FindPluginByContext(const sp_context_t *ctx)
2667 return g_PluginSys.FindPluginByContext(ctx);
2670 unsigned int GetPluginCount()
2672 return g_PluginSys.GetPluginCount();
2675 IPluginIterator *GetPluginIterator()
2677 return g_PluginSys.GetPluginIterator();
2680 void AddPluginsListener(IPluginsListener *listener)
2682 g_PluginSys.AddPluginsListener(listener);
2685 void RemovePluginsListener(IPluginsListener *listener)
2687 g_PluginSys.RemovePluginsListener(listener);
2690 IPlugin *PluginFromHandle(Handle_t handle, HandleError *err)
2692 return g_PluginSys.PluginFromHandle(handle, err);
2696 static OldPluginAPI sOldPluginAPI;
2698 IPluginManager *CPluginManager::GetOldAPI()
2700 return &sOldPluginAPI;