Update CS:GO team score gamedata.
2 * vim: set ts=4 sw=4 tw=99 noet :
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_Props.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 StringHashMap<void *>::Result r = m_Props.find(prop);
185 bool CPlugin::SetProperty(const char *prop, void *ptr)
187 return m_Props.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)
601 List<String>::iterator reqlib_iter;
602 List<String>::iterator lib_iter;
603 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
605 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
607 if ((*reqlib_iter) == (*lib_iter))
608 m_LibraryMissing = true;
612 unsigned int unbound = 0;
613 for (size_t i = 0; i < pOwner->m_fakes.length(); i++)
615 ke::Ref<Native> entry(pOwner->m_fakes[i]);
618 if (m_pRuntime->FindNativeByName(entry->name(), &idx) != SP_ERROR_NONE)
622 m_pRuntime->GetNativeByIndex(idx, &native);
625 native->status = SP_NATIVE_UNBOUND;
631 m_FakeNativesMissing = true;
634 /* :IDEA: in the future, add native trapping? */
635 if (m_FakeNativesMissing || m_LibraryMissing)
637 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
641 size_t CPlugin::GetConfigCount()
643 return (unsigned int)m_configs.size();
646 AutoConfig *CPlugin::GetConfig(size_t i)
648 if (i >= GetConfigCount())
656 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
658 /* Do a check for duplicates to prevent double-execution */
659 for (size_t i = 0; i < m_configs.size(); i++)
661 if (m_configs[i]->autocfg.compare(cfg) == 0
662 && m_configs[i]->folder.compare(folder) == 0
663 && m_configs[i]->create == autoCreate)
669 AutoConfig *c = new AutoConfig;
673 c->create = autoCreate;
675 m_configs.push_back(c);
678 void CPlugin::DropEverything()
681 List<WeakNative>::iterator wk_iter;
683 /* Tell everyone that depends on us that we're about to drop */
684 for (List<CPlugin *>::iterator iter = m_Dependents.begin();
685 iter != m_Dependents.end();
688 pOther = static_cast<CPlugin *>(*iter);
689 pOther->DependencyDropped(this);
692 /* Note: we don't care about things we depend on.
693 * The reason is that extensions have their own cleanup
694 * code for plugins. Although the "right" design would be
695 * to centralize that here, i'm omitting it for now. Thus,
696 * the code below to walk the plugins list will suffice.
699 /* Other plugins could be holding weak references that were
700 * added by us. We need to clean all of those up now.
702 for (List<CPlugin *>::iterator iter = g_PluginSys.m_plugins.begin();
703 iter != g_PluginSys.m_plugins.end();
706 (*iter)->ToNativeOwner()->DropRefsTo(this);
709 /* Proceed with the rest of the necessities. */
710 CNativeOwner::DropEverything();
713 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
715 ke::Ref<Native> entry = g_ShareSys.AddFakeNative(pFunc, name, func);
719 m_fakes.append(entry);
727 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
733 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
738 bool CPluginManager::CPluginIterator::MorePlugins()
740 return (current != mylist->end());
743 void CPluginManager::CPluginIterator::NextPlugin()
748 void CPluginManager::CPluginIterator::Release()
750 g_PluginSys.ReleaseIterator(this);
753 CPluginManager::CPluginIterator::~CPluginIterator()
757 void CPluginManager::CPluginIterator::Reset()
759 current = mylist->begin();
766 CPluginManager::CPluginManager()
768 m_AllPluginsLoaded = false;
770 m_LoadingLocked = false;
772 m_bBlockBadPlugins = true;
775 CPluginManager::~CPluginManager()
777 CStack<CPluginManager::CPluginIterator *>::iterator iter;
778 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
785 void CPluginManager::Shutdown()
787 List<CPlugin *>::iterator iter;
789 while ((iter = m_plugins.begin()) != m_plugins.end())
795 void CPluginManager::LoadAll(const char *config_path, const char *plugins_path)
797 LoadAll_FirstPass(config_path, plugins_path);
798 g_Extensions.MarkAllLoaded();
799 LoadAll_SecondPass();
800 g_Extensions.MarkAllLoaded();
804 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
806 /* First read in the database of plugin settings */
807 m_AllPluginsLoaded = false;
808 LoadPluginsFromDir(basedir, NULL);
811 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
813 char base_path[PLATFORM_MAX_PATH];
815 /* Form the current path to start reading from */
816 if (localpath == NULL)
818 libsys->PathFormat(base_path, sizeof(base_path), "%s", basedir);
820 libsys->PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
823 IDirectory *dir = libsys->OpenDirectory(base_path);
828 libsys->GetPlatformError(error, sizeof(error));
829 smcore.LogError("[SM] Failure reading from plugins path: %s", localpath);
830 smcore.LogError("[SM] Platform returned error: %s", error);
834 while (dir->MoreFiles())
836 if (dir->IsEntryDirectory()
837 && (strcmp(dir->GetEntryName(), ".") != 0)
838 && (strcmp(dir->GetEntryName(), "..") != 0)
839 && (strcmp(dir->GetEntryName(), "disabled") != 0)
840 && (strcmp(dir->GetEntryName(), "optional") != 0))
842 char new_local[PLATFORM_MAX_PATH];
843 if (localpath == NULL)
845 /* If no path yet, don't add a former slash */
846 smcore.Format(new_local, sizeof(new_local), "%s", dir->GetEntryName());
848 libsys->PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
850 LoadPluginsFromDir(basedir, new_local);
851 } else if (dir->IsEntryFile()) {
852 const char *name = dir->GetEntryName();
853 size_t len = strlen(name);
855 && strcmp(&name[len-4], ".smx") == 0)
857 /* If the filename matches, load the plugin */
858 char plugin[PLATFORM_MAX_PATH];
859 if (localpath == NULL)
861 smcore.Format(plugin, sizeof(plugin), "%s", name);
863 libsys->PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
865 LoadAutoPlugin(plugin);
870 libsys->CloseDirectory(dir);
873 LoadRes CPluginManager::_LoadPlugin(CPlugin **aResult, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
876 return LoadRes_NeverLoad;
881 * Does this plugin already exist?
884 if (m_LoadLookup.retrieve(path, &pPlugin))
886 /* Check to see if we should try reloading it */
887 if (pPlugin->GetStatus() == Plugin_BadLoad
888 || pPlugin->GetStatus() == Plugin_Error
889 || pPlugin->GetStatus() == Plugin_Failed)
891 UnloadPlugin(pPlugin);
897 return LoadRes_AlreadyLoaded;
901 pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
902 assert(pPlugin != NULL);
904 pPlugin->m_type = PluginType_MapUpdated;
906 ICompilation *co = NULL;
908 if (pPlugin->m_status == Plugin_Uncompiled)
910 co = g_pSourcePawn2->StartCompilation();
913 /* Do the actual compiling */
916 char fullpath[PLATFORM_MAX_PATH];
917 g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
919 pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err);
920 if (pPlugin->m_pRuntime == NULL)
926 "Unable to load plugin (error %d: %s)",
928 g_pSourcePawn2->GetErrorString(err));
930 pPlugin->m_status = Plugin_BadLoad;
934 if (pPlugin->UpdateInfo())
936 pPlugin->m_status = Plugin_Created;
942 smcore.Format(error, maxlength, "%s", pPlugin->m_errormsg);
948 if (pPlugin->GetStatus() == Plugin_Created)
950 unsigned char *pCodeHash = pPlugin->m_pRuntime->GetCodeHash();
952 char codeHashBuf[40];
953 smcore.Format(codeHashBuf, 40, "plugin_");
954 for (int i = 0; i < 16; i++)
955 smcore.Format(codeHashBuf + 7 + (i * 2), 3, "%02x", pCodeHash[i]);
957 const char *bulletinUrl = g_pGameConf->GetKeyValue(codeHashBuf);
958 if (bulletinUrl != NULL)
960 if (m_bBlockBadPlugins)
964 if (bulletinUrl[0] != '\0')
966 smcore.Format(error, maxlength, "Known malware detected and blocked. See %s for more info", bulletinUrl);
968 smcore.Format(error, maxlength, "Possible malware or illegal plugin detected and blocked");
971 pPlugin->m_status = Plugin_BadLoad;
973 if (bulletinUrl[0] != '\0')
975 smcore.Log("%s: Known malware detected. See %s for more info, blocking disabled in core.cfg", pPlugin->GetFilename(), bulletinUrl);
977 smcore.Log("%s: Possible malware or illegal plugin detected, blocking disabled in core.cfg", pPlugin->GetFilename());
983 LoadRes loadFailure = LoadRes_Failure;
985 if (pPlugin->GetStatus() == Plugin_Created)
987 /* First native pass - add anything from Core */
988 g_ShareSys.BindNativesToPlugin(pPlugin, true);
989 pPlugin->InitIdentity();
990 APLRes result = pPlugin->Call_AskPluginLoad(error, maxlength);
994 /* Autoload any modules */
995 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
998 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
999 loadFailure = LoadRes_Failure;
1001 case APLRes_SilentFailure:
1002 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1003 loadFailure = LoadRes_SilentFailure;
1004 pPlugin->SetSilentlyFailed(true);
1011 /* Save the time stamp */
1012 time_t t = pPlugin->GetFileTimeStamp();
1013 pPlugin->SetTimeStamp(t);
1018 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : loadFailure;
1021 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
1027 if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure)
1033 if (res == LoadRes_AlreadyLoaded)
1039 if (res == LoadRes_NeverLoad)
1043 if (m_LoadingLocked)
1045 smcore.Format(error, maxlength, "There is a global plugin loading lock in effect");
1049 smcore.Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
1057 /* Run second pass if we need to */
1058 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1060 if (!RunSecondPass(pl, error, maxlength))
1065 pl->Call_OnAllPluginsLoaded();
1071 void CPluginManager::LoadAutoPlugin(const char *plugin)
1075 char error[255] = "Unknown error";
1077 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1079 smcore.LogError("[SM] Failed to load plugin \"%s\": %s.", plugin, error);
1081 pl->GetStatus() <= Plugin_Created ? Plugin_BadLoad : pl->GetStatus(),
1086 if (res == LoadRes_Successful || res == LoadRes_Failure || res == LoadRes_SilentFailure)
1092 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1094 List<IPluginsListener *>::iterator iter;
1095 IPluginsListener *pListener;
1097 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1099 pListener = (*iter);
1100 pListener->OnPluginCreated(pPlugin);
1103 m_plugins.push_back(pPlugin);
1104 m_LoadLookup.insert(pPlugin->m_filename, pPlugin);
1107 void CPluginManager::LoadAll_SecondPass()
1109 List<CPlugin *>::iterator iter;
1113 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1116 if (pPlugin->GetStatus() == Plugin_Loaded)
1119 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1121 smcore.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1122 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1127 m_AllPluginsLoaded = true;
1130 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1139 IPluginContext *pBase = pPlugin->GetBaseContext();
1140 uint32_t num = pBase->GetPubVarsNum();
1141 sp_pubvar_t *pubvar;
1143 char pathfile[PLATFORM_MAX_PATH];
1145 for (uint32_t i=0; i<num; i++)
1147 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1151 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1153 pl = (_pl *)pubvar->offs;
1154 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1158 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1162 libsys->GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1163 if (strcmp(pathfile, file) == 0)
1167 if (pl->required == false)
1169 IPluginFunction *pFunc;
1171 smcore.Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1172 if ((pFunc=pBase->GetFunctionByName(buffer)))
1175 pFunc->Execute(&res);
1176 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1180 smcore.Format(error, maxlength, "Fatal error during initializing plugin load");
1188 /* Check that we aren't registering the same library twice */
1189 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1191 pPlugin->m_RequiredLibs.push_back(name);
1197 List<CPlugin *>::iterator iter;
1200 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1203 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1213 smcore.Format(error, maxlength, "Could not find required plugin \"%s\"", name);
1224 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1226 /* Find any extensions this plugin needs */
1235 IPluginContext *pBase = pPlugin->GetBaseContext();
1236 uint32_t num = pBase->GetPubVarsNum();
1237 sp_pubvar_t *pubvar;
1239 char path[PLATFORM_MAX_PATH];
1241 for (uint32_t i=0; i<num; i++)
1243 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1247 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1249 ext = (_ext *)pubvar->offs;
1250 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1254 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1260 /* Attempt to auto-load if necessary */
1263 libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1264 bool bErrorOnMissing = ext->required ? true : false;
1265 g_Extensions.LoadAutoExtension(path, bErrorOnMissing);
1270 /* Is this required? */
1273 libsys->PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1274 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1276 pExt = g_Extensions.FindExtensionByName(name);
1278 /* :TODO: should we bind to unloaded extensions?
1279 * Currently the extension manager will ignore this.
1281 if (!pExt || !pExt->IsRunning(NULL, 0))
1285 smcore.Format(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1291 g_Extensions.BindChildPlugin(pExt, pPlugin);
1296 IPluginFunction *pFunc;
1298 smcore.Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
1300 if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
1303 pFunc->Execute(&res);
1304 if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE)
1308 smcore.Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
1321 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1323 /* Second pass for extension requirements */
1324 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1329 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1334 /* Run another binding pass */
1335 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1337 /* Find any unbound natives. Right now, these are not allowed. */
1338 IPluginContext *pContext = pPlugin->GetBaseContext();
1339 uint32_t num = pContext->GetNativesNum();
1340 sp_native_t *native;
1341 for (unsigned int i=0; i<num; i++)
1343 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1347 if (native->status == SP_NATIVE_UNBOUND
1348 && native->name[0] != '@'
1349 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1353 smcore.Format(error, maxlength, "Native \"%s\" was not found", native->name);
1359 /* Finish by telling all listeners */
1360 List<IPluginsListener *>::iterator iter;
1361 IPluginsListener *pListener;
1362 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1364 pListener = (*iter);
1365 pListener->OnPluginLoaded(pPlugin);
1368 /* Tell this plugin to finish initializing itself */
1369 pPlugin->Call_OnPluginStart();
1371 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1372 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_fakes.length())
1374 List<CPlugin *>::iterator pl_iter;
1375 for (pl_iter = m_plugins.begin();
1376 pl_iter != m_plugins.end();
1379 CPlugin *pOther = (*pl_iter);
1380 if ((pOther->GetStatus() == Plugin_Error
1381 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1382 || pOther->m_FakeNativesMissing)
1384 TryRefreshDependencies(pOther);
1386 else if ((pOther->GetStatus() == Plugin_Running
1387 || pOther->GetStatus() == Plugin_Paused)
1388 && pOther != pPlugin)
1390 for (size_t i = 0; i < pPlugin->m_fakes.length(); i++)
1391 g_ShareSys.BindNativeToPlugin(pOther, pPlugin->m_fakes[i]);
1396 /* Go through our libraries and tell other plugins they're added */
1397 List<String>::iterator s_iter;
1398 for (s_iter = pPlugin->m_Libraries.begin();
1399 s_iter != pPlugin->m_Libraries.end();
1402 OnLibraryAction((*s_iter).c_str(), LibraryAction_Added);
1405 /* :TODO: optimize? does this even matter? */
1406 pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
1411 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1413 assert(pPlugin->GetBaseContext() != NULL);
1415 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1417 List<String>::iterator lib_iter;
1418 List<String>::iterator req_iter;
1419 List<CPlugin *>::iterator pl_iter;
1421 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1424 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1427 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1429 if ((*req_iter) == (*lib_iter))
1437 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1442 /* Find any unbound natives
1443 * Right now, these are not allowed
1445 IPluginContext *pContext = pPlugin->GetBaseContext();
1446 uint32_t num = pContext->GetNativesNum();
1447 sp_native_t *native;
1448 for (unsigned int i=0; i<num; i++)
1450 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1454 if (native->status == SP_NATIVE_UNBOUND
1455 && native->name[0] != '@'
1456 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1458 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1463 if (pPlugin->GetStatus() == Plugin_Error)
1465 /* If we got here, all natives are okay again! */
1466 pPlugin->m_status = Plugin_Running;
1467 if (pPlugin->m_pRuntime->IsPaused())
1469 pPlugin->m_pRuntime->SetPauseState(false);
1470 _SetPauseState(pPlugin, false);
1475 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1477 CPlugin *pPlugin = (CPlugin *)plugin;
1479 /* This prevents removal during insertion or anything else weird */
1480 if (m_plugins.find(pPlugin) == m_plugins.end())
1485 IPluginContext *pContext = plugin->GetBaseContext();
1486 if (pContext != NULL && pContext->IsInExec())
1489 smcore.Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
1490 engine->ServerCommand(buffer);
1494 /* Remove us from the lookup table and linked list */
1495 m_plugins.remove(pPlugin);
1496 m_LoadLookup.remove(pPlugin->m_filename);
1498 /* Go through our libraries and tell other plugins they're gone */
1499 List<String>::iterator s_iter;
1500 for (s_iter = pPlugin->m_Libraries.begin();
1501 s_iter != pPlugin->m_Libraries.end();
1504 OnLibraryAction((*s_iter).c_str(), LibraryAction_Removed);
1507 List<IPluginsListener *>::iterator iter;
1508 IPluginsListener *pListener;
1510 if (pPlugin->GetStatus() <= Plugin_Error)
1513 pPlugin->Call_OnPluginEnd();
1515 /* Notify listeners of unloading */
1516 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1518 pListener = (*iter);
1519 pListener->OnPluginUnloaded(pPlugin);
1523 pPlugin->DropEverything();
1525 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1527 /* Notify listeners of destruction */
1528 pListener = (*iter);
1529 pListener->OnPluginDestroyed(pPlugin);
1532 /* Tell the plugin to delete itself */
1538 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1541 IPluginContext *pContext;
1543 pContext = reinterpret_cast<IPluginContext *>(const_cast<sp_context_t *>(ctx));
1545 if (pContext->GetKey(2, (void **)&pPlugin))
1553 CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx)
1555 return (CPlugin *)FindPluginByContext(ctx);
1558 unsigned int CPluginManager::GetPluginCount()
1560 return m_plugins.size();
1563 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1565 m_listeners.push_back(listener);
1568 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1570 m_listeners.remove(listener);
1573 IPluginIterator *CPluginManager::GetPluginIterator()
1575 if (m_iters.empty())
1577 return new CPluginIterator(&m_plugins);
1579 CPluginIterator *iter = m_iters.front();
1586 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1591 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1593 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1594 size_t alias_len = 0;
1595 size_t local_len = 0;
1597 const char *ptr = alias;
1598 unsigned int alias_explicit_paths = 0;
1599 unsigned int alias_path_end = 0;
1600 while (*ptr != '\0')
1602 if (*ptr == '\\' || *ptr == '/')
1604 alias_explicit_paths++;
1605 alias_path_end = alias_len;
1611 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1613 /* Trailing slash is totally invalid here */
1618 unsigned int local_explicit_paths = 0;
1619 unsigned int local_path_end = 0;
1620 while (*ptr != '\0')
1622 if (*ptr == '\\' || *ptr == '/')
1624 local_explicit_paths++;
1625 local_path_end = local_len;
1631 /* If the alias has more explicit paths than the real path,
1632 * no match will be possible.
1634 if (alias_explicit_paths > local_explicit_paths)
1639 if (alias_explicit_paths)
1641 /* We need to find if the paths match now. For example, these should all match:
1643 * csdm optional/csdm
1644 * csdm/ban optional/crab/csdm/ban
1646 const char *aliasptr = alias;
1647 const char *localptr = localpath;
1651 if (*aliasptr != *localptr)
1653 /* We have to knock one path off */
1654 local_explicit_paths--;
1655 if (alias_explicit_paths > local_explicit_paths)
1657 /* Skip out if we're gonna have an impossible match */
1660 /* Eat up localptr tokens until we get a result */
1661 while (((localptr - localpath) < (int)local_path_end)
1663 && *localptr != '\\')
1667 /* Check if we hit the end of our searchable area.
1668 * This probably isn't possible because of the path
1669 * count check, but it's a good idea anyway.
1671 if ((localptr - localpath) >= (int)local_path_end)
1675 /* Consume the slash token */
1678 /* Reset the alias pointer so we can continue consuming */
1684 * This is safe because if localptr terminates early, aliasptr will too
1688 /* We should never reach the end of the string because of this check. */
1689 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1690 bool localend = (localptr - localpath) > (int)local_path_end;
1691 if (aliasend || localend)
1693 if (aliasend && localend)
1695 /* we matched, and we can break out now */
1699 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1704 /* If we got here, it's safe to compare the next two tokens */
1705 if (*localptr != *aliasptr)
1716 /* If we got here, it's time to compare filenames */
1717 const char *aliasptr = alias;
1718 const char *localptr = localpath;
1720 if (alias_explicit_paths)
1722 aliasptr = &alias[alias_path_end + 1];
1725 if (local_explicit_paths)
1727 localptr = &localpath[local_path_end + 1];
1732 if (*aliasptr == '*')
1734 /* First, see if this is the last character */
1735 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1737 /* If so, there's no need to match anything else */
1740 /* Otherwise, we need to search for an appropriate matching sequence in local.
1741 * Note that we only need to search up to the next asterisk.
1745 const char *local_orig = localptr;
1749 while (*aliasptr != '\0' && *aliasptr != '*')
1751 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1752 if (*aliasptr != *localptr)
1762 /* If we didn't get a match, we need to advance the search stream.
1763 * This will let us skip tokens while still searching for another match.
1765 localptr = ++local_orig;
1766 /* Make sure we don't go out of bounds */
1767 if (*localptr == '\0')
1778 /* If we got a match, move on to the next token */
1781 } else if (*aliasptr == '\0') {
1782 if (*localptr == '\0'
1784 strcmp(localptr, ".smx") == 0)
1790 } else if (*aliasptr != *localptr) {
1800 bool CPluginManager::IsLateLoadTime() const
1802 return (m_AllPluginsLoaded || !smcore.IsMapLoading());
1805 void CPluginManager::OnSourceModAllInitialized()
1807 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1810 handlesys->InitAccessDefaults(NULL, &sec);
1812 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1813 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1815 g_PluginType = handlesys->CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1816 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1818 rootmenu->AddRootConsoleCommand("plugins", "Manage Plugins", this);
1820 g_ShareSys.AddInterface(NULL, GetOldAPI());
1822 m_pOnLibraryAdded = forwardsys->CreateForward("OnLibraryAdded", ET_Ignore, 1, NULL, Param_String);
1823 m_pOnLibraryRemoved = forwardsys->CreateForward("OnLibraryRemoved", ET_Ignore, 1, NULL, Param_String);
1826 void CPluginManager::OnSourceModShutdown()
1828 rootmenu->RemoveRootConsoleCommand("plugins", this);
1832 handlesys->RemoveType(g_PluginType, m_MyIdent);
1833 g_ShareSys.DestroyIdentType(g_PluginIdent);
1834 g_ShareSys.DestroyIdentity(m_MyIdent);
1836 forwardsys->ReleaseForward(m_pOnLibraryAdded);
1837 forwardsys->ReleaseForward(m_pOnLibraryRemoved);
1840 ConfigResult CPluginManager::OnSourceModConfigChanged(const char *key,
1842 ConfigSource source,
1846 if (strcmp(key, "BlockBadPlugins") == 0) {
1847 if (strcasecmp(value, "yes") == 0)
1849 m_bBlockBadPlugins = true;
1850 } else if (strcasecmp(value, "no") == 0) {
1851 m_bBlockBadPlugins = false;
1853 smcore.Format(error, maxlength, "Invalid value: must be \"yes\" or \"no\"");
1854 return ConfigResult_Reject;
1856 return ConfigResult_Accept;
1858 return ConfigResult_Ignore;
1861 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1863 /* We don't care about the internal object, actually */
1866 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1868 *pSize = ((CPlugin *)object)->CalcMemUsage();
1872 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1880 sec.pIdentity = m_MyIdent;
1882 if ((_err=handlesys->ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1895 CPlugin *CPluginManager::GetPluginByOrder(int num)
1897 if (num < 1 || num > (int)GetPluginCount())
1905 SourceHook::List<CPlugin *>::iterator iter;
1906 for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
1913 const char *CPluginManager::GetStatusText(PluginStatus st)
1917 case Plugin_Running:
1923 case Plugin_Uncompiled:
1924 return "Uncompiled";
1925 case Plugin_BadLoad:
1935 static inline bool IS_STR_FILLED(const char *text)
1937 return text[0] != '\0';
1940 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
1942 int argcount = smcore.Argc(command);
1945 const char *cmd = smcore.Arg(command, 2);
1946 if (strcmp(cmd, "list") == 0)
1949 unsigned int id = 1;
1950 int plnum = GetPluginCount();
1954 rootmenu->ConsolePrint("[SM] No plugins loaded");
1959 rootmenu->ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1963 SourceHook::List<CPlugin *>::iterator iter;
1964 SourceHook::List<CPlugin *> m_FailList;
1966 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
1969 assert(pl->GetStatus() != Plugin_Created);
1971 const sm_plugininfo_t *info = pl->GetPublicInfo();
1972 if (pl->GetStatus() != Plugin_Running && !pl->IsSilentlyFailed())
1974 len += smcore.Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1976 if (pl->GetStatus() <= Plugin_Error)
1978 /* Plugin has failed to load. */
1979 m_FailList.push_back(pl);
1984 len += smcore.Format(buffer, sizeof(buffer), " %02d", id);
1986 if (pl->GetStatus() < Plugin_Created)
1988 if (pl->IsSilentlyFailed())
1989 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " Disabled:");
1990 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1991 if (IS_STR_FILLED(info->version))
1993 len += smcore.Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1995 if (IS_STR_FILLED(info->author))
1997 smcore.Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
2002 smcore.Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename);
2004 rootmenu->ConsolePrint("%s", buffer);
2007 if (!m_FailList.empty())
2009 rootmenu->ConsolePrint("Load Errors:");
2011 SourceHook::List<CPlugin *>::iterator _iter;
2015 for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
2017 pl = (CPlugin *)*_iter;
2018 rootmenu->ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
2024 else if (strcmp(cmd, "load") == 0)
2028 rootmenu->ConsolePrint("[SM] Usage: sm plugins load <file>");
2034 const char *filename = smcore.Arg(command, 3);
2036 char pluginfile[256];
2037 const char *ext = libsys->GetFileExtension(filename) ? "" : ".smx";
2038 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
2040 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
2044 rootmenu->ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
2050 rootmenu->ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
2054 rootmenu->ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
2059 else if (strcmp(cmd, "unload") == 0)
2063 rootmenu->ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2069 const char *arg = smcore.Arg(command, 3);
2070 int id = strtol(arg, &end, 10);
2074 pl = GetPluginByOrder(id);
2077 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2083 char pluginfile[256];
2084 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2085 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2087 if (!m_LoadLookup.retrieve(pluginfile, &pl))
2089 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2094 char name[PLATFORM_MAX_PATH];
2096 if (pl->GetStatus() < Plugin_Created)
2098 const sm_plugininfo_t *info = pl->GetPublicInfo();
2099 smcore.Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2103 smcore.Format(name, sizeof(name), "%s", pl->GetFilename());
2106 if (UnloadPlugin(pl))
2108 rootmenu->ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2112 rootmenu->ConsolePrint("[SM] Failed to unload plugin %s.", name);
2117 else if (strcmp(cmd, "unload_all") == 0)
2119 g_PluginSys.UnloadAll();
2120 rootmenu->ConsolePrint("[SM] All plugins have been unloaded.");
2123 else if (strcmp(cmd, "load_lock") == 0)
2125 if (m_LoadingLocked)
2127 rootmenu->ConsolePrint("[SM] There is already a loading lock in effect.");
2131 m_LoadingLocked = true;
2132 rootmenu->ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
2136 else if (strcmp(cmd, "load_unlock") == 0)
2138 if (m_LoadingLocked)
2140 m_LoadingLocked = false;
2141 rootmenu->ConsolePrint("[SM] The loading lock is no longer in effect.");
2145 rootmenu->ConsolePrint("[SM] There was no loading lock in effect.");
2149 else if (strcmp(cmd, "info") == 0)
2153 rootmenu->ConsolePrint("[SM] Usage: sm plugins info <#>");
2159 const char *arg = smcore.Arg(command, 3);
2160 int id = strtol(arg, &end, 10);
2164 pl = GetPluginByOrder(id);
2167 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2173 char pluginfile[256];
2174 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2175 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2177 if (!m_LoadLookup.retrieve(pluginfile, &pl))
2179 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2184 const sm_plugininfo_t *info = pl->GetPublicInfo();
2186 rootmenu->ConsolePrint(" Filename: %s", pl->GetFilename());
2187 if (pl->GetStatus() <= Plugin_Error)
2189 if (IS_STR_FILLED(info->name))
2191 if (IS_STR_FILLED(info->description))
2193 rootmenu->ConsolePrint(" Title: %s (%s)", info->name, info->description);
2195 rootmenu->ConsolePrint(" Title: %s", info->name);
2198 if (IS_STR_FILLED(info->author))
2200 rootmenu->ConsolePrint(" Author: %s", info->author);
2202 if (IS_STR_FILLED(info->version))
2204 rootmenu->ConsolePrint(" Version: %s", info->version);
2206 if (IS_STR_FILLED(info->url))
2208 rootmenu->ConsolePrint(" URL: %s", info->url);
2210 if (pl->GetStatus() == Plugin_Error)
2212 rootmenu->ConsolePrint(" Error: %s", pl->m_errormsg);
2216 if (pl->GetStatus() == Plugin_Running)
2218 rootmenu->ConsolePrint(" Status: running");
2222 rootmenu->ConsolePrint(" Status: not running");
2225 const char *typestr = "";
2226 switch (pl->GetType())
2228 case PluginType_MapUpdated:
2229 typestr = "Map Change if Updated";
2231 case PluginType_MapOnly:
2232 typestr = "Map Change";
2234 case PluginType_Private:
2235 case PluginType_Global:
2240 rootmenu->ConsolePrint(" Reloads: %s", typestr);
2242 if (pl->m_FileVersion >= 3)
2244 rootmenu->ConsolePrint(" Timestamp: %s", pl->m_DateTime);
2247 unsigned char *pCodeHash = pl->m_pRuntime->GetCodeHash();
2248 unsigned char *pDataHash = pl->m_pRuntime->GetDataHash();
2250 char combinedHash[33];
2251 for (int i = 0; i < 16; i++)
2252 smcore.Format(combinedHash + (i * 2), 3, "%02x", pCodeHash[i] ^ pDataHash[i]);
2254 rootmenu->ConsolePrint(" Hash: %s", combinedHash);
2258 rootmenu->ConsolePrint(" Load error: %s", pl->m_errormsg);
2259 if (pl->GetStatus() < Plugin_Created)
2261 rootmenu->ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2262 info->name ? info->name : "<none>",
2263 info->version ? info->version : "<none>");
2264 if (IS_STR_FILLED(info->url))
2266 rootmenu->ConsolePrint(" File URL: %s", info->url);
2273 else if (strcmp(cmd, "refresh") == 0)
2275 smcore.DoGlobalPluginLoads();
2276 rootmenu->ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
2279 else if (strcmp(cmd, "reload") == 0)
2283 rootmenu->ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
2289 const char *arg = smcore.Arg(command, 3);
2290 int id = strtol(arg, &end, 10);
2294 pl = GetPluginByOrder(id);
2297 rootmenu->ConsolePrint("[SM] Plugin index %d not found.", id);
2303 char pluginfile[256];
2304 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2305 g_pSM->BuildPath(Path_None, pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2307 if (!m_LoadLookup.retrieve(pluginfile, &pl))
2309 rootmenu->ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2314 char name[PLATFORM_MAX_PATH];
2315 const sm_plugininfo_t *info = pl->GetPublicInfo();
2316 if (pl->GetStatus() <= Plugin_Paused)
2317 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2319 strcpy(name, pl->GetFilename());
2321 if (ReloadPlugin(pl))
2323 rootmenu->ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
2327 rootmenu->ConsolePrint("[SM] Failed to reload plugin %s.", name);
2334 /* Draw the main menu */
2335 rootmenu->ConsolePrint("SourceMod Plugins Menu:");
2336 rootmenu->DrawGenericOption("info", "Information about a plugin");
2337 rootmenu->DrawGenericOption("list", "Show loaded plugins");
2338 rootmenu->DrawGenericOption("load", "Load a plugin");
2339 rootmenu->DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
2340 rootmenu->DrawGenericOption("load_unlock", "Re-enables plugin loading");
2341 rootmenu->DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
2342 rootmenu->DrawGenericOption("reload", "Reloads a plugin");
2343 rootmenu->DrawGenericOption("unload", "Unload a plugin");
2344 rootmenu->DrawGenericOption("unload_all", "Unloads all plugins");
2347 bool CPluginManager::ReloadPlugin(CPlugin *pl)
2349 List<CPlugin *>::iterator iter;
2350 char filename[PLATFORM_MAX_PATH];
2356 strcpy(filename, pl->m_filename);
2357 ptype = pl->GetType();
2359 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
2367 if (!UnloadPlugin(pl))
2371 if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded)))
2376 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2378 if ((*iter) == (CPlugin *)newpl)
2380 m_plugins.erase(iter);
2386 for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
2389 m_plugins.insert(iter, (CPlugin *)newpl);
2394 void CPluginManager::RefreshAll()
2396 /* If we're in a load lock, just skip this whole bit. */
2397 if (m_LoadingLocked)
2402 List<CPlugin *>::iterator iter;
2403 List<CPlugin *> tmp_list = m_plugins;
2407 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2410 if (pl->GetType() == PluginType_MapOnly)
2412 UnloadPlugin((IPlugin *)pl);
2414 else if (pl->GetType() == PluginType_MapUpdated)
2416 t = pl->GetFileTimeStamp();
2417 if (!t || t > pl->GetTimeStamp())
2419 pl->SetTimeStamp(t);
2420 UnloadPlugin((IPlugin *)pl);
2426 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2428 List<IPluginsListener *>::iterator iter;
2429 IPluginsListener *pListener;
2430 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2432 pListener = (*iter);
2433 pListener->OnPluginPauseChange(pl, paused);
2437 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2439 List<CPlugin *>::iterator iter;
2441 IPluginFunction *pFunction;
2443 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2447 if (pPlugin->GetStatus() <= Plugin_Paused)
2449 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2453 pForward->AddFunction(pFunction);
2459 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2461 if (pToken->type != g_PluginIdent)
2466 return (CPlugin *)(pToken->ptr);
2469 void CPluginManager::OnLibraryAction(const char *lib, LibraryAction action)
2473 case LibraryAction_Removed:
2474 m_pOnLibraryRemoved->PushString(lib);
2475 m_pOnLibraryRemoved->Execute(NULL);
2477 case LibraryAction_Added:
2478 m_pOnLibraryAdded->PushString(lib);
2479 m_pOnLibraryAdded->Execute(NULL);
2484 bool CPluginManager::LibraryExists(const char *lib)
2486 List<CPlugin *>::iterator iter;
2488 for (iter=m_plugins.begin();
2489 iter!=m_plugins.end();
2492 CPlugin *pl = (*iter);
2493 if (pl->GetStatus() != Plugin_Running)
2497 List<String>::iterator s_iter;
2498 for (s_iter = pl->m_Libraries.begin();
2499 s_iter != pl->m_Libraries.end();
2502 if ((*s_iter).compare(lib) == 0)
2512 void CPluginManager::AllPluginsLoaded()
2514 List<CPlugin *>::iterator iter;
2517 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2520 pl->Call_OnAllPluginsLoaded();
2524 void CPluginManager::UnloadAll()
2526 List<CPlugin *>::iterator iter;
2527 while ( (iter = m_plugins.begin()) != m_plugins.end() )
2529 UnloadPlugin((*iter));
2533 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
2536 List<CPlugin *>::iterator iter;
2538 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2549 SMPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
2555 id = strtol(arg, &end, 10);
2559 pl = GetPluginByOrder(id);
2567 char pluginfile[256];
2568 const char *ext = libsys->GetFileExtension(arg) ? "" : ".smx";
2569 smcore.Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2571 if (!m_LoadLookup.retrieve(pluginfile, &pl))
2578 void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue)
2580 SyncMaxClients(newvalue);
2583 void CPluginManager::SyncMaxClients(int max_clients)
2585 List<CPlugin *>::iterator iter;
2587 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2589 (*iter)->SyncMaxClients(max_clients);
2593 const CVector<SMPlugin *> *CPluginManager::ListPlugins()
2595 CVector<SMPlugin *> *list = new CVector<SMPlugin *>();
2597 for (List<CPlugin *>::iterator iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2598 list->push_back((*iter));
2603 void CPluginManager::FreePluginList(const CVector<SMPlugin *> *list)
2605 delete const_cast<CVector<SMPlugin *> *>(list);
2608 class OldPluginAPI : public IPluginManager
2611 IPlugin *LoadPlugin(const char *path,
2618 return g_PluginSys.LoadPlugin(path, debug, type, error, maxlength, wasloaded);
2621 bool UnloadPlugin(IPlugin *plugin)
2623 return g_PluginSys.UnloadPlugin(plugin);
2626 IPlugin *FindPluginByContext(const sp_context_t *ctx)
2628 return g_PluginSys.FindPluginByContext(ctx);
2631 unsigned int GetPluginCount()
2633 return g_PluginSys.GetPluginCount();
2636 IPluginIterator *GetPluginIterator()
2638 return g_PluginSys.GetPluginIterator();
2641 void AddPluginsListener(IPluginsListener *listener)
2643 g_PluginSys.AddPluginsListener(listener);
2646 void RemovePluginsListener(IPluginsListener *listener)
2648 g_PluginSys.RemovePluginsListener(listener);
2651 IPlugin *PluginFromHandle(Handle_t handle, HandleError *err)
2653 return g_PluginSys.PluginFromHandle(handle, err);
2657 static OldPluginAPI sOldPluginAPI;
2659 IPluginManager *CPluginManager::GetOldAPI()
2661 return &sOldPluginAPI;