Fixed regression (introduced by revision 2242 aka Phase 0) where 'sm plugins list' displayed all plugins as having an id of 1
3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
33 #include "PluginSys.h"
35 #include "LibrarySys.h"
36 #include "HandleSys.h"
37 #include "ForwardSys.h"
38 #include "sourcemm_api.h"
39 #include "sourcemod.h"
40 #include "TextParsers.h"
42 #include "ExtensionSys.h"
43 #include "sm_srvcmds.h"
44 #include "sm_stringutil.h"
45 #include "ConCmdManager.h"
46 #include "PlayerManager.h"
47 #include "CoreConfig.h"
48 #include "Translator.h"
50 CPluginManager g_PluginSys;
51 HandleType_t g_PluginType = 0;
52 IdentityType_t g_PluginIdent = 0;
54 CPlugin::CPlugin(const char *file)
56 static int MySerial = 0;
58 m_type = PluginType_Private;
59 m_status = Plugin_Uncompiled;
60 m_serial = ++MySerial;
62 m_errormsg[256] = '\0';
63 snprintf(m_filename, sizeof(m_filename), "%s", file);
66 m_pProps = sm_trie_create();
67 m_FakeNativesMissing = false;
68 m_LibraryMissing = false;
69 m_bGotAllLoaded = false;
70 m_pPhrases = g_Translator.CreatePhraseCollection();
78 sec.pOwner = g_PluginSys.GetIdentity();
79 sec.pIdentity = sec.pOwner;
81 g_HandleSys.FreeHandle(m_handle, &sec);
82 g_ShareSys.DestroyIdentity(m_ident);
92 m_ctx.vm->FreeContext(m_ctx.ctx);
97 m_ctx.vm->AbortCompilation(m_ctx.co);
103 g_pSourcePawn->FreeFromMemory(m_plugin);
108 sm_trie_destroy(m_pProps);
110 for (size_t i=0; i<m_configs.size(); i++)
115 if (m_pPhrases != NULL)
117 m_pPhrases->Destroy();
122 void CPlugin::InitIdentity()
126 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this);
127 m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
128 m_ctx.base->SetIdentity(m_ident);
132 unsigned int CPlugin::CalcMemUsage()
134 unsigned int base_size =
136 + sizeof(IdentityToken_t)
137 + (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig)))
138 + sm_trie_mem_usage(m_pProps);
140 for (unsigned int i = 0; i < m_configs.size(); i++)
142 base_size += m_configs[i]->autocfg.size();
143 base_size += m_configs[i]->folder.size();
146 for (List<String>::iterator i = m_Libraries.begin();
147 i != m_Libraries.end();
150 base_size += (*i).size();
153 for (List<String>::iterator i = m_RequiredLibs.begin();
154 i != m_RequiredLibs.end();
157 base_size += (*i).size();
160 if (m_plugin != NULL)
162 base_size += sizeof(sp_plugin_t);
163 base_size += m_plugin->data_size;
164 base_size += m_plugin->pcode_size;
165 base_size += (m_plugin->info.natives_num * sizeof(sp_file_natives_t));
166 base_size += (m_plugin->info.publics_num * sizeof(sp_file_publics_t));
167 base_size += (m_plugin->info.pubvars_num * sizeof(sp_file_pubvars_t));
168 base_size += (m_plugin->debug.files_num * sizeof(sp_fdbg_file_t));
169 base_size += (m_plugin->debug.lines_num * sizeof(sp_fdbg_line_t));
170 base_size += (m_plugin->debug.syms_num * sizeof(sp_fdbg_symbol_t));
171 /* We can't get strtab size, oh well. */
174 if (m_ctx.base != NULL)
176 base_size += sizeof(BaseContext);
177 base_size += m_ctx.base->GetPublicsNum() * sizeof(CFunction);
179 if (m_ctx.ctx != NULL)
181 base_size += m_ctx.ctx->mem_size;
182 base_size += (m_plugin->debug.files_num * sizeof(sp_debug_file_t));
183 base_size += (m_plugin->debug.lines_num * sizeof(sp_debug_line_t));
184 base_size += (m_plugin->debug.syms_num * sizeof(sp_debug_symbol_t));
185 base_size += (m_plugin->info.pubvars_num * sizeof(sp_pubvar_t));
186 base_size += (m_plugin->info.publics_num * sizeof(sp_public_t));
187 base_size += (m_plugin->info.natives_num * sizeof(sp_native_t));
188 /* We also don't know the JIT code size, oh well. */
194 Handle_t CPlugin::GetMyHandle()
199 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
201 char fullpath[PLATFORM_MAX_PATH];
202 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
203 FILE *fp = fopen(fullpath, "rb");
205 CPlugin *pPlugin = new CPlugin(file);
211 snprintf(error, maxlength, "Unable to open file");
213 pPlugin->m_status = Plugin_BadLoad;
218 sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
224 snprintf(error, maxlength, "Error %d while parsing plugin", err);
226 pPlugin->m_status = Plugin_BadLoad;
232 pPlugin->m_plugin = pl;
237 bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */)
239 bool exists = sm_trie_retrieve(m_pProps, prop, ptr);
241 if (exists && remove)
243 sm_trie_delete(m_pProps, prop);
249 bool CPlugin::SetProperty(const char *prop, void *ptr)
251 return sm_trie_insert(m_pProps, prop, ptr);
254 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
261 /* :NOTICE: We will eventually need to change these natives
262 * for swapping in new contexts
264 if (m_ctx.co || m_ctx.ctx)
269 m_status = Plugin_Uncompiled;
272 m_ctx.co = vm->StartCompilation(m_plugin);
277 void CPlugin::CancelMyCompile()
284 m_ctx.vm->AbortCompilation(m_ctx.co);
289 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
297 m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
300 memset(&m_ctx, 0, sizeof(m_ctx));
303 snprintf(error, maxlength, "JIT failed to compile (error %d)", err);
308 m_ctx.base = new BaseContext(m_ctx.ctx);
309 m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
311 m_status = Plugin_Created;
319 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
321 PluginStatus old_status = m_status;
324 if (old_status == Plugin_Running)
326 /* Tell everyone we're now paused */
327 g_PluginSys._SetPauseState(this, true);
331 va_start(ap, error_fmt);
332 vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
337 m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
341 void CPlugin::UpdateInfo()
343 /* Now grab the info */
345 IPluginContext *base = GetBaseContext();
346 int err = base->FindPubvarByName("myinfo", &idx);
348 memset(&m_info, 0, sizeof(m_info));
350 if (err == SP_ERROR_NONE)
352 struct sm_plugininfo_c_t
360 sm_plugininfo_c_t *cinfo;
363 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
364 base->LocalToString(cinfo->name, (char **)&m_info.name);
365 base->LocalToString(cinfo->description, (char **)&m_info.description);
366 base->LocalToString(cinfo->author, (char **)&m_info.author);
367 base->LocalToString(cinfo->url, (char **)&m_info.url);
368 base->LocalToString(cinfo->version, (char **)&m_info.version);
371 m_info.author = m_info.author ? m_info.author : "";
372 m_info.description = m_info.description ? m_info.description : "";
373 m_info.name = m_info.name ? m_info.name : "";
374 m_info.url = m_info.url ? m_info.url : "";
375 m_info.version = m_info.version ? m_info.version : "";
377 if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE)
379 struct __version_info
386 __version_info *info;
388 const char *pDate, *pTime;
393 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info);
394 m_FileVersion = info->version;
395 if (m_FileVersion >= 3)
397 base->LocalToString(info->date, (char **)&pDate);
398 base->LocalToString(info->time, (char **)&pTime);
399 UTIL_Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime);
408 void CPlugin::Call_OnPluginStart()
410 if (m_status != Plugin_Loaded)
415 m_status = Plugin_Running;
418 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart");
425 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
427 SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)");
431 void CPlugin::Call_OnPluginEnd()
433 if (m_status > Plugin_Paused)
439 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd");
445 pFunction->Execute(&result);
448 void CPlugin::Call_OnAllPluginsLoaded()
450 if (m_status > Plugin_Paused)
460 m_bGotAllLoaded = true;
463 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnAllPluginsLoaded");
464 if (pFunction != NULL)
466 pFunction->Execute(&result);
471 if ((pFunction = m_ctx.base->GetFunctionByName("OnMapStart")) != NULL)
473 pFunction->Execute(NULL);
477 if (SM_AreConfigsExecuted())
479 SM_ExecuteForPlugin(GetBaseContext());
483 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
485 if (m_status != Plugin_Created)
490 m_status = Plugin_Loaded;
494 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
501 pFunction->PushCell(m_handle);
502 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
503 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
504 pFunction->PushCell(maxlength);
505 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
510 if (!result || m_status != Plugin_Loaded)
518 const sp_plugin_t *CPlugin::GetPluginStructure()
523 IPluginContext *CPlugin::GetBaseContext()
528 sp_context_t *CPlugin::GetContext()
533 const char *CPlugin::GetFilename()
538 PluginType CPlugin::GetType()
543 const sm_plugininfo_t *CPlugin::GetPublicInfo()
548 unsigned int CPlugin::GetSerial()
553 PluginStatus CPlugin::GetStatus()
558 bool CPlugin::IsDebugging()
565 return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
568 void CPlugin::LibraryActions(bool dropping)
570 List<String>::iterator iter;
571 for (iter = m_Libraries.begin();
572 iter != m_Libraries.end();
575 g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping);
579 bool CPlugin::SetPauseState(bool paused)
581 if (paused && GetStatus() != Plugin_Running)
584 } else if (!paused && GetStatus() != Plugin_Paused) {
590 LibraryActions(true);
593 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
597 pFunction->PushCell(paused ? 1 : 0);
598 pFunction->Execute(&result);
603 m_status = Plugin_Paused;
604 m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED;
606 m_status = Plugin_Running;
607 m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
610 g_PluginSys._SetPauseState(this, paused);
614 LibraryActions(false);
620 IdentityToken_t *CPlugin::GetIdentity()
625 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
633 snprintf(error, maxlength, "Plugin is not runnable.");
638 if (debug && IsDebugging())
642 snprintf(error, maxlength, "Plugin is already in debug mode.");
646 else if (!debug && !IsDebugging())
650 snprintf(error, maxlength, "Plugins is already in production mode.");
655 ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
656 if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
660 snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
665 sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
669 memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
670 new_ctx->hp = m_ctx.ctx->hp;
671 new_ctx->sp = m_ctx.ctx->sp;
672 new_ctx->frm = m_ctx.ctx->frm;
673 new_ctx->dbreak = m_ctx.ctx->dbreak;
674 new_ctx->context = m_ctx.ctx->context;
675 memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
677 uint32_t nativeCount = m_plugin->info.natives_num;
678 for (uint32_t i=0; i<nativeCount; i++)
680 new_ctx->natives[i].pfn = m_ctx.ctx->natives[i].pfn;
681 new_ctx->natives[i].status = m_ctx.ctx->natives[i].status;
684 g_pVM->FreeContext(m_ctx.ctx);
686 m_ctx.base->SetContext(new_ctx);
694 snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
702 bool CPlugin::IsRunnable()
704 return (m_status <= Plugin_Paused) ? true : false;
707 time_t CPlugin::GetFileTimeStamp()
709 char path[PLATFORM_MAX_PATH];
710 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
711 #ifdef PLATFORM_WINDOWS
713 if (_stat(path, &s) != 0)
714 #elif defined PLATFORM_POSIX
716 if (stat(path, &s) != 0)
727 time_t CPlugin::GetTimeStamp()
732 void CPlugin::SetTimeStamp(time_t t)
737 IPhraseCollection *CPlugin::GetPhrases()
742 void CPlugin::DependencyDropped(CPlugin *pOwner)
749 List<String>::iterator reqlib_iter;
750 List<String>::iterator lib_iter;
751 for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++)
753 for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++)
755 if ((*reqlib_iter) == (*lib_iter))
757 m_LibraryMissing = true;
762 List<NativeEntry *>::iterator iter;
763 NativeEntry *pNative;
766 unsigned int unbound = 0;
768 for (iter = pOwner->m_Natives.begin();
769 iter != pOwner->m_Natives.end();
773 /* Find this native! */
774 if (m_ctx.base->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE)
779 m_ctx.base->GetNativeByIndex(idx, &native);
781 native->status = SP_NATIVE_UNBOUND;
787 m_FakeNativesMissing = true;
790 /* :IDEA: in the future, add native trapping? */
791 if (m_FakeNativesMissing || m_LibraryMissing)
793 SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename());
797 unsigned int CPlugin::GetConfigCount()
799 return (unsigned int)m_configs.size();
802 AutoConfig *CPlugin::GetConfig(unsigned int i)
804 if (i >= GetConfigCount())
812 void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder)
814 /* Do a check for duplicates to prevent double-execution */
815 for (size_t i = 0; i < m_configs.size(); i++)
817 if (m_configs[i]->autocfg.compare(cfg) == 0
818 && m_configs[i]->folder.compare(folder) == 0
819 && m_configs[i]->create == autoCreate)
825 AutoConfig *c = new AutoConfig;
829 c->create = autoCreate;
831 m_configs.push_back(c);
834 void CPlugin::DropEverything()
837 List<CPlugin *>::iterator iter;
838 List<WeakNative>::iterator wk_iter;
840 /* Tell everyone that depends on us that we're about to drop */
841 for (iter = m_Dependents.begin();
842 iter != m_Dependents.end();
846 pOther->DependencyDropped(this);
849 /* Note: we don't care about things we depend on.
850 * The reason is that extensions have their own cleanup
851 * code for plugins. Although the "right" design would be
852 * to centralize that here, i'm omitting it for now. Thus,
853 * the code below to walk the plugins list will suffice.
856 /* Other plugins could be holding weak references that were
857 * added by us. We need to clean all of those up now.
859 for (iter = g_PluginSys.m_plugins.begin();
860 iter != g_PluginSys.m_plugins.end();
863 (*iter)->DropRefsTo(this);
866 /* Proceed with the rest of the necessities. */
867 CNativeOwner::DropEverything();
870 bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func)
874 if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL)
879 m_Natives.push_back(pEntry);
888 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
894 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
899 bool CPluginManager::CPluginIterator::MorePlugins()
901 return (current != mylist->end());
904 void CPluginManager::CPluginIterator::NextPlugin()
909 void CPluginManager::CPluginIterator::Release()
911 g_PluginSys.ReleaseIterator(this);
914 CPluginManager::CPluginIterator::~CPluginIterator()
918 void CPluginManager::CPluginIterator::Reset()
920 current = mylist->begin();
927 CPluginManager::CPluginManager()
929 m_LoadLookup = sm_trie_create();
930 m_AllPluginsLoaded = false;
932 m_LoadingLocked = false;
935 CPluginManager::~CPluginManager()
938 * Ignore the fact that there might be plugins in the cache.
939 * This usually means that Core is not being unloaded properly, and everything
940 * will crash anyway. YAY
942 sm_trie_destroy(m_LoadLookup);
944 CStack<CPluginManager::CPluginIterator *>::iterator iter;
945 for (iter=m_iters.begin(); iter!=m_iters.end(); iter++)
952 void CPluginManager::Shutdown()
954 List<CPlugin *>::iterator iter;
956 while ((iter = m_plugins.begin()) != m_plugins.end())
962 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
964 /* First read in the database of plugin settings */
967 m_AllPluginsLoaded = false;
968 if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay)
970 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
971 const char *err_msg = textparsers->GetSMCErrorString(err);
974 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
978 LoadPluginsFromDir(basedir, NULL);
981 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
983 char base_path[PLATFORM_MAX_PATH];
985 /* Form the current path to start reading from */
986 if (localpath == NULL)
988 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
990 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
993 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
998 g_LibSys.GetPlatformError(error, sizeof(error));
999 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
1000 g_Logger.LogError("[SM] Platform returned error: %s", error);
1004 while (dir->MoreFiles())
1006 if (dir->IsEntryDirectory()
1007 && (strcmp(dir->GetEntryName(), ".") != 0)
1008 && (strcmp(dir->GetEntryName(), "..") != 0)
1009 && (strcmp(dir->GetEntryName(), "disabled") != 0)
1010 && (strcmp(dir->GetEntryName(), "optional") != 0))
1012 char new_local[PLATFORM_MAX_PATH];
1013 if (localpath == NULL)
1015 /* If no path yet, don't add a former slash */
1016 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
1018 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
1020 LoadPluginsFromDir(basedir, new_local);
1021 } else if (dir->IsEntryFile()) {
1022 const char *name = dir->GetEntryName();
1023 size_t len = strlen(name);
1025 && strcmp(&name[len-4], ".smx") == 0)
1027 /* If the filename matches, load the plugin */
1028 char plugin[PLATFORM_MAX_PATH];
1029 if (localpath == NULL)
1031 snprintf(plugin, sizeof(plugin), "%s", name);
1033 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
1035 LoadAutoPlugin(plugin);
1040 g_LibSys.CloseDirectory(dir);
1043 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength)
1045 if (m_LoadingLocked)
1047 return LoadRes_NeverLoad;
1050 bool no_load = false;
1051 PluginSettings *pset;
1052 unsigned int setcount = m_PluginInfo.GetSettingsNum();
1053 for (unsigned int i=0; i<setcount; i++)
1055 if ((pset = m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
1059 if (pset->blockload_val)
1067 * Does this plugin already exist?
1070 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
1072 /* Check to see if we should try reloading it */
1073 if (pPlugin->GetStatus() == Plugin_BadLoad
1074 || pPlugin->GetStatus() == Plugin_Error
1075 || pPlugin->GetStatus() == Plugin_Failed
1078 UnloadPlugin(pPlugin);
1086 return LoadRes_AlreadyLoaded;
1092 return LoadRes_NeverLoad;
1095 pPlugin = CPlugin::CreatePlugin(path, error, maxlength);
1097 assert(pPlugin != NULL);
1099 pPlugin->m_type = PluginType_MapUpdated;
1101 ICompilation *co = NULL;
1103 if (pPlugin->m_status == Plugin_Uncompiled)
1105 co = pPlugin->StartMyCompile(g_pVM);
1108 for (unsigned int i=0; i<setcount; i++)
1110 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
1114 pPlugin->m_type = pset->type_val;
1117 for (unsigned int j=0; j<pset->opts_num; j++)
1119 const char *key, *val;
1120 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
1125 if (!g_pVM->SetCompilationOption(co, key, val))
1129 snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
1131 pPlugin->CancelMyCompile();
1139 /* Do the actual compiling */
1142 pPlugin->FinishMyCompile(error, maxlength);
1146 /* Get the status */
1147 if (pPlugin->GetStatus() == Plugin_Created)
1149 /* First native pass - add anything from Core */
1150 g_ShareSys.BindNativesToPlugin(pPlugin, true);
1151 pPlugin->InitIdentity();
1152 if (pPlugin->Call_AskPluginLoad(error, maxlength))
1154 /* Autoload any modules */
1155 LoadOrRequireExtensions(pPlugin, 1, error, maxlength);
1159 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1163 /* Save the time stamp */
1164 time_t t = pPlugin->GetFileTimeStamp();
1165 pPlugin->SetTimeStamp(t);
1172 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
1175 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded)
1181 if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure)
1187 if (res == LoadRes_AlreadyLoaded)
1193 if (res == LoadRes_NeverLoad)
1195 if (m_LoadingLocked)
1197 UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect");
1201 UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)");
1208 /* Run second pass if we need to */
1209 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
1211 if (!RunSecondPass(pl, error, maxlength))
1216 pl->Call_OnAllPluginsLoaded();
1222 void CPluginManager::LoadAutoPlugin(const char *plugin)
1226 char error[255] = "Unknown error";
1228 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
1230 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
1231 pl->SetErrorState(Plugin_Failed, "%s", error);
1234 if (res == LoadRes_Successful)
1240 void CPluginManager::AddPlugin(CPlugin *pPlugin)
1242 List<IPluginsListener *>::iterator iter;
1243 IPluginsListener *pListener;
1245 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1247 pListener = (*iter);
1248 pListener->OnPluginCreated(pPlugin);
1251 m_plugins.push_back(pPlugin);
1252 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
1255 void CPluginManager::LoadAll_SecondPass()
1257 List<CPlugin *>::iterator iter;
1261 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1264 if (pPlugin->GetStatus() == Plugin_Loaded)
1267 if (!RunSecondPass(pPlugin, error, sizeof(error)))
1269 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
1270 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
1275 m_AllPluginsLoaded = true;
1278 bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength)
1287 IPluginContext *pBase = pPlugin->GetBaseContext();
1288 uint32_t num = pBase->GetPubVarsNum();
1289 sp_pubvar_t *pubvar;
1291 char pathfile[PLATFORM_MAX_PATH];
1293 for (uint32_t i=0; i<num; i++)
1295 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1299 if (strncmp(pubvar->name, "__pl_", 5) == 0)
1301 pl = (_pl *)pubvar->offs;
1302 if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE)
1306 if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE)
1310 g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename());
1311 if (strcmp(pathfile, file) == 0)
1315 if (pl->required == false)
1317 IPluginFunction *pFunc;
1319 UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]);
1320 if ((pFunc=pBase->GetFunctionByName(buffer)))
1323 pFunc->Execute(&res);
1324 if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
1328 snprintf(error, maxlength, "Fatal error during initializing plugin load");
1336 /* Check that we aren't registering the same library twice */
1337 if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end())
1339 pPlugin->m_RequiredLibs.push_back(name);
1345 List<CPlugin *>::iterator iter;
1348 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
1351 if (pl->m_Libraries.find(name) != pl->m_Libraries.end())
1361 snprintf(error, maxlength, "Could not find required plugin \"%s\"", name);
1372 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
1374 /* Find any extensions this plugin needs */
1383 IPluginContext *pBase = pPlugin->GetBaseContext();
1384 uint32_t num = pBase->GetPubVarsNum();
1385 sp_pubvar_t *pubvar;
1387 char path[PLATFORM_MAX_PATH];
1389 for (uint32_t i=0; i<num; i++)
1391 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
1395 if (strncmp(pubvar->name, "__ext_", 6) == 0)
1397 ext = (_ext *)pubvar->offs;
1398 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
1402 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
1408 /* Attempt to auto-load if necessary */
1411 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1412 g_Extensions.LoadAutoExtension(path);
1417 /* Is this required? */
1420 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
1421 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
1423 pExt = g_Extensions.FindExtensionByName(name);
1425 /* :TODO: should we bind to unloaded extensions?
1426 * Currently the extension manager will ignore this.
1428 if (!pExt || !pExt->IsRunning(NULL, 0))
1432 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
1438 g_Extensions.BindChildPlugin(pExt, pPlugin);
1443 IPluginFunction *pFunc;
1445 UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]);
1447 if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL)
1450 pFunc->Execute(&res);
1451 if (pPlugin->GetContext()->n_err != SP_ERROR_NONE)
1455 UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)");
1468 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
1470 /* Second pass for extension requirements */
1471 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
1476 if (!FindOrRequirePluginDeps(pPlugin, error, maxlength))
1481 /* Run another binding pass */
1482 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1484 /* Find any unbound natives. Right now, these are not allowed. */
1485 IPluginContext *pContext = pPlugin->GetBaseContext();
1486 uint32_t num = pContext->GetNativesNum();
1487 sp_native_t *native;
1488 for (unsigned int i=0; i<num; i++)
1490 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1494 if (native->status == SP_NATIVE_UNBOUND
1495 && native->name[0] != '@'
1496 && !(native->flags & SP_NTVFLAG_OPTIONAL))
1500 snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
1506 /* Finish by telling all listeners */
1507 List<IPluginsListener *>::iterator iter;
1508 IPluginsListener *pListener;
1509 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1511 pListener = (*iter);
1512 pListener->OnPluginLoaded(pPlugin);
1515 /* Tell this plugin to finish initializing itself */
1516 pPlugin->Call_OnPluginStart();
1518 /* Now, if we have fake natives, go through all plugins that might need rebinding */
1519 if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size())
1521 List<CPlugin *>::iterator pl_iter;
1523 for (pl_iter = m_plugins.begin();
1524 pl_iter != m_plugins.end();
1527 pOther = (*pl_iter);
1528 if ((pOther->GetStatus() == Plugin_Error
1529 && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing))
1530 || pOther->m_FakeNativesMissing)
1532 TryRefreshDependencies(pOther);
1534 else if ((pOther->GetStatus() == Plugin_Running
1535 || pOther->GetStatus() == Plugin_Paused)
1536 && pOther != pPlugin)
1538 List<NativeEntry *>::iterator nv_iter;
1539 for (nv_iter = pPlugin->m_Natives.begin();
1540 nv_iter != pPlugin->m_Natives.end();
1543 g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter));
1549 /* Go through our libraries and tell other plugins they're added */
1550 List<String>::iterator s_iter;
1551 for (s_iter = pPlugin->m_Libraries.begin();
1552 s_iter != pPlugin->m_Libraries.end();
1555 OnLibraryAction((*s_iter).c_str(), true, false);
1558 /* :TODO: optimize? does this even matter? */
1559 pPlugin->GetPhrases()->AddPhraseFile("core.phrases");
1564 void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin)
1566 assert(pPlugin->GetBaseContext() != NULL);
1568 g_ShareSys.BindNativesToPlugin(pPlugin, false);
1570 List<String>::iterator lib_iter;
1571 List<String>::iterator req_iter;
1572 List<CPlugin *>::iterator pl_iter;
1574 for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++)
1577 for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++)
1580 for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++)
1582 if ((*req_iter) == (*lib_iter))
1590 pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str());
1595 /* Find any unbound natives
1596 * Right now, these are not allowed
1598 IPluginContext *pContext = pPlugin->GetBaseContext();
1599 uint32_t num = pContext->GetNativesNum();
1600 sp_native_t *native;
1601 for (unsigned int i=0; i<num; i++)
1603 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
1607 if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL))
1609 pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name);
1614 if (pPlugin->GetStatus() == Plugin_Error)
1616 /* If we got here, all natives are okay again! */
1617 pPlugin->m_status = Plugin_Running;
1618 if ((pPlugin->m_ctx.ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
1620 pPlugin->m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED;
1621 _SetPauseState(pPlugin, false);
1626 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1628 CPlugin *pPlugin = (CPlugin *)plugin;
1630 /* This prevents removal during insertion or anything else weird */
1631 if (m_plugins.find(pPlugin) == m_plugins.end())
1636 IPluginContext *pContext = plugin->GetBaseContext();
1637 if (pContext->IsInExec())
1640 UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename());
1641 engine->ServerCommand(buffer);
1645 /* Remove us from the lookup table and linked list */
1646 m_plugins.remove(pPlugin);
1647 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
1649 /* Go through our libraries and tell other plugins they're gone */
1650 List<String>::iterator s_iter;
1651 for (s_iter = pPlugin->m_Libraries.begin();
1652 s_iter != pPlugin->m_Libraries.end();
1655 OnLibraryAction((*s_iter).c_str(), true, true);
1658 List<IPluginsListener *>::iterator iter;
1659 IPluginsListener *pListener;
1661 if (pPlugin->GetStatus() <= Plugin_Error)
1663 /* Notify listeners of unloading */
1664 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1666 pListener = (*iter);
1667 pListener->OnPluginUnloaded(pPlugin);
1670 pPlugin->Call_OnPluginEnd();
1673 pPlugin->DropEverything();
1675 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1677 /* Notify listeners of destruction */
1678 pListener = (*iter);
1679 pListener->OnPluginDestroyed(pPlugin);
1682 /* Tell the plugin to delete itself */
1688 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1690 IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
1694 unsigned int CPluginManager::GetPluginCount()
1696 return m_plugins.size();
1699 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1701 m_listeners.push_back(listener);
1704 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1706 m_listeners.remove(listener);
1709 IPluginIterator *CPluginManager::GetPluginIterator()
1711 if (m_iters.empty())
1713 return new CPluginIterator(&m_plugins);
1715 CPluginIterator *iter = m_iters.front();
1722 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1727 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1729 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1730 size_t alias_len = 0;
1731 size_t local_len = 0;
1733 const char *ptr = alias;
1734 unsigned int alias_explicit_paths = 0;
1735 unsigned int alias_path_end = 0;
1736 while (*ptr != '\0')
1738 if (*ptr == '\\' || *ptr == '/')
1740 alias_explicit_paths++;
1741 alias_path_end = alias_len;
1747 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1749 /* Trailing slash is totally invalid here */
1754 unsigned int local_explicit_paths = 0;
1755 unsigned int local_path_end = 0;
1756 while (*ptr != '\0')
1758 if (*ptr == '\\' || *ptr == '/')
1760 local_explicit_paths++;
1761 local_path_end = local_len;
1767 /* If the alias has more explicit paths than the real path,
1768 * no match will be possible.
1770 if (alias_explicit_paths > local_explicit_paths)
1775 if (alias_explicit_paths)
1777 /* We need to find if the paths match now. For example, these should all match:
1779 * csdm optional/csdm
1780 * csdm/ban optional/crab/csdm/ban
1782 const char *aliasptr = alias;
1783 const char *localptr = localpath;
1787 if (*aliasptr != *localptr)
1789 /* We have to knock one path off */
1790 local_explicit_paths--;
1791 if (alias_explicit_paths > local_explicit_paths)
1793 /* Skip out if we're gonna have an impossible match */
1796 /* Eat up localptr tokens until we get a result */
1797 while (((localptr - localpath) < (int)local_path_end)
1799 && *localptr != '\\')
1803 /* Check if we hit the end of our searchable area.
1804 * This probably isn't possible because of the path
1805 * count check, but it's a good idea anyway.
1807 if ((localptr - localpath) >= (int)local_path_end)
1811 /* Consume the slash token */
1814 /* Reset the alias pointer so we can continue consuming */
1820 * This is safe because if localptr terminates early, aliasptr will too
1824 /* We should never reach the end of the string because of this check. */
1825 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1826 bool localend = (localptr - localpath) > (int)local_path_end;
1827 if (aliasend || localend)
1829 if (aliasend && localend)
1831 /* we matched, and we can break out now */
1835 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1840 /* If we got here, it's safe to compare the next two tokens */
1841 if (*localptr != *aliasptr)
1852 /* If we got here, it's time to compare filenames */
1853 const char *aliasptr = alias;
1854 const char *localptr = localpath;
1856 if (alias_explicit_paths)
1858 aliasptr = &alias[alias_path_end + 1];
1861 if (local_explicit_paths)
1863 localptr = &localpath[local_path_end + 1];
1868 if (*aliasptr == '*')
1870 /* First, see if this is the last character */
1871 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1873 /* If so, there's no need to match anything else */
1876 /* Otherwise, we need to search for an appropriate matching sequence in local.
1877 * Note that we only need to search up to the next asterisk.
1881 const char *local_orig = localptr;
1885 while (*aliasptr != '\0' && *aliasptr != '*')
1887 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1888 if (*aliasptr != *localptr)
1898 /* If we didn't get a match, we need to advance the search stream.
1899 * This will let us skip tokens while still searching for another match.
1901 localptr = ++local_orig;
1902 /* Make sure we don't go out of bounds */
1903 if (*localptr == '\0')
1914 /* If we got a match, move on to the next token */
1917 } else if (*aliasptr == '\0') {
1918 if (*localptr == '\0'
1920 strcmp(localptr, ".smx") == 0)
1926 } else if (*aliasptr != *localptr) {
1936 bool CPluginManager::IsLateLoadTime() const
1938 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1941 void CPluginManager::OnSourceModAllInitialized()
1943 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1946 g_HandleSys.InitAccessDefaults(NULL, &sec);
1948 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1949 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1951 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1952 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1954 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1956 g_ShareSys.AddInterface(NULL, this);
1959 void CPluginManager::OnSourceModShutdown()
1961 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1965 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1966 g_ShareSys.DestroyIdentType(g_PluginIdent);
1967 g_ShareSys.DestroyIdentity(m_MyIdent);
1970 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1972 /* We don't care about the internal object, actually */
1975 bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1977 *pSize = ((CPlugin *)object)->CalcMemUsage();
1981 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1989 sec.pIdentity = m_MyIdent;
1991 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
2004 CPlugin *CPluginManager::GetPluginByOrder(int num)
2006 if (num < 1 || num > (int)GetPluginCount())
2014 SourceHook::List<CPlugin *>::iterator iter;
2015 for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {}
2022 const char *CPluginManager::GetStatusText(PluginStatus st)
2026 case Plugin_Running:
2032 case Plugin_Uncompiled:
2033 return "Uncompiled";
2034 case Plugin_BadLoad:
2044 void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command)
2046 int argcount = command.ArgC();
2049 const char *cmd = command.Arg(2);
2050 if (strcmp(cmd, "list") == 0)
2053 unsigned int id = 1;
2054 int plnum = GetPluginCount();
2058 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
2063 g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
2067 SourceHook::List<CPlugin *>::iterator iter;
2068 SourceHook::List<CPlugin *> m_FailList;
2070 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2073 assert(pl->GetStatus() != Plugin_Created);
2075 const sm_plugininfo_t *info = pl->GetPublicInfo();
2076 if (pl->GetStatus() != Plugin_Running)
2078 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
2080 if (pl->GetStatus() <= Plugin_Error)
2082 /* Plugin has failed to load. */
2083 m_FailList.push_back(pl);
2088 len += UTIL_Format(buffer, sizeof(buffer), " %02d", id);
2090 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2091 if (IS_STR_FILLED(info->version))
2093 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
2095 if (IS_STR_FILLED(info->author))
2097 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
2099 g_RootMenu.ConsolePrint("%s", buffer);
2102 if (!m_FailList.empty())
2104 g_RootMenu.ConsolePrint("Load Errors:");
2106 SourceHook::List<CPlugin *>::iterator _iter;
2110 for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++)
2112 pl = (CPlugin *)*_iter;
2113 g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg);
2119 else if (strcmp(cmd, "load") == 0)
2123 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
2129 const char *filename = command.Arg(3);
2131 char pluginfile[256];
2132 const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx";
2133 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext);
2135 IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
2139 g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile);
2145 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile);
2149 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error);
2154 else if (strcmp(cmd, "unload") == 0)
2158 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>");
2164 const char *arg = command.Arg(3);
2165 int id = strtol(arg, &end, 10);
2169 pl = GetPluginByOrder(id);
2172 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2178 char pluginfile[256];
2179 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2180 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2182 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2184 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2189 char name[PLATFORM_MAX_PATH];
2190 const sm_plugininfo_t *info = pl->GetPublicInfo();
2191 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2193 if (UnloadPlugin(pl))
2195 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
2199 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
2204 else if (strcmp(cmd, "unload_all") == 0)
2206 g_PluginSys.UnloadAll();
2207 g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded.");
2210 else if (strcmp(cmd, "load_lock") == 0)
2212 if (m_LoadingLocked)
2214 g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect.");
2218 m_LoadingLocked = true;
2219 g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded.");
2223 else if (strcmp(cmd, "load_unlock") == 0)
2225 if (m_LoadingLocked)
2227 m_LoadingLocked = false;
2228 g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect.");
2232 g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect.");
2236 else if (strcmp(cmd, "info") == 0)
2240 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
2246 const char *arg = command.Arg(3);
2247 int id = strtol(arg, &end, 10);
2251 pl = GetPluginByOrder(id);
2254 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2260 char pluginfile[256];
2261 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2262 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2264 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2266 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2271 const sm_plugininfo_t *info = pl->GetPublicInfo();
2273 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
2274 if (pl->GetStatus() <= Plugin_Error)
2276 if (IS_STR_FILLED(info->name))
2278 if (IS_STR_FILLED(info->description))
2280 g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description);
2282 g_RootMenu.ConsolePrint(" Title: %s", info->name);
2285 if (IS_STR_FILLED(info->author))
2287 g_RootMenu.ConsolePrint(" Author: %s", info->author);
2289 if (IS_STR_FILLED(info->version))
2291 g_RootMenu.ConsolePrint(" Version: %s", info->version);
2293 if (IS_STR_FILLED(info->url))
2295 g_RootMenu.ConsolePrint(" URL: %s", info->url);
2297 if (pl->GetStatus() == Plugin_Error)
2299 g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg);
2303 if (pl->GetStatus() == Plugin_Running)
2305 if (pl->IsDebugging())
2307 g_RootMenu.ConsolePrint(" Status: running, debugging");
2311 g_RootMenu.ConsolePrint(" Status: running");
2316 g_RootMenu.ConsolePrint(" Status: not running");
2319 const char *typestr = "";
2320 switch (pl->GetType())
2322 case PluginType_MapUpdated:
2323 typestr = "Map Change if Updated";
2325 case PluginType_MapOnly:
2326 typestr = "Map Change";
2328 case PluginType_Private:
2329 case PluginType_Global:
2334 g_RootMenu.ConsolePrint(" Reloads: %s", typestr);
2336 if (pl->m_FileVersion >= 3)
2338 g_RootMenu.ConsolePrint(" Timestamp: %s", pl->m_DateTime);
2343 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
2344 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
2345 info->name ? info->name : "<none>",
2346 info->version ? info->version : "<none>");
2347 if (IS_STR_FILLED(info->url))
2349 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
2355 else if (strcmp(cmd, "debug") == 0)
2359 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
2365 const char *arg = command.Arg(3);
2366 int id = strtol(arg, &end, 10);
2370 pl = GetPluginByOrder(id);
2373 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2379 char pluginfile[256];
2380 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2381 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2383 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2385 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2391 const char *mode = command.Arg(4);
2392 if ((res=strcmp("on", mode)) && strcmp("off", mode))
2394 g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
2408 if (debug && pl->IsDebugging())
2410 g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
2413 else if (!debug && !pl->IsDebugging())
2415 g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
2420 if (pl->ToggleDebugMode(debug, error, sizeof(error)))
2422 g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
2427 g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
2428 g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
2432 else if (strcmp(cmd, "refresh") == 0)
2434 g_SourceMod.DoGlobalPluginLoads();
2435 g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded.");
2438 else if (strcmp(cmd, "reload") == 0)
2442 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>");
2448 const char *arg = command.Arg(3);
2449 int id = strtol(arg, &end, 10);
2453 pl = GetPluginByOrder(id);
2456 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id);
2462 char pluginfile[256];
2463 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2464 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2466 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))
2468 g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile);
2473 char name[PLATFORM_MAX_PATH];
2474 const sm_plugininfo_t *info = pl->GetPublicInfo();
2475 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
2477 if (ReloadPlugin(pl))
2479 g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name);
2483 g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name);
2490 /* Draw the main menu */
2491 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
2492 g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
2493 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
2494 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
2495 g_RootMenu.DrawGenericOption("load", "Load a plugin");
2496 g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded");
2497 g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading");
2498 g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder");
2499 g_RootMenu.DrawGenericOption("reload", "Reloads a plugin");
2500 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
2501 g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins");
2504 bool CPluginManager::ReloadPlugin(CPlugin *pl)
2506 List<CPlugin *>::iterator iter;
2507 char filename[PLATFORM_MAX_PATH];
2508 bool debug, wasloaded;
2513 strcpy(filename, pl->m_filename);
2514 debug = pl->IsDebugging();
2515 ptype = pl->GetType();
2517 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++)
2525 if (!UnloadPlugin(pl))
2529 if (!(newpl=LoadPlugin(filename, debug, ptype, NULL, 0, &wasloaded)))
2534 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2536 if ((*iter) == (CPlugin *)newpl)
2538 m_plugins.erase(iter);
2544 for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i<id; iter++, i++)
2547 m_plugins.insert(iter, (CPlugin *)newpl);
2552 void CPluginManager::ReloadOrUnloadPlugins()
2554 /* If we're in a load lock, just skip this whole bit. */
2555 if (m_LoadingLocked)
2560 List<CPlugin *>::iterator iter;
2561 List<CPlugin *> tmp_list = m_plugins;
2565 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
2568 if (pl->GetType() == PluginType_MapOnly)
2570 UnloadPlugin((IPlugin *)pl);
2572 else if (pl->GetType() == PluginType_MapUpdated)
2574 t = pl->GetFileTimeStamp();
2575 if (!t || t > pl->GetTimeStamp())
2577 pl->SetTimeStamp(t);
2578 UnloadPlugin((IPlugin *)pl);
2584 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
2586 List<IPluginsListener *>::iterator iter;
2587 IPluginsListener *pListener;
2588 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
2590 pListener = (*iter);
2591 pListener->OnPluginPauseChange(pl, paused);
2595 void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward)
2597 List<CPlugin *>::iterator iter;
2599 IPluginFunction *pFunction;
2601 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++)
2605 if (pPlugin->GetStatus() <= Plugin_Paused)
2607 pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name);
2611 pForward->AddFunction(pFunction);
2617 CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken)
2619 if (pToken->type != g_PluginIdent)
2624 return (CPlugin *)(pToken->ptr);
2627 void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop)
2629 List<CPlugin *>::iterator iter;
2646 const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded";
2648 for (iter=m_plugins.begin();
2649 iter!=m_plugins.end();
2652 CPlugin *pl = (*iter);
2653 if (pl->GetStatus() != Plugin_Running)
2657 IPluginContext *pContext = pl->GetBaseContext();
2658 IPluginFunction *pf = pContext->GetFunctionByName(name);
2663 uint32_t num_vars = pContext->GetPubVarsNum();
2664 for (uint32_t i=0; i<num_vars; i++)
2666 sp_pubvar_t *pubvar;
2667 if (pContext->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
2671 if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0)
2673 plc = (_pl *)pubvar->offs;
2679 pContext->LocalToString(plc->name, &str);
2680 if (strcmp(str, lib) != 0)
2684 pf->PushString(lib);
2687 else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0)
2689 ext = (_ext *)pubvar->offs;
2695 pContext->LocalToString(ext->name, &str);
2696 if (strcmp(str, lib) != 0)
2700 pf->PushString(lib);
2707 bool CPluginManager::LibraryExists(const char *lib)
2709 List<CPlugin *>::iterator iter;
2711 for (iter=m_plugins.begin();
2712 iter!=m_plugins.end();
2715 CPlugin *pl = (*iter);
2716 if (pl->GetStatus() != Plugin_Running)
2720 List<String>::iterator s_iter;
2721 for (s_iter = pl->m_Libraries.begin();
2722 s_iter != pl->m_Libraries.end();
2725 if ((*s_iter).compare(lib) == 0)
2735 void CPluginManager::AllPluginsLoaded()
2737 List<CPlugin *>::iterator iter;
2740 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
2743 pl->Call_OnAllPluginsLoaded();
2747 void CPluginManager::UnloadAll()
2749 List<CPlugin *>::iterator iter;
2750 while ( (iter = m_plugins.begin()) != m_plugins.end() )
2752 UnloadPlugin((*iter));
2756 int CPluginManager::GetOrderOfPlugin(IPlugin *pl)
2759 List<CPlugin *>::iterator iter;
2761 for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++)
2772 CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg)
2778 id = strtol(arg, &end, 10);
2782 pl = GetPluginByOrder(id);
2790 char pluginfile[256];
2791 const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx";
2792 UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext);
2794 if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl))