2 * ===============================================================
3 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
4 * ===============================================================
6 * This file is not open source and may not be copied without explicit
7 * written permission of AlliedModders LLC. This file may not be redistributed
8 * in whole or significant part.
9 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
15 #include "PluginSys.h"
17 #include "LibrarySys.h"
18 #include "HandleSys.h"
19 #include "sourcemm_api.h"
20 #include "sourcemod.h"
21 #include "CTextParsers.h"
23 #include "ExtensionSys.h"
24 #include "sm_srvcmds.h"
25 #include "sm_stringutil.h"
27 CPluginManager g_PluginSys;
28 HandleType_t g_PluginType = 0;
29 IdentityType_t g_PluginIdent = 0;
31 CPlugin::CPlugin(const char *file)
33 static int MySerial = 0;
35 m_type = PluginType_Private;
36 m_status = Plugin_Uncompiled;
37 m_serial = ++MySerial;
39 m_errormsg[256] = '\0';
40 snprintf(m_filename, sizeof(m_filename), "%s", file);
50 sec.pOwner = g_PluginSys.GetIdentity();
51 sec.pIdentity = sec.pOwner;
53 g_HandleSys.FreeHandle(m_handle, &sec);
54 g_ShareSys.DestroyIdentity(m_ident);
64 m_ctx.vm->FreeContext(m_ctx.ctx);
69 m_ctx.vm->AbortCompilation(m_ctx.co);
75 g_pSourcePawn->FreeFromMemory(m_plugin);
80 void CPlugin::InitIdentity()
84 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
85 m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
86 m_ctx.base->SetIdentity(m_ident);
90 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
92 char fullpath[PLATFORM_MAX_PATH+1];
93 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
94 FILE *fp = fopen(fullpath, "rb");
96 CPlugin *pPlugin = new CPlugin(file);
102 snprintf(error, maxlength, "Unable to open file");
104 pPlugin->m_status = Plugin_BadLoad;
109 sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
115 snprintf(error, maxlength, "Error %d while parsing plugin", err);
117 pPlugin->m_status = Plugin_BadLoad;
123 pPlugin->m_plugin = pl;
128 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
135 /* :NOTICE: We will eventually need to change these natives
136 * for swapping in new contexts
138 if (m_ctx.co || m_ctx.ctx)
143 m_status = Plugin_Uncompiled;
146 m_ctx.co = vm->StartCompilation(m_plugin);
151 void CPlugin::CancelMyCompile()
158 m_ctx.vm->AbortCompilation(m_ctx.co);
163 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
171 m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
174 memset(&m_ctx, 0, sizeof(m_ctx));
177 snprintf(error, maxlength, "Failed to compile (error %d)", err);
182 m_ctx.base = new BaseContext(m_ctx.ctx);
183 m_ctx.base->SetRunnable(false);
184 m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
186 m_status = Plugin_Created;
194 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
199 va_start(ap, error_fmt);
200 vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
205 m_ctx.base->SetRunnable(false);
209 void CPlugin::UpdateInfo()
211 /* Now grab the info */
213 IPluginContext *base = GetBaseContext();
214 int err = base->FindPubvarByName("myinfo", &idx);
216 memset(&m_info, 0, sizeof(m_info));
218 if (err == SP_ERROR_NONE)
220 struct sm_plugininfo_c_t
228 sm_plugininfo_c_t *cinfo;
231 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
232 base->LocalToString(cinfo->name, (char **)&m_info.name);
233 base->LocalToString(cinfo->description, (char **)&m_info.description);
234 base->LocalToString(cinfo->author, (char **)&m_info.author);
235 base->LocalToString(cinfo->url, (char **)&m_info.url);
236 base->LocalToString(cinfo->version, (char **)&m_info.version);
239 m_info.author = m_info.author ? m_info.author : "";
240 m_info.description = m_info.description ? m_info.description : "";
241 m_info.name = m_info.name ? m_info.name : "";
242 m_info.url = m_info.url ? m_info.url : "";
243 m_info.version = m_info.version ? m_info.version : "";
246 void CPlugin::Call_OnPluginStart()
248 if (m_status != Plugin_Loaded)
253 m_status = Plugin_Running;
256 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart");
262 pFunction->PushCell(m_handle);
263 pFunction->Execute(&result);
266 void CPlugin::Call_OnPluginEnd()
268 if (m_status < Plugin_Paused)
274 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd");
280 pFunction->Execute(&result);
283 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
285 if (m_status != Plugin_Created)
290 m_status = Plugin_Loaded;
291 m_ctx.base->SetRunnable(true);
295 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad");
302 pFunction->PushCell(m_handle);
303 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
304 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
305 pFunction->PushCell(maxlength);
306 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
311 if (!result || m_status != Plugin_Loaded)
319 const sp_plugin_t *CPlugin::GetPluginStructure() const
324 IPluginContext *CPlugin::GetBaseContext() const
329 sp_context_t *CPlugin::GetContext() const
334 const char *CPlugin::GetFilename() const
339 PluginType CPlugin::GetType() const
344 const sm_plugininfo_t *CPlugin::GetPublicInfo() const
349 unsigned int CPlugin::GetSerial() const
354 PluginStatus CPlugin::GetStatus() const
359 bool CPlugin::IsDebugging() const
366 return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
369 bool CPlugin::SetPauseState(bool paused)
371 if (paused && GetStatus() != Plugin_Paused)
374 } else if (!paused && GetStatus() != Plugin_Running) {
378 m_status = (paused) ? Plugin_Paused : Plugin_Running;
380 IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange");
384 pFunction->PushCell(paused ? 1 : 0);
385 pFunction->Execute(&result);
388 g_PluginSys._SetPauseState(this, paused);
393 IdentityToken_t *CPlugin::GetIdentity() const
398 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
406 snprintf(error, maxlength, "Plugin is not runnable.");
411 if (debug && IsDebugging())
415 snprintf(error, maxlength, "Plugin is already in debug mode.");
418 } else if (!debug && !IsDebugging()) {
421 snprintf(error, maxlength, "Plugins is already in production mode.");
426 ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
427 if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
431 snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
436 sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
440 memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
441 new_ctx->hp = m_ctx.ctx->hp;
442 new_ctx->sp = m_ctx.ctx->sp;
443 new_ctx->frm = m_ctx.ctx->frm;
444 new_ctx->dbreak = m_ctx.ctx->dbreak;
445 new_ctx->context = m_ctx.ctx->context;
446 memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
448 g_pVM->FreeContext(m_ctx.ctx);
450 m_ctx.base->SetContext(new_ctx);
456 snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
464 bool CPlugin::IsRunnable() const
466 return (m_status <= Plugin_Paused) ? true : false;
469 time_t CPlugin::GetFileTimeStamp()
471 char path[PLATFORM_MAX_PATH+1];
472 g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename);
473 #ifdef PLATFORM_WINDOWS
475 if (_stat(path, &s) != 0)
476 #elif defined PLATFORM_POSIX
478 if (stat(path, &s) != 0)
487 time_t CPlugin::GetTimeStamp() const
492 void CPlugin::SetTimeStamp(time_t t)
497 void CPlugin::AddLangFile(unsigned int index)
499 m_PhraseFiles.push_back(index);
502 size_t CPlugin::GetLangFileCount() const
504 return m_PhraseFiles.size();
507 unsigned int CPlugin::GetFileByIndex(unsigned int index) const
509 return m_PhraseFiles.at(index);
516 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
522 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
527 bool CPluginManager::CPluginIterator::MorePlugins()
529 return (current != mylist->end());
532 void CPluginManager::CPluginIterator::NextPlugin()
537 void CPluginManager::CPluginIterator::Release()
539 g_PluginSys.ReleaseIterator(this);
542 CPluginManager::CPluginIterator::~CPluginIterator()
546 void CPluginManager::CPluginIterator::Reset()
548 current = mylist->begin();
555 CPluginManager::CPluginManager()
557 m_LoadLookup = sm_trie_create();
558 m_AllPluginsLoaded = false;
562 CPluginManager::~CPluginManager()
565 * Ignore the fact that there might be plugins in the cache.
566 * This usually means that Core is not being unloaded properly, and everything
567 * will crash anyway. YAY
569 sm_trie_destroy(m_LoadLookup);
573 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
575 /* First read in the database of plugin settings */
577 unsigned int line, col;
578 m_AllPluginsLoaded = false;
579 if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
581 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
582 const char *err_msg = g_TextParser.GetSMCErrorString(err);
585 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
589 LoadPluginsFromDir(basedir, NULL);
592 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
594 char base_path[PLATFORM_MAX_PATH+1];
596 /* Form the current path to start reading from */
597 if (localpath == NULL)
599 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
601 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
604 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
609 g_LibSys.GetPlatformError(error, sizeof(error));
610 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
611 g_Logger.LogError("[SM] Platform returned error: %s", error);
615 while (dir->MoreFiles())
617 if (dir->IsEntryDirectory()
618 && (strcmp(dir->GetEntryName(), ".") != 0)
619 && (strcmp(dir->GetEntryName(), "..") != 0)
620 && (strcmp(dir->GetEntryName(), "disabled") != 0)
621 && (strcmp(dir->GetEntryName(), "optional") != 0))
623 char new_local[PLATFORM_MAX_PATH+1];
624 if (localpath == NULL)
626 /* If no path yet, don't add a former slash */
627 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
629 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
631 LoadPluginsFromDir(basedir, new_local);
632 } else if (dir->IsEntryFile()) {
633 const char *name = dir->GetEntryName();
634 size_t len = strlen(name);
636 && strcmp(&name[len-4], ".smx") == 0)
638 /* If the filename matches, load the plugin */
639 char plugin[PLATFORM_MAX_PATH+1];
640 if (localpath == NULL)
642 snprintf(plugin, sizeof(plugin), "%s", name);
644 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
646 LoadAutoPlugin(plugin);
651 g_LibSys.CloseDirectory(dir);
654 //well i have discovered that gabe newell is very fat, so i wrote this comment now
655 LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t err_max)
658 * Does this plugin already exist?
661 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
663 /* Check to see if we should try reloading it */
664 if (pPlugin->GetStatus() == Plugin_BadLoad
665 || pPlugin->GetStatus() == Plugin_Error
666 || pPlugin->GetStatus() == Plugin_Failed)
668 UnloadPlugin(pPlugin);
674 return LoadRes_AlreadyLoaded;
678 pPlugin = CPlugin::CreatePlugin(path, error, err_max);
680 assert(pPlugin != NULL);
682 pPlugin->m_type = PluginType_MapUpdated;
684 ICompilation *co = NULL;
686 if (pPlugin->m_status == Plugin_Uncompiled)
688 co = pPlugin->StartMyCompile(g_pVM);
691 PluginSettings *pset;
692 unsigned int setcount = m_PluginInfo.GetSettingsNum();
693 for (unsigned int i=0; i<setcount; i++)
695 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
699 pPlugin->m_type = pset->type_val;
702 for (unsigned int j=0; j<pset->opts_num; j++)
704 const char *key, *val;
705 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
710 if (!g_pVM->SetCompilationOption(co, key, val))
714 snprintf(error, err_max, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
716 pPlugin->CancelMyCompile();
724 /* Do the actual compiling */
727 pPlugin->FinishMyCompile(NULL, 0);
732 if (pPlugin->GetStatus() == Plugin_Created)
734 AddCoreNativesToPlugin(pPlugin);
735 pPlugin->InitIdentity();
736 if (pPlugin->Call_AskPluginLoad(error, err_max))
738 /* Autoload any modules */
739 LoadOrRequireExtensions(pPlugin, 1, error, err_max);
743 /* Save the time stamp */
744 time_t t = pPlugin->GetFileTimeStamp();
745 pPlugin->SetTimeStamp(t);
752 return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure;
755 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max, bool *wasloaded)
761 if ((res=_LoadPlugin(&pl, path, debug, type, error, err_max)) == LoadRes_Failure)
767 if (res == LoadRes_AlreadyLoaded)
775 /* Run second pass if we need to */
776 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
778 if (!RunSecondPass(pl, error, err_max))
788 void CPluginManager::LoadAutoPlugin(const char *plugin)
792 char error[255] = "Unknown error";
794 if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure)
796 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
797 pl->SetErrorState(Plugin_Failed, "%s", error);
800 if (res == LoadRes_Successful)
806 void CPluginManager::AddPlugin(CPlugin *pPlugin)
808 List<IPluginsListener *>::iterator iter;
809 IPluginsListener *pListener;
811 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
814 pListener->OnPluginCreated(pPlugin);
817 m_plugins.push_back(pPlugin);
818 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
821 void CPluginManager::LoadAll_SecondPass()
823 List<CPlugin *>::iterator iter;
827 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
830 if (pPlugin->GetStatus() == Plugin_Loaded)
833 if (!RunSecondPass(pPlugin, error, sizeof(error)))
835 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
836 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
841 m_AllPluginsLoaded = true;
844 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
846 /* Find any extensions this plugin needs */
855 IPluginContext *pBase = pPlugin->GetBaseContext();
856 uint32_t num = pBase->GetPubVarsNum();
859 char path[PLATFORM_MAX_PATH+1];
861 for (uint32_t i=0; i<num; i++)
863 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
867 if (strncmp(pubvar->name, "__ext_", 6) == 0)
869 ext = (_ext *)pubvar->offs;
870 if (!ext->required && !ext->autoload)
874 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
878 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
884 /* Attempt to auto-load if necessary */
887 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
888 g_Extensions.LoadAutoExtension(path);
890 } else if (pass == 2) {
891 /* Is this required? */
894 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
895 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
897 pExt = g_Extensions.FindExtensionByName(name);
899 /* :TODO: should we bind to unloaded extensions?
900 * Currently the extension manager will ignore this.
902 if (!pExt || !pExt->IsRunning(NULL, 0))
906 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
910 g_Extensions.BindChildPlugin(pExt, pPlugin);
920 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
922 /* Second pass for extension requirements */
923 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
928 /* Bind all extra natives */
929 g_Extensions.BindAllNativesToPlugin(pPlugin);
931 /* Find any unbound natives
932 * Right now, these are not allowed
934 IPluginContext *pContext = pPlugin->GetBaseContext();
935 uint32_t num = pContext->GetNativesNum();
937 for (unsigned int i=0; i<num; i++)
939 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
943 if (native->status == SP_NATIVE_UNBOUND)
947 snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
953 /* Finish by telling all listeners */
954 List<IPluginsListener *>::iterator iter;
955 IPluginsListener *pListener;
956 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
959 pListener->OnPluginLoaded(pPlugin);
962 /* Tell this plugin to finish initializing itself */
963 pPlugin->Call_OnPluginStart();
968 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
970 List<sp_nativeinfo_t *>::iterator iter;
972 for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
974 sp_nativeinfo_t *natives = (*iter);
975 IPluginContext *ctx = pPlugin->GetBaseContext();
977 /* Attempt to bind every native! */
978 while (natives[i].func != NULL)
980 ctx->BindNative(&natives[i++]);
985 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
987 CPlugin *pPlugin = (CPlugin *)plugin;
989 /* This prevents removal during insertion or anything else weird */
990 if (m_plugins.find(pPlugin) == m_plugins.end())
995 List<IPluginsListener *>::iterator iter;
996 IPluginsListener *pListener;
998 if (pPlugin->GetStatus() <= Plugin_Error)
1000 /* Notify listeners of unloading */
1001 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1003 pListener = (*iter);
1004 pListener->OnPluginUnloaded(pPlugin);
1007 pPlugin->Call_OnPluginEnd();
1010 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1012 /* Notify listeners of destruction */
1013 pListener = (*iter);
1014 pListener->OnPluginDestroyed(pPlugin);
1017 /* Remove us from the lookup table and linked list */
1018 m_plugins.remove(pPlugin);
1019 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
1021 /* Tell the plugin to delete itself */
1027 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1029 IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
1033 unsigned int CPluginManager::GetPluginCount()
1035 return m_plugins.size();
1038 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1040 m_listeners.push_back(listener);
1043 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1045 m_listeners.remove(listener);
1048 IPluginIterator *CPluginManager::GetPluginIterator()
1050 if (m_iters.empty())
1052 return new CPluginIterator(&m_plugins);
1054 CPluginIterator *iter = m_iters.front();
1061 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1066 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1068 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1069 size_t alias_len = 0;
1070 size_t local_len = 0;
1072 const char *ptr = alias;
1073 unsigned int alias_explicit_paths = 0;
1074 unsigned int alias_path_end = 0;
1075 while (*ptr != '\0')
1077 if (*ptr == '\\' || *ptr == '/')
1079 alias_explicit_paths++;
1080 alias_path_end = alias_len;
1086 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1088 /* Trailing slash is totally invalid here */
1093 unsigned int local_explicit_paths = 0;
1094 unsigned int local_path_end = 0;
1095 while (*ptr != '\0')
1097 if (*ptr == '\\' || *ptr == '/')
1099 local_explicit_paths++;
1100 local_path_end = local_len;
1106 /* If the alias has more explicit paths than the real path,
1107 * no match will be possible.
1109 if (alias_explicit_paths > local_explicit_paths)
1114 if (alias_explicit_paths)
1116 /* We need to find if the paths match now. For example, these should all match:
1118 * csdm optional/csdm
1119 * csdm/ban optional/crab/csdm/ban
1121 const char *aliasptr = alias;
1122 const char *localptr = localpath;
1126 if (*aliasptr != *localptr)
1128 /* We have to knock one path off */
1129 local_explicit_paths--;
1130 if (alias_explicit_paths > local_explicit_paths)
1132 /* Skip out if we're gonna have an impossible match */
1135 /* Eat up localptr tokens until we get a result */
1136 while (((localptr - localpath) < (int)local_path_end)
1138 && *localptr != '\\')
1142 /* Check if we hit the end of our searchable area.
1143 * This probably isn't possible because of the path
1144 * count check, but it's a good idea anyway.
1146 if ((localptr - localpath) >= (int)local_path_end)
1150 /* Consume the slash token */
1153 /* Reset the alias pointer so we can continue consuming */
1159 * This is safe because if localptr terminates early, aliasptr will too
1163 /* We should never reach the end of the string because of this check. */
1164 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1165 bool localend = (localptr - localpath) > (int)local_path_end;
1166 if (aliasend || localend)
1168 if (aliasend && localend)
1170 /* we matched, and we can break out now */
1174 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1179 /* If we got here, it's safe to compare the next two tokens */
1180 if (*localptr != *aliasptr)
1191 /* If we got here, it's time to compare filenames */
1192 const char *aliasptr = alias;
1193 const char *localptr = localpath;
1195 if (alias_explicit_paths)
1197 aliasptr = &alias[alias_path_end + 1];
1200 if (local_explicit_paths)
1202 localptr = &localpath[local_path_end + 1];
1207 if (*aliasptr == '*')
1209 /* First, see if this is the last character */
1210 if ((unsigned)(aliasptr - alias) == alias_len - 1)
1212 /* If so, there's no need to match anything else */
1215 /* Otherwise, we need to search for an appropriate matching sequence in local.
1216 * Note that we only need to search up to the next asterisk.
1220 const char *local_orig = localptr;
1224 while (*aliasptr != '\0' && *aliasptr != '*')
1226 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1227 if (*aliasptr != *localptr)
1237 /* If we didn't get a match, we need to advance the search stream.
1238 * This will let us skip tokens while still searching for another match.
1240 localptr = ++local_orig;
1241 /* Make sure we don't go out of bounds */
1242 if (*localptr == '\0')
1253 /* If we got a match, move on to the next token */
1256 } else if (*aliasptr == '\0') {
1257 if (*localptr == '\0'
1259 strcmp(localptr, ".smx") == 0)
1265 } else if (*aliasptr != *localptr) {
1275 bool CPluginManager::IsLateLoadTime() const
1277 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1280 void CPluginManager::OnSourceModAllInitialized()
1282 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1285 g_HandleSys.InitAccessDefaults(NULL, &sec);
1287 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1288 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1290 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1291 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1293 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1295 g_ShareSys.AddInterface(NULL, this);
1298 void CPluginManager::OnSourceModShutdown()
1300 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1302 List<CPlugin *>::iterator iter;
1303 while ( (iter = m_plugins.begin()) != m_plugins.end() )
1305 UnloadPlugin((*iter));
1308 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1309 g_ShareSys.DestroyIdentType(g_PluginIdent);
1310 g_ShareSys.DestroyIdentity(m_MyIdent);
1313 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1315 /* We don't care about the internal object, actually */
1318 void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
1320 m_natives.push_back(natives);
1323 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1331 sec.pIdentity = m_MyIdent;
1333 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1346 CPlugin *CPluginManager::GetPluginByOrder(int num)
1348 if (num < 1 || num > (int)GetPluginCount())
1356 IPluginIterator *iter = GetPluginIterator();
1357 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
1359 pl = (CPlugin *)(iter->GetPlugin());
1365 const char *CPluginManager::GetStatusText(PluginStatus st)
1369 case Plugin_Running:
1375 case Plugin_Uncompiled:
1376 return "Uncompiled";
1377 case Plugin_BadLoad:
1387 #define IS_STR_FILLED(var) (var[0] != '\0')
1388 void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1392 const char *cmd = g_RootMenu.GetArgument(2);
1393 if (strcmp(cmd, "list") == 0)
1396 unsigned int id = 1;
1397 int plnum = GetPluginCount();
1401 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
1404 g_RootMenu.ConsolePrint("[SM] Displaying %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1407 IPluginIterator *iter = GetPluginIterator();
1408 for (; iter->MorePlugins(); iter->NextPlugin(), id++)
1410 IPlugin *pl = iter->GetPlugin();
1411 assert(pl->GetStatus() != Plugin_Created);
1413 const sm_plugininfo_t *info = pl->GetPublicInfo();
1414 if (pl->GetStatus() != Plugin_Running)
1416 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1418 len += UTIL_Format(buffer, sizeof(buffer), " %02d", id);
1420 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1421 if (IS_STR_FILLED(info->version))
1423 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1425 if (IS_STR_FILLED(info->author))
1427 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
1429 g_RootMenu.ConsolePrint("%s", buffer);
1434 } else if (strcmp(cmd, "load") == 0) {
1437 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
1443 const char *filename = g_RootMenu.GetArgument(3);
1444 IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded);
1448 g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", filename);
1454 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename);
1456 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error);
1460 } else if (strcmp(cmd, "unload") == 0) {
1463 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>");
1468 int num = atoi(g_RootMenu.GetArgument(3));
1469 if (num < 1 || num > (int)GetPluginCount())
1471 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
1475 IPluginIterator *iter = GetPluginIterator();
1476 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++)
1478 IPlugin *pl = iter->GetPlugin();
1480 char name[PLATFORM_MAX_PATH+1];
1481 const sm_plugininfo_t *info = pl->GetPublicInfo();
1482 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1484 if (UnloadPlugin(pl))
1486 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
1488 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
1493 } else if (strcmp(cmd, "info") == 0) {
1496 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
1500 int num = atoi(g_RootMenu.GetArgument(3));
1501 if (num < 1 || num > (int)GetPluginCount())
1503 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1507 CPlugin *pl = GetPluginByOrder(num);
1508 const sm_plugininfo_t *info = pl->GetPublicInfo();
1510 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
1511 if (pl->GetStatus() <= Plugin_Error)
1513 if (IS_STR_FILLED(info->name))
1515 g_RootMenu.ConsolePrint(" Title: %s", info->name);
1517 if (IS_STR_FILLED(info->version))
1519 g_RootMenu.ConsolePrint(" Version: %s", info->version);
1521 if (IS_STR_FILLED(info->url))
1523 g_RootMenu.ConsolePrint(" URL: %s", info->url);
1525 if (IS_STR_FILLED(info->author))
1527 g_RootMenu.ConsolePrint(" Author: %s", info->author);
1529 if (IS_STR_FILLED(info->version))
1531 g_RootMenu.ConsolePrint(" Version: %s", info->version);
1533 if (IS_STR_FILLED(info->description))
1535 g_RootMenu.ConsolePrint(" Description: %s", info->description);
1537 g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "yes" : "no");
1538 g_RootMenu.ConsolePrint(" Paused: %s", pl->GetStatus() == Plugin_Running ? "no" : "yes");
1540 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
1541 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
1542 info->name ? info->name : "<none>",
1543 info->version ? info->version : "<none>");
1544 if (IS_STR_FILLED(info->url))
1546 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
1551 } else if (strcmp(cmd, "debug") == 0) {
1554 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
1558 int num = atoi(g_RootMenu.GetArgument(3));
1559 if (num < 1 || num > (int)GetPluginCount())
1561 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1566 const char *mode = g_RootMenu.GetArgument(4);
1567 if ((res=strcmp("on", mode)) && strcmp("off", mode))
1569 g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
1581 CPlugin *pl = GetPluginByOrder(num);
1582 if (debug && pl->IsDebugging())
1584 g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
1586 } else if (!debug && !pl->IsDebugging()) {
1587 g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
1592 if (pl->ToggleDebugMode(debug, error, sizeof(error)))
1594 g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
1597 g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
1598 g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
1604 /* Draw the main menu */
1605 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
1606 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
1607 g_RootMenu.DrawGenericOption("load", "Load a plugin");
1608 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
1609 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
1610 g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");
1613 void CPluginManager::ReloadOrUnloadPlugins()
1615 List<CPlugin *>::iterator iter;
1616 List<CPlugin *> tmp_list = m_plugins;
1620 for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++)
1623 if (pl->GetType() == PluginType_MapOnly)
1625 UnloadPlugin((IPlugin *)pl);
1626 } else if (pl->GetType() == PluginType_MapUpdated) {
1627 t = pl->GetFileTimeStamp();
1628 if (!t || t > pl->GetTimeStamp())
1630 pl->SetTimeStamp(t);
1631 UnloadPlugin((IPlugin *)pl);
1637 void CPluginManager::_SetPauseState(CPlugin *pl, bool paused)
1639 List<IPluginsListener *>::iterator iter;
1640 IPluginsListener *pListener;
1641 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1643 pListener = (*iter);
1644 pListener->OnPluginPauseChange(pl, paused);