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;
29 m_errormsg[256] = '\0';
30 snprintf(m_filename, sizeof(m_filename), "%s", file);
40 sec.pOwner = g_PluginSys.GetIdentity();
41 sec.pIdentity = sec.pOwner;
43 g_HandleSys.FreeHandle(m_handle, &sec);
44 g_ShareSys.DestroyIdentity(m_ident);
54 m_ctx.vm->FreeContext(m_ctx.ctx);
59 m_ctx.vm->AbortCompilation(m_ctx.co);
65 for (uint32_t i=0; i<m_plugin->info.publics_num; i++)
67 g_PluginSys.ReleaseFunctionToPool(m_pub_funcs[i]);
69 delete [] m_pub_funcs;
75 for (unsigned int i=0; i<m_funcsnum; i++)
77 g_PluginSys.ReleaseFunctionToPool(m_priv_funcs[i]);
79 delete [] m_priv_funcs;
85 g_pSourcePawn->FreeFromMemory(m_plugin);
90 void CPlugin::InitIdentity()
94 m_ident = g_ShareSys.CreateIdentity(g_PluginIdent);
95 m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL);
96 m_ctx.base->SetIdentity(m_ident);
100 CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength)
102 char fullpath[PLATFORM_MAX_PATH+1];
103 g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file);
104 FILE *fp = fopen(fullpath, "rb");
106 CPlugin *pPlugin = new CPlugin(file);
112 snprintf(error, maxlength, "Unable to open file");
114 pPlugin->m_status = Plugin_BadLoad;
119 sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err);
125 snprintf(error, maxlength, "Error %d while parsing plugin", err);
127 pPlugin->m_status = Plugin_BadLoad;
133 pPlugin->m_plugin = pl;
138 ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm)
145 /* :NOTICE: We will eventually need to change these natives
146 * for swapping in new contexts
148 if (m_ctx.co || m_ctx.ctx)
153 m_status = Plugin_Uncompiled;
156 m_ctx.co = vm->StartCompilation(m_plugin);
161 void CPlugin::CancelMyCompile()
168 m_ctx.vm->AbortCompilation(m_ctx.co);
173 bool CPlugin::FinishMyCompile(char *error, size_t maxlength)
181 m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err);
184 memset(&m_ctx, 0, sizeof(m_ctx));
187 snprintf(error, maxlength, "Failed to compile (error %d)", err);
192 m_ctx.base = new BaseContext(m_ctx.ctx);
193 m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this;
195 m_funcsnum = m_ctx.vm->FunctionCount(m_ctx.ctx);
198 * Note: Since the m_plugin member will never change,
199 * it is safe to assume the function count will never change
201 if (m_funcsnum && m_priv_funcs == NULL)
203 m_priv_funcs = new CFunction *[m_funcsnum];
204 memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
209 if (m_plugin->info.publics_num && m_pub_funcs == NULL)
211 m_pub_funcs = new CFunction *[m_plugin->info.publics_num];
212 memset(m_pub_funcs, 0, sizeof(CFunction *) * m_plugin->info.publics_num);
217 m_status = Plugin_Created;
225 void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...)
230 va_start(ap, error_fmt);
231 vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap);
235 IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id)
237 CFunction *pFunc = NULL;
238 funcid_t save = func_id;
243 if (func_id >= m_plugin->info.publics_num)
247 pFunc = m_pub_funcs[func_id];
250 pFunc = g_PluginSys.GetFunctionFromPool(save, this);
251 m_pub_funcs[func_id] = pFunc;
256 if (!g_pVM->FunctionLookup(m_ctx.ctx, func_id, &index))
260 pFunc = m_priv_funcs[func_id];
263 pFunc = g_PluginSys.GetFunctionFromPool(save, this);
264 m_priv_funcs[func_id] = pFunc;
271 IPluginFunction *CPlugin::GetFunctionByName(const char *public_name)
274 IPluginContext *base = m_ctx.base;
276 if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE)
281 CFunction *pFunc = m_pub_funcs[index];
284 sp_public_t *pub = NULL;
285 base->GetPublicByIndex(index, &pub);
288 pFunc = g_PluginSys.GetFunctionFromPool(pub->funcid, this);
289 m_pub_funcs[index] = pFunc;
296 void CPlugin::UpdateInfo()
298 /* Now grab the info */
300 IPluginContext *base = GetBaseContext();
301 int err = base->FindPubvarByName("myinfo", &idx);
303 if (err == SP_ERROR_NONE)
305 struct sm_plugininfo_c_t
313 sm_plugininfo_c_t *cinfo;
316 base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo);
317 base->LocalToString(cinfo->name, (char **)&m_info.name);
318 base->LocalToString(cinfo->description, (char **)&m_info.description);
319 base->LocalToString(cinfo->author, (char **)&m_info.author);
320 base->LocalToString(cinfo->url, (char **)&m_info.url);
321 base->LocalToString(cinfo->version, (char **)&m_info.version);
324 m_info.author = m_info.author ? m_info.author : "";
325 m_info.description = m_info.description ? m_info.description : "";
326 m_info.name = m_info.name ? m_info.name : "";
327 m_info.url = m_info.url ? m_info.url : "";
328 m_info.version = m_info.version ? m_info.version : "";
331 void CPlugin::Call_OnPluginInit()
333 if (m_status != Plugin_Loaded)
338 m_status = Plugin_Running;
341 IPluginFunction *pFunction = GetFunctionByName("OnPluginInit");
347 pFunction->PushCell(m_handle);
348 pFunction->Execute(&result);
351 void CPlugin::Call_OnPluginUnload()
353 if (m_status < Plugin_Paused)
359 IPluginFunction *pFunction = GetFunctionByName("OnPluginUnload");
365 pFunction->Execute(&result);
368 bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength)
370 if (m_status != Plugin_Created)
375 m_status = Plugin_Loaded;
379 IPluginFunction *pFunction = GetFunctionByName("AskPluginLoad");
386 pFunction->PushCell(m_handle);
387 pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0);
388 pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK);
389 pFunction->PushCell(maxlength);
390 if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE)
392 m_status = Plugin_Failed;
396 if (!result || m_status != Plugin_Loaded)
398 m_status = Plugin_Failed;
405 const sp_plugin_t *CPlugin::GetPluginStructure() const
410 IPluginContext *CPlugin::GetBaseContext() const
415 sp_context_t *CPlugin::GetContext() const
420 const char *CPlugin::GetFilename() const
425 PluginType CPlugin::GetType() const
430 const sm_plugininfo_t *CPlugin::GetPublicInfo() const
435 unsigned int CPlugin::GetSerial() const
440 PluginStatus CPlugin::GetStatus() const
445 bool CPlugin::IsDebugging() const
452 return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
455 bool CPlugin::SetPauseState(bool paused)
457 if (paused && GetStatus() != Plugin_Paused)
460 } else if (!paused && GetStatus() != Plugin_Running) {
464 m_status = (paused) ? Plugin_Paused : Plugin_Running;
466 IPluginFunction *pFunction = GetFunctionByName("OnPluginPauseChange");
470 pFunction->PushCell(paused ? 1 : 0);
471 pFunction->Execute(&result);
477 IdentityToken_t *CPlugin::GetIdentity() const
482 bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength)
490 snprintf(error, maxlength, "Plugin is not runnable.");
495 if (debug && IsDebugging())
499 snprintf(error, maxlength, "Plugin is already in debug mode.");
502 } else if (!debug && !IsDebugging()) {
505 snprintf(error, maxlength, "Plugins is already in production mode.");
510 ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin);
511 if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0"))
515 snprintf(error, maxlength, "Failed to change plugin mode (JIT failure).");
520 sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err);
524 memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size);
525 new_ctx->hp = m_ctx.ctx->hp;
526 new_ctx->sp = m_ctx.ctx->sp;
527 new_ctx->frm = m_ctx.ctx->frm;
528 new_ctx->dbreak = m_ctx.ctx->dbreak;
529 new_ctx->context = m_ctx.ctx->context;
530 memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user));
532 g_pVM->FreeContext(m_ctx.ctx);
534 m_ctx.base->SetContext(new_ctx);
540 snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err);
548 bool CPlugin::IsRunnable() const
550 return (m_status <= Plugin_Paused) ? true : false;
557 CPluginManager::CPluginIterator::CPluginIterator(List<CPlugin *> *_mylist)
563 IPlugin *CPluginManager::CPluginIterator::GetPlugin()
568 bool CPluginManager::CPluginIterator::MorePlugins()
570 return (current != mylist->end());
573 void CPluginManager::CPluginIterator::NextPlugin()
578 void CPluginManager::CPluginIterator::Release()
580 g_PluginSys.ReleaseIterator(this);
583 CPluginManager::CPluginIterator::~CPluginIterator()
587 void CPluginManager::CPluginIterator::Reset()
589 current = mylist->begin();
596 CPluginManager::CPluginManager()
598 m_LoadLookup = sm_trie_create();
599 m_AllPluginsLoaded = false;
603 CPluginManager::~CPluginManager()
606 * Ignore the fact that there might be plugins in the cache.
607 * This usually means that Core is not being unloaded properly, and everything
608 * will crash anyway. YAY
610 sm_trie_destroy(m_LoadLookup);
614 void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir)
616 /* First read in the database of plugin settings */
618 unsigned int line, col;
619 m_AllPluginsLoaded = false;
620 if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay)
622 g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config);
623 const char *err_msg = g_TextParser.GetSMCErrorString(err);
626 g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg);
630 LoadPluginsFromDir(basedir, NULL);
633 void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath)
635 char base_path[PLATFORM_MAX_PATH+1];
637 /* Form the current path to start reading from */
638 if (localpath == NULL)
640 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir);
642 g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath);
645 IDirectory *dir = g_LibSys.OpenDirectory(base_path);
650 g_LibSys.GetPlatformError(error, sizeof(error));
651 g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath);
652 g_Logger.LogError("[SM] Platform returned error: %s", error);
656 while (dir->MoreFiles())
658 if (dir->IsEntryDirectory()
659 && (strcmp(dir->GetEntryName(), ".") != 0)
660 && (strcmp(dir->GetEntryName(), "..") != 0)
661 && (strcmp(dir->GetEntryName(), "disabled") != 0)
662 && (strcmp(dir->GetEntryName(), "optional") != 0))
664 char new_local[PLATFORM_MAX_PATH+1];
665 if (localpath == NULL)
667 /* If no path yet, don't add a former slash */
668 snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName());
670 g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName());
672 LoadPluginsFromDir(basedir, new_local);
673 } else if (dir->IsEntryFile()) {
674 const char *name = dir->GetEntryName();
675 size_t len = strlen(name);
677 && strcmp(&name[len-4], ".smx") == 0)
679 /* If the filename matches, load the plugin */
680 char plugin[PLATFORM_MAX_PATH+1];
681 if (localpath == NULL)
683 snprintf(plugin, sizeof(plugin), "%s", name);
685 g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name);
687 LoadAutoPlugin(plugin);
692 g_LibSys.CloseDirectory(dir);
695 //well i have discovered that gabe newell is very fat, so i wrote this comment now
696 bool CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t err_max)
699 * Does this plugin already exist?
702 if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin))
704 /* First check the type */
705 PluginType type = pPlugin->GetType();
706 if (type == PluginType_Private
707 || type == PluginType_Global)
711 /* Check to see if we should try reloading it */
712 if (pPlugin->GetStatus() == Plugin_BadLoad
713 || pPlugin->GetStatus() == Plugin_Error
714 || pPlugin->GetStatus() == Plugin_Failed)
716 UnloadPlugin(pPlugin);
720 pPlugin = CPlugin::CreatePlugin(path, error, err_max);
722 assert(pPlugin != NULL);
724 pPlugin->m_type = PluginType_MapUpdated;
726 ICompilation *co = NULL;
728 if (pPlugin->m_status == Plugin_Uncompiled)
730 co = pPlugin->StartMyCompile(g_pVM);
733 PluginSettings *pset;
734 unsigned int setcount = m_PluginInfo.GetSettingsNum();
735 for (unsigned int i=0; i<setcount; i++)
737 if ((pset=m_PluginInfo.GetSettingsIfMatch(i, path)) == NULL)
741 pPlugin->m_type = pset->type_val;
744 for (unsigned int j=0; j<pset->opts_num; j++)
746 const char *key, *val;
747 m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val);
752 if (!g_pVM->SetCompilationOption(co, key, val))
756 snprintf(error, err_max, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val);
758 pPlugin->CancelMyCompile();
759 pPlugin->m_status = Plugin_Failed;
767 /* Do the actual compiling */
770 pPlugin->FinishMyCompile(NULL, 0);
775 if (pPlugin->GetStatus() == Plugin_Created)
777 AddCoreNativesToPlugin(pPlugin);
778 pPlugin->InitIdentity();
779 if (pPlugin->Call_AskPluginLoad(error, err_max))
781 /* Autoload any modules */
782 LoadOrRequireExtensions(pPlugin, 1, error, err_max);
791 return (pPlugin->GetStatus() == Plugin_Loaded);
794 IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max)
798 if (!_LoadPlugin(&pl, path, debug, type, error, err_max))
806 /* Run second pass if we need to */
807 if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded)
809 if (!RunSecondPass(pl, error, err_max))
819 void CPluginManager::LoadAutoPlugin(const char *plugin)
822 char error[255] = "Unknown error";
824 if (!_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error)))
826 g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error);
827 pl->SetErrorState(Plugin_Failed, "%s", error);
833 void CPluginManager::AddPlugin(CPlugin *pPlugin)
835 List<IPluginsListener *>::iterator iter;
836 IPluginsListener *pListener;
838 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
841 pListener->OnPluginCreated(pPlugin);
844 m_plugins.push_back(pPlugin);
845 sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin);
848 void CPluginManager::LoadAll_SecondPass()
850 List<CPlugin *>::iterator iter;
854 for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++)
857 if (pPlugin->GetStatus() == Plugin_Loaded)
860 if (!RunSecondPass(pPlugin, error, sizeof(error)))
862 g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error);
863 pPlugin->SetErrorState(Plugin_Failed, "%s", error);
868 m_AllPluginsLoaded = true;
871 bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength)
873 /* Find any extensions this plugin needs */
882 IPluginContext *pBase = pPlugin->GetBaseContext();
883 uint32_t num = pBase->GetPubVarsNum();
886 char path[PLATFORM_MAX_PATH+1];
888 for (uint32_t i=0; i<num; i++)
890 if (pBase->GetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE)
894 if (strncmp(pubvar->name, "__ext_", 6) == 0)
896 ext = (_ext *)pubvar->offs;
897 if (!ext->required && !ext->autoload)
901 if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE)
905 if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE)
911 /* Attempt to auto-load if necessary */
914 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
915 g_Extensions.LoadAutoExtension(path);
917 } else if (pass == 2) {
918 /* Is this required? */
921 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
922 if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL)
924 pExt = g_Extensions.FindExtensionByName(name);
926 /* :TODO: should we bind to unloaded extensions?
927 * Currently the extension manager will ignore this.
929 if (!pExt || !pExt->IsRunning(NULL, 0))
933 snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file);
935 pPlugin->m_status = Plugin_Failed;
938 g_Extensions.BindChildPlugin(pExt, pPlugin);
948 bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength)
950 /* Second pass for extension requirements */
951 if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength))
956 /* Bind all extra natives */
957 g_Extensions.BindAllNativesToPlugin(pPlugin);
959 /* Find any unbound natives
960 * Right now, these are not allowed
962 IPluginContext *pContext = pPlugin->GetBaseContext();
963 uint32_t num = pContext->GetNativesNum();
965 for (unsigned int i=0; i<num; i++)
967 if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE)
971 if (native->status == SP_NATIVE_UNBOUND)
975 snprintf(error, maxlength, "Native \"%s\" was not found.", native->name);
981 /* Finish by telling all listeners */
982 List<IPluginsListener *>::iterator iter;
983 IPluginsListener *pListener;
984 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
987 pListener->OnPluginLoaded(pPlugin);
990 /* Tell this plugin to finish initializing itself */
991 pPlugin->Call_OnPluginInit();
996 void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin)
998 List<sp_nativeinfo_t *>::iterator iter;
1000 for (iter=m_natives.begin(); iter!=m_natives.end(); iter++)
1002 sp_nativeinfo_t *natives = (*iter);
1003 IPluginContext *ctx = pPlugin->GetBaseContext();
1005 /* Attempt to bind every native! */
1006 while (natives[i].func != NULL)
1008 ctx->BindNative(&natives[i++]);
1013 bool CPluginManager::UnloadPlugin(IPlugin *plugin)
1015 CPlugin *pPlugin = (CPlugin *)plugin;
1017 /* This prevents removal during insertion or anything else weird */
1018 if (m_plugins.find(pPlugin) == m_plugins.end())
1023 List<IPluginsListener *>::iterator iter;
1024 IPluginsListener *pListener;
1026 if (pPlugin->GetStatus() <= Plugin_Error)
1028 /* Notify listeners of unloading */
1029 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1031 pListener = (*iter);
1032 pListener->OnPluginUnloaded(pPlugin);
1035 pPlugin->Call_OnPluginUnload();
1038 for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++)
1040 /* Notify listeners of destruction */
1041 pListener = (*iter);
1042 pListener->OnPluginDestroyed(pPlugin);
1045 /* Remove us from the lookup table and linked list */
1046 m_plugins.remove(pPlugin);
1047 sm_trie_delete(m_LoadLookup, pPlugin->m_filename);
1049 /* Tell the plugin to delete itself */
1055 IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx)
1057 IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF];
1061 unsigned int CPluginManager::GetPluginCount()
1063 return m_plugins.size();
1066 void CPluginManager::AddPluginsListener(IPluginsListener *listener)
1068 m_listeners.push_back(listener);
1071 void CPluginManager::RemovePluginsListener(IPluginsListener *listener)
1073 m_listeners.remove(listener);
1076 IPluginIterator *CPluginManager::GetPluginIterator()
1078 if (m_iters.empty())
1080 return new CPluginIterator(&m_plugins);
1082 CPluginIterator *iter = m_iters.front();
1089 void CPluginManager::ReleaseIterator(CPluginIterator *iter)
1094 void CPluginManager::ReleaseFunctionToPool(CFunction *func)
1101 m_funcpool.push(func);
1104 CFunction *CPluginManager::GetFunctionFromPool(funcid_t f, CPlugin *plugin)
1106 if (m_funcpool.empty())
1108 return new CFunction(f, plugin);
1110 CFunction *func = m_funcpool.front();
1112 func->Set(f, plugin);
1117 bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath)
1119 /* As an optimization, we do not call strlen, but compute the length in the first pass */
1120 size_t alias_len = 0;
1121 size_t local_len = 0;
1123 const char *ptr = alias;
1124 unsigned int alias_explicit_paths = 0;
1125 unsigned int alias_path_end = 0;
1126 while (*ptr != '\0')
1128 if (*ptr == '\\' || *ptr == '/')
1130 alias_explicit_paths++;
1131 alias_path_end = alias_len;
1137 if (alias_explicit_paths && alias_path_end == alias_len - 1)
1139 /* Trailing slash is totally invalid here */
1144 unsigned int local_explicit_paths = 0;
1145 unsigned int local_path_end = 0;
1146 while (*ptr != '\0')
1148 if (*ptr == '\\' || *ptr == '/')
1150 local_explicit_paths++;
1151 local_path_end = local_len;
1157 /* If the alias has more explicit paths than the real path,
1158 * no match will be possible.
1160 if (alias_explicit_paths > local_explicit_paths)
1165 if (alias_explicit_paths)
1167 /* We need to find if the paths match now. For example, these should all match:
1169 * csdm optional/csdm
1170 * csdm/ban optional/crab/csdm/ban
1172 const char *aliasptr = alias;
1173 const char *localptr = localpath;
1177 if (*aliasptr != *localptr)
1179 /* We have to knock one path off */
1180 local_explicit_paths--;
1181 if (alias_explicit_paths > local_explicit_paths)
1183 /* Skip out if we're gonna have an impossible match */
1186 /* Eat up localptr tokens until we get a result */
1187 while (((localptr - localpath) < (int)local_path_end)
1189 && *localptr != '\\')
1193 /* Check if we hit the end of our searchable area.
1194 * This probably isn't possible because of the path
1195 * count check, but it's a good idea anyway.
1197 if ((localptr - localpath) >= (int)local_path_end)
1201 /* Consume the slash token */
1204 /* Reset the alias pointer so we can continue consuming */
1210 * This is safe because if localptr terminates early, aliasptr will too
1214 /* We should never reach the end of the string because of this check. */
1215 bool aliasend = (aliasptr - alias) > (int)alias_path_end;
1216 bool localend = (localptr - localpath) > (int)local_path_end;
1217 if (aliasend || localend)
1219 if (aliasend && localend)
1221 /* we matched, and we can break out now */
1225 /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */
1230 /* If we got here, it's safe to compare the next two tokens */
1231 if (*localptr != *aliasptr)
1242 /* If we got here, it's time to compare filenames */
1243 const char *aliasptr = alias;
1244 const char *localptr = localpath;
1246 if (alias_explicit_paths)
1248 aliasptr = &alias[alias_path_end + 1];
1251 if (local_explicit_paths)
1253 localptr = &localpath[local_path_end + 1];
1258 if (*aliasptr == '*')
1260 /* First, see if this is the last character */
1261 if (aliasptr - alias == alias_len - 1)
1263 /* If so, there's no need to match anything else */
1266 /* Otherwise, we need to search for an appropriate matching sequence in local.
1267 * Note that we only need to search up to the next asterisk.
1271 const char *local_orig = localptr;
1275 while (*aliasptr != '\0' && *aliasptr != '*')
1277 /* Since aliasptr is never '\0', localptr hitting the end will fail */
1278 if (*aliasptr != *localptr)
1288 /* If we didn't get a match, we need to advance the search stream.
1289 * This will let us skip tokens while still searching for another match.
1291 localptr = ++local_orig;
1292 /* Make sure we don't go out of bounds */
1293 if (*localptr == '\0')
1304 /* If we got a match, move on to the next token */
1307 } else if (*aliasptr == '\0') {
1308 if (*localptr == '\0'
1310 strcmp(localptr, ".smx") == 0)
1316 } else if (*aliasptr != *localptr) {
1326 bool CPluginManager::IsLateLoadTime() const
1328 return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading());
1331 void CPluginManager::OnSourceModAllInitialized()
1333 m_MyIdent = g_ShareSys.CreateCoreIdentity();
1336 g_HandleSys.InitAccessDefaults(NULL, &sec);
1338 sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY;
1339 sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY;
1341 g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL);
1342 g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN");
1344 g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this);
1347 void CPluginManager::OnSourceModShutdown()
1349 g_RootMenu.RemoveRootConsoleCommand("plugins", this);
1351 List<CPlugin *>::iterator iter;
1352 while ( (iter = m_plugins.begin()) != m_plugins.end() )
1354 UnloadPlugin((*iter));
1357 g_HandleSys.RemoveType(g_PluginType, m_MyIdent);
1358 g_ShareSys.DestroyIdentType(g_PluginIdent);
1359 g_ShareSys.DestroyIdentity(m_MyIdent);
1362 void CPluginManager::OnHandleDestroy(HandleType_t type, void *object)
1364 /* We don't care about the internal object, actually */
1367 void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives)
1369 m_natives.push_back(natives);
1372 IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err)
1380 sec.pIdentity = m_MyIdent;
1382 if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None)
1395 CPlugin *CPluginManager::GetPluginByOrder(int num)
1397 if (num < 1 || num > (int)GetPluginCount())
1405 IPluginIterator *iter = GetPluginIterator();
1406 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++) {}
1408 pl = (CPlugin *)(iter->GetPlugin());
1414 const char *CPluginManager::GetStatusText(PluginStatus st)
1418 case Plugin_Running:
1424 case Plugin_Uncompiled:
1425 return "Uncompiled";
1426 case Plugin_BadLoad:
1436 #define IS_STR_FILLED(var) (var[0] != '\0')
1437 void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount)
1441 const char *cmd = g_RootMenu.GetArgument(2);
1442 if (strcmp(cmd, "list") == 0)
1445 unsigned int id = 1;
1446 int plnum = GetPluginCount();
1450 g_RootMenu.ConsolePrint("[SM] No plugins loaded");
1453 g_RootMenu.ConsolePrint("[SM] Displaying %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : "");
1456 IPluginIterator *iter = GetPluginIterator();
1457 for (; iter->MorePlugins(); iter->NextPlugin(), id++)
1459 IPlugin *pl = iter->GetPlugin();
1460 assert(pl->GetStatus() != Plugin_Created);
1462 const sm_plugininfo_t *info = pl->GetPublicInfo();
1464 len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus()));
1465 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1466 if (IS_STR_FILLED(info->version))
1468 len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version);
1470 if (IS_STR_FILLED(info->author))
1472 UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author);
1474 g_RootMenu.ConsolePrint("%s", buffer);
1479 } else if (strcmp(cmd, "load") == 0) {
1482 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load <file>");
1487 const char *filename = g_RootMenu.GetArgument(3);
1488 IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error));
1492 g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename);
1494 g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error);
1498 } else if (strcmp(cmd, "unload") == 0) {
1501 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>");
1506 int num = atoi(g_RootMenu.GetArgument(3));
1507 if (num < 1 || num > (int)GetPluginCount())
1509 g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num);
1513 IPluginIterator *iter = GetPluginIterator();
1514 for (; iter->MorePlugins() && id<num; iter->NextPlugin(), id++)
1516 IPlugin *pl = iter->GetPlugin();
1518 char name[PLATFORM_MAX_PATH+1];
1519 const sm_plugininfo_t *info = pl->GetPublicInfo();
1520 strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename());
1522 if (UnloadPlugin(pl))
1524 g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name);
1526 g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name);
1531 } else if (strcmp(cmd, "info") == 0) {
1534 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>");
1539 int num = atoi(g_RootMenu.GetArgument(3));
1540 if (num < 1 || num > (int)GetPluginCount())
1542 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1546 CPlugin *pl = GetPluginByOrder(num);
1547 const sm_plugininfo_t *info = pl->GetPublicInfo();
1549 g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename());
1550 if (pl->GetStatus() <= Plugin_Error)
1552 if (IS_STR_FILLED(info->name))
1554 g_RootMenu.ConsolePrint(" Title: %s", info->name);
1556 if (IS_STR_FILLED(info->version))
1558 g_RootMenu.ConsolePrint(" Version: %s", info->version);
1560 if (IS_STR_FILLED(info->url))
1562 g_RootMenu.ConsolePrint(" URL: %s", info->url);
1564 if (IS_STR_FILLED(info->author))
1566 g_RootMenu.ConsolePrint(" Author: %s", info->author);
1568 if (IS_STR_FILLED(info->version))
1570 g_RootMenu.ConsolePrint(" Version: %s", info->version);
1572 if (IS_STR_FILLED(info->description))
1574 g_RootMenu.ConsolePrint(" Description: %s", info->description);
1576 g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "yes" : "no");
1577 g_RootMenu.ConsolePrint(" Paused: %s", pl->GetStatus() == Plugin_Running ? "yes" : "no");
1579 g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg);
1580 g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")",
1581 info->name ? info->name : "<none>",
1582 info->version ? info->version : "<none>");
1583 if (IS_STR_FILLED(info->url))
1585 g_RootMenu.ConsolePrint(" File URL: %s", info->url);
1590 } else if (strcmp(cmd, "debug") == 0) {
1593 g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]");
1597 int num = atoi(g_RootMenu.GetArgument(3));
1598 if (num < 1 || num > (int)GetPluginCount())
1600 g_RootMenu.ConsolePrint("[SM] Plugin index not found.");
1605 const char *mode = g_RootMenu.GetArgument(4);
1606 if ((res=strcmp("on", mode)) && strcmp("off", mode))
1608 g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\"");
1620 CPlugin *pl = GetPluginByOrder(num);
1621 if (debug && pl->IsDebugging())
1623 g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode.");
1625 } else if (!debug && !pl->IsDebugging()) {
1626 g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin.");
1631 if (pl->ToggleDebugMode(debug, error, sizeof(error)))
1633 g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename());
1636 g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename());
1637 g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error);
1643 /* Draw the main menu */
1644 g_RootMenu.ConsolePrint("SourceMod Plugins Menu:");
1645 g_RootMenu.DrawGenericOption("list", "Show loaded plugins");
1646 g_RootMenu.DrawGenericOption("load", "Load a plugin");
1647 g_RootMenu.DrawGenericOption("unload", "Unload a plugin");
1648 g_RootMenu.DrawGenericOption("info", "Information about a plugin");
1649 g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin");