4 #include "LibrarySys.h"
6 #include "sourcemm_api.h"
8 #include "CTextParsers.h"
10 #include "ExtensionSys.h"
11 #include "sm_srvcmds.h"
12 #include "sm_stringutil.h"
14 CPluginManager g_PluginSys;
15 HandleType_t g_PluginType = 0;
16 IdentityType_t g_PluginIdent = 0;
18 CPlugin::CPlugin(const char *file)
20 static int MySerial = 0;
22 m_type = PluginType_Private;
23 m_status = Plugin_Uncompiled;
24 m_serial = ++MySerial;
26 m_errormsg[256] = '\0';
27 snprintf(m_filename, sizeof(m_filename), "%s", file);
37 sec.pOwner = g_PluginSys.GetIdentity();
38 sec.pIdentity = sec.pOwner;
40 g_HandleSys.FreeHandle(m_handle, &sec);
41 g_ShareSys.DestroyIdentity(m_ident);
51 m_ctx.vm->FreeContext(m_ctx.ctx);
56 m_ctx.vm->AbortCompilation(m_ctx.co);
62 g_pSourcePawn->FreeFromMemory(m_plugin);
67 void CPlugin::InitIdentity()
71 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
72 m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
73 m_ctx.base->SetIdentity(m_ident);
77 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
79 char fullpath[PLATFORM_MAX_PATH+1];
80 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
81 FILE *fp = fopen(fullpath, "rb");
83 CPlugin *pPlugin = new CPlugin(file);
89 snprintf(error, maxlength, "Unable to open file");
91 pPlugin->m_status = Plugin_BadLoad;
96 sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
102 snprintf(error, maxlength, "Error %d while parsing plugin", err);
104 pPlugin->m_status = Plugin_BadLoad;
110 pPlugin->m_plugin = pl;
115 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
122 /* :NOTICE: We will eventually need to change these natives
123 * for swapping in new contexts
125 if (m_ctx.co || m_ctx.ctx)
130 m_status = Plugin_Uncompiled;
133 m_ctx.co = vm->StartCompilation(m_plugin);
138 void CPlugin::CancelMyCompile()
145 m_ctx.vm->AbortCompilation(m_ctx.co);
150 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
158 m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
161 memset(&m_ctx, 0, sizeof(m_ctx));
164 snprintf(error, maxlength, "Failed to compile (error %d)", err);
169 m_ctx.base = new BaseContext(m_ctx.ctx);
170 m_ctx.base->SetRunnable(false);
171 m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
173 m_status = Plugin_Created;
181 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
186 va_start(ap, error_fmt);
187 vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
192 m_ctx.base->SetRunnable(false);
196 void CPlugin::UpdateInfo()
198 /* Now grab the info */
200 IPluginContext *base = GetBaseContext();
201 int err = base->FindPubvarByName("myinfo", &idx);
203 if (err == SP_ERROR_NONE)
205 struct sm_plugininfo_c_t
213 sm_plugininfo_c_t *cinfo;
216 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
217 base->LocalToString(cinfo->name, (char **)&m_info.name);
218 base->LocalToString(cinfo->description, (char **)&m_info.description);
219 base->LocalToString(cinfo->author, (char **)&m_info.author);
220 base->LocalToString(cinfo->url, (char **)&m_info.url);
221 base->LocalToString(cinfo->version, (char **)&m_info.version);
224 m_info.author = m_info.author ? m_info.author : "";
225 m_info.description = m_info.description ? m_info.description : "";
226 m_info.name = m_info.name ? m_info.name : "";
227 m_info.url = m_info.url ? m_info.url : "";
228 m_info.version = m_info.version ? m_info.version : "";
231 void CPlugin::Call_OnPluginInit()
233 if (m_status != Plugin_Loaded)
238 m_status = Plugin_Running;
241 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginInit");
247 pFunction->PushCell(m_handle);
248 pFunction->Execute(&result);
251 void CPlugin::Call_OnPluginUnload()
253 if (m_status < Plugin_Paused)
259 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginUnload");
265 pFunction->Execute(&result);
268 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
270 if (m_status != Plugin_Created)
275 m_status = Plugin_Loaded;
276 m_ctx.base->SetRunnable(true);
280 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
287 pFunction->PushCell(m_handle);
288 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
289 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
290 pFunction->PushCell(maxlength);
291 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
296 if (!result || m_status != Plugin_Loaded)
304 const sp_plugin_t *CPlugin::GetPluginStructure() const
309 IPluginContext *CPlugin::GetBaseContext() const
314 sp_context_t *CPlugin::GetContext() const
319 const char *CPlugin::GetFilename() const
324 PluginType CPlugin::GetType() const
329 const sm_plugininfo_t *CPlugin::GetPublicInfo() const
334 unsigned int CPlugin::GetSerial() const
339 PluginStatus CPlugin::GetStatus() const
344 bool CPlugin::IsDebugging() const
351 return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
354 bool CPlugin::SetPauseState(bool paused)
356 if (paused && GetStatus() != Plugin_Paused)
359 } else if (!paused && GetStatus() != Plugin_Running) {
363 m_status = (paused) ? Plugin_Paused : Plugin_Running;
365 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
369 pFunction->PushCell(paused ? 1 : 0);
370 pFunction->Execute(&result);
376 IdentityToken_t *CPlugin::GetIdentity() const
381 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
389 snprintf(error, maxlength, "Plugin is not runnable.");
394 if (debug && IsDebugging())
398 snprintf(error, maxlength, "Plugin is already in debug mode.");
401 } else if (!debug && !IsDebugging()) {
404 snprintf(error, maxlength, "Plugins is already in production mode.");
409 ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
410 if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
414 snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
419 sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
423 memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
424 new_ctx->hp = m_ctx.ctx->hp;
425 new_ctx->sp = m_ctx.ctx->sp;
426 new_ctx->frm = m_ctx.ctx->frm;
427 new_ctx->dbreak = m_ctx.ctx->dbreak;
428 new_ctx->context = m_ctx.ctx->context;
429 memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
431 g_pVM->FreeContext(m_ctx.ctx);
433 m_ctx.base->SetContext(new_ctx);
439 snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
447 bool CPlugin::IsRunnable() const
449 return (m_status <= Plugin_Paused) ? true : false;
452 time_t CPlugin::GetFileTimeStamp()
454 char path[PLATFORM_MAX_PATH+1];
455 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
456 #ifdef PLATFORM_WINDOWS
458 if (_stat(path, &s) != 0)
459 #else if defined PLATFORM_POSIX
461 if (stat(path, &s) != 0)
464 g_Logger.LogError("Could not obtain plugin time stamp, error: %d", errno);
471 time_t CPlugin::GetTimeStamp() const
476 void CPlugin::SetTimeStamp(time_t t)
485 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
491 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
496 bool CPluginManager::CPluginIterator::MorePlugins()
498 return (current != mylist->end());
501 void CPluginManager::CPluginIterator::NextPlugin()
506 void CPluginManager::CPluginIterator::Release()
508 g_PluginSys.ReleaseIterator(this);
511 CPluginManager::CPluginIterator::~CPluginIterator()
515 void CPluginManager::CPluginIterator::Reset()
517 current = mylist->begin();
524 CPluginManager::CPluginManager()
526 m_LoadLookup = sm_trie_create();
527 m_AllPluginsLoaded = false;
531 CPluginManager::~CPluginManager()
534 * Ignore the fact that there might be plugins in the cache.
535 * This usually means that Core is not being unloaded properly, and everything
536 * will crash anyway. YAY
538 sm_trie_destroy(m_LoadLookup);
542 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
544 /* First read in the database of plugin settings */
546 unsigned int line, col;
547 m_AllPluginsLoaded = false;
548 if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
550 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
551 const char *err_msg = g_TextParser.GetSMCErrorString(err);
554 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
558 LoadPluginsFromDir(basedir, NULL);
561 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
563 char base_path[PLATFORM_MAX_PATH+1];
565 /* Form the current path to start reading from */
566 if (localpath == NULL)
568 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
570 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
573 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
578 g_LibSys.GetPlatformError(error, sizeof(error));
579 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
580 g_Logger.LogError("[SM] Platform returned error: %s", error);
584 while (dir->MoreFiles())
586 if (dir->IsEntryDirectory()
587 && (strcmp(dir->GetEntryName(), ".") != 0)
588 && (strcmp(dir->GetEntryName(), "..") != 0)
589 && (strcmp(dir->GetEntryName(), "disabled") != 0)
590 && (strcmp(dir->GetEntryName(), "optional") != 0))
592 char new_local[PLATFORM_MAX_PATH+1];
593 if (localpath == NULL)
595 /* If no path yet, don't add a former slash */
596 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
598 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
600 LoadPluginsFromDir(basedir, new_local);
601 } else if (dir->IsEntryFile()) {
602 const char *name = dir->GetEntryName();
603 size_t len = strlen(name);
605 && strcmp(&name[len-4], ".smx") == 0)
607 /* If the filename matches, load the plugin */
608 char plugin[PLATFORM_MAX_PATH+1];
609 if (localpath == NULL)
611 snprintf(plugin, sizeof(plugin), "%s", name);
613 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
615 LoadAutoPlugin(plugin);
620 g_LibSys.CloseDirectory(dir);
623 //well i have discovered that gabe newell is very fat, so i wrote this comment now
624 bool CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t err_max)
627 * Does this plugin already exist?
630 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
632 /* First check the type */
633 PluginType type = pPlugin->GetType();
634 if (type == PluginType_Private
635 || type == PluginType_Global)
639 /* Check to see if we should try reloading it */
640 if (pPlugin->GetStatus() == Plugin_BadLoad
641 || pPlugin->GetStatus() == Plugin_Error
642 || pPlugin->GetStatus() == Plugin_Failed)
644 UnloadPlugin(pPlugin);
648 pPlugin = CPlugin::CreatePlugin(path, error, err_max);
650 assert(pPlugin != NULL);
652 pPlugin->m_type = PluginType_MapUpdated;
654 ICompilation *co = NULL;
656 if (pPlugin->m_status == Plugin_Uncompiled)
658 co = pPlugin->StartMyCompile(g_pVM);
661 PluginSettings *pset;
662 unsigned int setcount = m_PluginInfo.GetSettingsNum();
663 for (unsigned int i=0; i<setcount; i++)
665 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
669 pPlugin->m_type = pset->type_val;
672 for (unsigned int j=0; j<pset->opts_num; j++)
674 const char *key, *val;
675 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
680 if (!g_pVM->SetCompilationOption(co, key, val))
684 snprintf(error, err_max, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
686 pPlugin->CancelMyCompile();
694 /* Do the actual compiling */
697 pPlugin->FinishMyCompile(NULL, 0);
702 if (pPlugin->GetStatus() == Plugin_Created)
704 AddCoreNativesToPlugin(pPlugin);
705 pPlugin->InitIdentity();
706 if (pPlugin->Call_AskPluginLoad(error, err_max))
708 /* Autoload any modules */
709 LoadOrRequireExtensions(pPlugin, 1, error, err_max);
713 /* Save the time stamp */
714 time_t t = pPlugin->GetFileTimeStamp();
715 pPlugin->SetTimeStamp(t);
722 return (pPlugin->GetStatus() == Plugin_Loaded);
725 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
729 if (!_LoadPlugin(&pl, path, debug, type, error, err_max))
737 /* Run second pass if we need to */
738 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
740 if (!RunSecondPass(pl, error, err_max))
750 void CPluginManager::LoadAutoPlugin(const char *plugin)
753 char error[255] = "Unknown error";
755 if (!_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error)))
757 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
758 pl->SetErrorState(Plugin_Failed, "%s", error);
764 void CPluginManager::AddPlugin(CPlugin *pPlugin)
766 List<IPluginsListener *>::iterator iter;
767 IPluginsListener *pListener;
769 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
772 pListener->OnPluginCreated(pPlugin);
775 m_plugins.push_back(pPlugin);
776 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
779 void CPluginManager::LoadAll_SecondPass()
781 List<CPlugin *>::iterator iter;
785 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
788 if (pPlugin->GetStatus() == Plugin_Loaded)
791 if (!RunSecondPass(pPlugin, error, sizeof(error)))
793 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
794 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
799 m_AllPluginsLoaded = true;
802 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
804 /* Find any extensions this plugin needs */
813 IPluginContext *pBase = pPlugin->GetBaseContext();
814 uint32_t num = pBase->GetPubVarsNum();
817 char path[PLATFORM_MAX_PATH+1];
819 for (uint32_t i=0; i<num; i++)
821 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
825 if (strncmp(pubvar->name, "__ext_", 6) == 0)
827 ext = (_ext *)pubvar->offs;
828 if (!ext->required && !ext->autoload)
832 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
836 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
842 /* Attempt to auto-load if necessary */
845 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
846 g_Extensions.LoadAutoExtension(path);
848 } else if (pass == 2) {
849 /* Is this required? */
852 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
853 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
855 pExt = g_Extensions.FindExtensionByName(name);
857 /* :TODO: should we bind to unloaded extensions?
858 * Currently the extension manager will ignore this.
860 if (!pExt || !pExt->IsRunning(NULL, 0))
864 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
868 g_Extensions.BindChildPlugin(pExt, pPlugin);
878 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
880 /* Second pass for extension requirements */
881 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
886 /* Bind all extra natives */
887 g_Extensions.BindAllNativesToPlugin(pPlugin);
889 /* Find any unbound natives
890 * Right now, these are not allowed
892 IPluginContext *pContext = pPlugin->GetBaseContext();
893 uint32_t num = pContext->GetNativesNum();
895 for (unsigned int i=0; i<num; i++)
897 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
901 if (native->status == SP_NATIVE_UNBOUND)
905 snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
911 /* Finish by telling all listeners */
912 List<IPluginsListener *>::iterator iter;
913 IPluginsListener *pListener;
914 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
917 pListener->OnPluginLoaded(pPlugin);
920 /* Tell this plugin to finish initializing itself */
921 pPlugin->Call_OnPluginInit();
926 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
928 List<sp_nativeinfo_t *>::iterator iter;
930 for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
932 sp_nativeinfo_t *natives = (*iter);
933 IPluginContext *ctx = pPlugin->GetBaseContext();
935 /* Attempt to bind every native! */
936 while (natives[i].func != NULL)
938 ctx->BindNative(&natives[i++]);
943 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
945 CPlugin *pPlugin = (CPlugin *)plugin;
947 /* This prevents removal during insertion or anything else weird */
948 if (m_plugins.find(pPlugin) == m_plugins.end())
953 List<IPluginsListener *>::iterator iter;
954 IPluginsListener *pListener;
956 if (pPlugin->GetStatus() <= Plugin_Error)
958 /* Notify listeners of unloading */
959 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
962 pListener->OnPluginUnloaded(pPlugin);
965 pPlugin->Call_OnPluginUnload();
968 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
970 /* Notify listeners of destruction */
972 pListener->OnPluginDestroyed(pPlugin);
975 /* Remove us from the lookup table and linked list */
976 m_plugins.remove(pPlugin);
977 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
979 /* Tell the plugin to delete itself */
985 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
987 IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
991 unsigned int CPluginManager::GetPluginCount()
993 return m_plugins.size();
996 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
998 m_listeners.push_back(listener);
1001 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1003 m_listeners.remove(listener);
1006 IPluginIterator *CPluginManager::GetPluginIterator()
1008 if (m_iters.empty())
1010 return new CPluginIterator(&m_plugins);
1012 CPluginIterator *iter = m_iters.front();
1019 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1024 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1026 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1027 size_t alias_len = 0;
1028 size_t local_len = 0;
1030 const char *ptr = alias;
1031 unsigned int alias_explicit_paths = 0;
1032 unsigned int alias_path_end = 0;
1033 while (*ptr != '\0')
1035 if (*ptr == '\\' || *ptr == '/')
1037 alias_explicit_paths++;
1038 alias_path_end = alias_len;
1044 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1046 /* Trailing slash is totally invalid here */
1051 unsigned int local_explicit_paths = 0;
1052 unsigned int local_path_end = 0;
1053 while (*ptr != '\0')
1055 if (*ptr == '\\' || *ptr == '/')
1057 local_explicit_paths++;
1058 local_path_end = local_len;
1064 /* If the alias has more explicit paths than the real path,
1065 * no match will be possible.
1067 if (alias_explicit_paths > local_explicit_paths)
1072 if (alias_explicit_paths)
1074 /* We need to find if the paths match now. For example, these should all match:
1076 * csdm optional/csdm
1077 * csdm/ban optional/crab/csdm/ban
1079 const char *aliasptr = alias;
1080 const char *localptr = localpath;
1084 if (*aliasptr != *localptr)
1086 /* We have to knock one path off */
1087 local_explicit_paths--;
1088 if (alias_explicit_paths > local_explicit_paths)
1090 /* Skip out if we're gonna have an impossible match */
1093 /* Eat up localptr tokens until we get a result */
1094 while (((localptr - localpath) < (int)local_path_end)
1096 && *localptr != '\\')
1100 /* Check if we hit the end of our searchable area.
1101 * This probably isn't possible because of the path
1102 * count check, but it's a good idea anyway.
1104 if ((localptr - localpath) >= (int)local_path_end)
1108 /* Consume the slash token */
1111 /* Reset the alias pointer so we can continue consuming */
1117 * This is safe because if localptr terminates early, aliasptr will too
1121 /* We should never reach the end of the string because of this check. */
1122 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1123 bool localend = (localptr - localpath) > (int)local_path_end;
1124 if (aliasend || localend)
1126 if (aliasend && localend)
1128 /* we matched, and we can break out now */
1132 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1137 /* If we got here, it's safe to compare the next two tokens */
1138 if (*localptr != *aliasptr)
1149 /* If we got here, it's time to compare filenames */
1150 const char *aliasptr = alias;
1151 const char *localptr = localpath;
1153 if (alias_explicit_paths)
1155 aliasptr = &alias[alias_path_end + 1];
1158 if (local_explicit_paths)
1160 localptr = &localpath[local_path_end + 1];
1165 if (*aliasptr == '*')
1167 /* First, see if this is the last character */
1168 if (aliasptr - alias == alias_len - 1)
1170 /* If so, there's no need to match anything else */
1173 /* Otherwise, we need to search for an appropriate matching sequence in local.
1174 * Note that we only need to search up to the next asterisk.
1178 const char *local_orig = localptr;
1182 while (*aliasptr != '\0' && *aliasptr != '*')
1184 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1185 if (*aliasptr != *localptr)
1195 /* If we didn't get a match, we need to advance the search stream.
1196 * This will let us skip tokens while still searching for another match.
1198 localptr = ++local_orig;
1199 /* Make sure we don't go out of bounds */
1200 if (*localptr == '\0')
1211 /* If we got a match, move on to the next token */
1214 } else if (*aliasptr == '\0') {
1215 if (*localptr == '\0'
1217 strcmp(localptr, ".smx") == 0)
1223 } else if (*aliasptr != *localptr) {
1233 bool CPluginManager::IsLateLoadTime() const
1235 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1238 void CPluginManager::OnSourceModAllInitialized()
1240 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1243 g_HandleSys.InitAccessDefaults(NULL, &sec);
1245 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1246 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1248 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1249 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1251 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1253 g_ShareSys.AddInterface(NULL, this);
1256 void CPluginManager::OnSourceModShutdown()
1258 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1260 List<CPlugin *>::iterator iter;
1261 while ( (iter = m_plugins.begin()) != m_plugins.end() )
1263 UnloadPlugin((*iter));
1266 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1267 g_ShareSys.DestroyIdentType(g_PluginIdent);
1268 g_ShareSys.DestroyIdentity(m_MyIdent);
1271 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1273 /* We don't care about the internal object, actually */
1276 void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
1278 m_natives.push_back(natives);
1281 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1289 sec.pIdentity = m_MyIdent;
1291 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1304 CPlugin *CPluginManager::GetPluginByOrder(int num)
1306 if (num < 1 || num > (int)GetPluginCount())
1314 IPluginIterator *iter = GetPluginIterator();
1315 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
1317 pl = (CPlugin *)(iter->GetPlugin());
1323 const char *CPluginManager::GetStatusText(PluginStatus st)
1327 case Plugin_Running:
1333 case Plugin_Uncompiled:
1334 return "Uncompiled";
1335 case Plugin_BadLoad:
1345 #define IS_STR_FILLED(var) (var[0] != '\0')
1346 void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1350 const char *cmd = g_RootMenu.GetArgument(2);
1351 if (strcmp(cmd, "list") == 0)
1354 unsigned int id = 1;
1355 int plnum = GetPluginCount();
1359 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
1362 g_RootMenu.ConsolePrint("[SM] Displaying %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1365 IPluginIterator *iter = GetPluginIterator();
1366 for (; iter->MorePlugins(); iter->NextPlugin(), id++)
1368 IPlugin *pl = iter->GetPlugin();
1369 assert(pl->GetStatus() != Plugin_Created);
1371 const sm_plugininfo_t *info = pl->GetPublicInfo();
1373 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1374 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1375 if (IS_STR_FILLED(info->version))
1377 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1379 if (IS_STR_FILLED(info->author))
1381 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
1383 g_RootMenu.ConsolePrint("%s", buffer);
1388 } else if (strcmp(cmd, "load") == 0) {
1391 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
1396 const char *filename = g_RootMenu.GetArgument(3);
1397 IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error));
1401 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename);
1403 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error);
1407 } else if (strcmp(cmd, "unload") == 0) {
1410 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>");
1415 int num = atoi(g_RootMenu.GetArgument(3));
1416 if (num < 1 || num > (int)GetPluginCount())
1418 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
1422 IPluginIterator *iter = GetPluginIterator();
1423 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++)
1425 IPlugin *pl = iter->GetPlugin();
1427 char name[PLATFORM_MAX_PATH+1];
1428 const sm_plugininfo_t *info = pl->GetPublicInfo();
1429 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1431 if (UnloadPlugin(pl))
1433 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
1435 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
1440 } else if (strcmp(cmd, "info") == 0) {
1443 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
1448 int num = atoi(g_RootMenu.GetArgument(3));
1449 if (num < 1 || num > (int)GetPluginCount())
1451 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1455 CPlugin *pl = GetPluginByOrder(num);
1456 const sm_plugininfo_t *info = pl->GetPublicInfo();
1458 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
1459 if (pl->GetStatus() <= Plugin_Error)
1461 if (IS_STR_FILLED(info->name))
1463 g_RootMenu.ConsolePrint(" Title: %s", info->name);
1465 if (IS_STR_FILLED(info->version))
1467 g_RootMenu.ConsolePrint(" Version: %s", info->version);
1469 if (IS_STR_FILLED(info->url))
1471 g_RootMenu.ConsolePrint(" URL: %s", info->url);
1473 if (IS_STR_FILLED(info->author))
1475 g_RootMenu.ConsolePrint(" Author: %s", info->author);
1477 if (IS_STR_FILLED(info->version))
1479 g_RootMenu.ConsolePrint(" Version: %s", info->version);
1481 if (IS_STR_FILLED(info->description))
1483 g_RootMenu.ConsolePrint(" Description: %s", info->description);
1485 g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "yes" : "no");
1486 g_RootMenu.ConsolePrint(" Paused: %s", pl->GetStatus() == Plugin_Running ? "yes" : "no");
1488 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
1489 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
1490 info->name ? info->name : "<none>",
1491 info->version ? info->version : "<none>");
1492 if (IS_STR_FILLED(info->url))
1494 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
1499 } else if (strcmp(cmd, "debug") == 0) {
1502 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
1506 int num = atoi(g_RootMenu.GetArgument(3));
1507 if (num < 1 || num > (int)GetPluginCount())
1509 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1514 const char *mode = g_RootMenu.GetArgument(4);
1515 if ((res=strcmp("on", mode)) && strcmp("off", mode))
1517 g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
1529 CPlugin *pl = GetPluginByOrder(num);
1530 if (debug && pl->IsDebugging())
1532 g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
1534 } else if (!debug && !pl->IsDebugging()) {
1535 g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
1540 if (pl->ToggleDebugMode(debug, error, sizeof(error)))
1542 g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
1545 g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
1546 g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
1552 /* Draw the main menu */
1553 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
1554 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
1555 g_RootMenu.DrawGenericOption("load", "Load a plugin");
1556 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
1557 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
1558 g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
1561 void CPluginManager::ReloadOrUnloadPlugins()
1563 List<CPlugin *>::iterator iter;
1564 List<CPlugin *> tmp_list = m_plugins;
1568 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
1571 if (pl->GetType() == PluginType_MapOnly)
1573 UnloadPlugin((IPlugin *)pl);
1574 } else if (pl->GetType() == PluginType_MapUpdated) {
1575 t=pl->GetFileTimeStamp();
1580 if (t > pl->GetTimeStamp())
1582 pl->SetTimeStamp(t);
1583 UnloadPlugin((IPlugin *)pl);