dvander@370: /** ds@653: * vim: set ts=4 : dvander@370: * =============================================================== dvander@370: * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. dvander@370: * =============================================================== dvander@370: * dvander@370: * This file is not open source and may not be copied without explicit dvander@370: * written permission of AlliedModders LLC. This file may not be redistributed dvander@370: * in whole or significant part. dvander@370: * For information, see LICENSE.txt or http://www.sourcemod.net/license.php dvander@370: * dvander@370: * Version: $Id$ dvander@370: */ dvander@370: dvander@173: #include dvander@173: #include "PluginSys.h" dvander@229: #include "ShareSys.h" dvander@190: #include "LibrarySys.h" dvander@220: #include "HandleSys.h" ds@611: #include "ForwardSys.h" dvander@197: #include "sourcemm_api.h" dvander@209: #include "sourcemod.h" dvander@591: #include "TextParsers.h" dvander@592: #include "Logger.h" dvander@298: #include "ExtensionSys.h" dvander@309: #include "sm_srvcmds.h" dvander@309: #include "sm_stringutil.h" dvander@173: dvander@211: CPluginManager g_PluginSys; dvander@220: HandleType_t g_PluginType = 0; dvander@229: IdentityType_t g_PluginIdent = 0; dvander@173: dvander@209: CPlugin::CPlugin(const char *file) dvander@209: { dvander@209: static int MySerial = 0; dvander@209: dvander@209: m_type = PluginType_Private; dvander@209: m_status = Plugin_Uncompiled; dvander@209: m_serial = ++MySerial; dvander@209: m_plugin = NULL; dvander@209: m_errormsg[256] = '\0'; dvander@209: snprintf(m_filename, sizeof(m_filename), "%s", file); dvander@229: m_handle = 0; dvander@229: m_ident = NULL; dvander@486: m_pProps = sm_trie_create(); dvander@605: m_FakeNativesMissing = false; dvander@209: } dvander@209: dvander@209: CPlugin::~CPlugin() dvander@173: { dvander@298: if (m_handle) dvander@298: { dvander@298: HandleSecurity sec; dvander@298: sec.pOwner = g_PluginSys.GetIdentity(); dvander@298: sec.pIdentity = sec.pOwner; dvander@298: dvander@298: g_HandleSys.FreeHandle(m_handle, &sec); dvander@298: g_ShareSys.DestroyIdentity(m_ident); dvander@298: } dvander@298: dvander@209: if (m_ctx.base) dvander@209: { dvander@262: delete m_ctx.base; dvander@209: m_ctx.base = NULL; dvander@209: } dvander@209: if (m_ctx.ctx) dvander@209: { dvander@209: m_ctx.vm->FreeContext(m_ctx.ctx); dvander@209: m_ctx.ctx = NULL; dvander@209: } dvander@209: if (m_ctx.co) dvander@209: { dvander@209: m_ctx.vm->AbortCompilation(m_ctx.co); dvander@209: m_ctx.co = NULL; dvander@209: } dvander@209: dvander@222: if (m_plugin) dvander@222: { dvander@222: g_pSourcePawn->FreeFromMemory(m_plugin); dvander@222: m_plugin = NULL; dvander@222: } faluco@641: if (m_pProps) dvander@486: { dvander@486: sm_trie_destroy(m_pProps); dvander@486: } dvander@229: } dvander@229: dvander@229: void CPlugin::InitIdentity() dvander@229: { dvander@229: if (!m_handle) dvander@229: { dvander@229: m_ident = g_ShareSys.CreateIdentity(g_PluginIdent); dvander@262: m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL); dvander@262: m_ctx.base->SetIdentity(m_ident); dvander@229: } dvander@209: } dvander@209: dvander@536: Handle_t CPlugin::GetMyHandle() dvander@536: { dvander@536: return m_handle; dvander@536: } dvander@536: dvander@209: CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength) dvander@209: { ds@652: char fullpath[PLATFORM_MAX_PATH]; dvander@317: g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file); dvander@209: FILE *fp = fopen(fullpath, "rb"); dvander@173: dvander@298: CPlugin *pPlugin = new CPlugin(file); dvander@298: dvander@173: if (!fp) dvander@173: { dvander@209: if (error) dvander@209: { dvander@209: snprintf(error, maxlength, "Unable to open file"); dvander@209: } dvander@298: pPlugin->m_status = Plugin_BadLoad; dvander@298: return pPlugin; dvander@173: } dvander@173: dvander@173: int err; dvander@173: sp_plugin_t *pl = g_pSourcePawn->LoadFromFilePointer(fp, &err); dvander@173: if (pl == NULL) dvander@173: { dvander@209: fclose(fp); dvander@209: if (error) dvander@209: { dvander@209: snprintf(error, maxlength, "Error %d while parsing plugin", err); dvander@209: } dvander@298: pPlugin->m_status = Plugin_BadLoad; dvander@298: return pPlugin; dvander@173: } dvander@173: dvander@173: fclose(fp); dvander@173: dvander@209: pPlugin->m_plugin = pl; dvander@298: dvander@209: return pPlugin; dvander@209: } dvander@209: dvander@486: bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */) dvander@486: { dvander@486: bool exists = sm_trie_retrieve(m_pProps, prop, ptr); dvander@486: dvander@486: if (exists && remove) dvander@486: { dvander@486: sm_trie_delete(m_pProps, prop); dvander@486: } dvander@486: dvander@486: return exists; dvander@486: } dvander@486: dvander@486: bool CPlugin::SetProperty(const char *prop, void *ptr) dvander@486: { dvander@486: return sm_trie_insert(m_pProps, prop, ptr); dvander@486: } dvander@486: dvander@209: ICompilation *CPlugin::StartMyCompile(IVirtualMachine *vm) dvander@209: { dvander@209: if (!m_plugin) dvander@173: { dvander@209: return NULL; dvander@173: } dvander@173: dvander@209: /* :NOTICE: We will eventually need to change these natives dvander@209: * for swapping in new contexts dvander@209: */ dvander@209: if (m_ctx.co || m_ctx.ctx) dvander@173: { dvander@173: return NULL; dvander@173: } dvander@173: dvander@209: m_status = Plugin_Uncompiled; dvander@173: dvander@209: m_ctx.vm = vm; dvander@209: m_ctx.co = vm->StartCompilation(m_plugin); dvander@209: dvander@209: return m_ctx.co; dvander@209: } dvander@173: dvander@209: void CPlugin::CancelMyCompile() dvander@209: { dvander@209: if (!m_ctx.co) dvander@209: { dvander@209: return; dvander@209: } dvander@176: dvander@209: m_ctx.vm->AbortCompilation(m_ctx.co); dvander@209: m_ctx.co = NULL; dvander@209: m_ctx.vm = NULL; dvander@209: } dvander@209: dvander@209: bool CPlugin::FinishMyCompile(char *error, size_t maxlength) dvander@209: { dvander@209: if (!m_ctx.co) dvander@176: { dvander@209: return false; dvander@176: } dvander@176: dvander@209: int err; dvander@209: m_ctx.ctx = m_ctx.vm->CompileToContext(m_ctx.co, &err); dvander@209: if (!m_ctx.ctx) dvander@176: { dvander@209: memset(&m_ctx, 0, sizeof(m_ctx)); dvander@298: if (error) dvander@209: { dvander@209: snprintf(error, maxlength, "Failed to compile (error %d)", err); dvander@209: } dvander@209: return false; dvander@176: } dvander@176: dvander@262: m_ctx.base = new BaseContext(m_ctx.ctx); dvander@209: m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this; dvander@209: dvander@211: m_status = Plugin_Created; faluco@216: m_ctx.co = NULL; dvander@211: dvander@209: UpdateInfo(); dvander@209: dvander@209: return true; dvander@209: } dvander@209: dvander@209: void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...) dvander@209: { dvander@605: PluginStatus old_status = m_status; dvander@209: m_status = status; dvander@209: dvander@605: if (old_status == Plugin_Running) dvander@605: { dvander@605: /* Tell everyone we're now paused */ dvander@605: g_PluginSys._SetPauseState(this, true); dvander@605: } dvander@605: dvander@209: va_list ap; dvander@209: va_start(ap, error_fmt); dvander@209: vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap); dvander@209: va_end(ap); dvander@173: dvander@525: if (m_ctx.ctx) dvander@176: { dvander@525: m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED; dvander@176: } dvander@176: } dvander@176: dvander@173: void CPlugin::UpdateInfo() dvander@173: { dvander@173: /* Now grab the info */ dvander@173: uint32_t idx; dvander@173: IPluginContext *base = GetBaseContext(); dvander@173: int err = base->FindPubvarByName("myinfo", &idx); dvander@173: dvander@413: memset(&m_info, 0, sizeof(m_info)); dvander@413: dvander@173: if (err == SP_ERROR_NONE) dvander@173: { dvander@173: struct sm_plugininfo_c_t dvander@173: { dvander@173: cell_t name; dvander@173: cell_t description; dvander@173: cell_t author; dvander@173: cell_t version; dvander@173: cell_t url; dvander@173: }; dvander@173: sm_plugininfo_c_t *cinfo; dvander@173: cell_t local_addr; dvander@173: dvander@173: base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo); dvander@173: base->LocalToString(cinfo->name, (char **)&m_info.name); dvander@173: base->LocalToString(cinfo->description, (char **)&m_info.description); dvander@173: base->LocalToString(cinfo->author, (char **)&m_info.author); dvander@173: base->LocalToString(cinfo->url, (char **)&m_info.url); dvander@173: base->LocalToString(cinfo->version, (char **)&m_info.version); dvander@173: } dvander@173: dvander@173: m_info.author = m_info.author ? m_info.author : ""; dvander@173: m_info.description = m_info.description ? m_info.description : ""; dvander@173: m_info.name = m_info.name ? m_info.name : ""; dvander@173: m_info.url = m_info.url ? m_info.url : ""; dvander@173: m_info.version = m_info.version ? m_info.version : ""; dvander@173: } dvander@173: ds@426: void CPlugin::Call_OnPluginStart() dvander@211: { dvander@211: if (m_status != Plugin_Loaded) dvander@211: { dvander@211: return; dvander@211: } dvander@211: dvander@211: m_status = Plugin_Running; dvander@211: dvander@211: cell_t result; dvander@340: IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginStart"); dvander@211: if (!pFunction) dvander@211: { dvander@211: return; dvander@211: } dvander@211: dvander@282: pFunction->Execute(&result); dvander@282: } dvander@282: ds@426: void CPlugin::Call_OnPluginEnd() dvander@282: { faluco@736: if (m_status > Plugin_Paused) dvander@211: { dvander@282: return; dvander@211: } dvander@282: dvander@282: cell_t result; dvander@340: IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginEnd"); dvander@282: if (!pFunction) dvander@282: { dvander@282: return; dvander@282: } dvander@282: dvander@282: pFunction->Execute(&result); dvander@211: } dvander@211: dvander@211: bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) dvander@211: { dvander@211: if (m_status != Plugin_Created) dvander@211: { dvander@211: return false; dvander@211: } dvander@211: faluco@216: m_status = Plugin_Loaded; faluco@216: dvander@211: int err; dvander@211: cell_t result; dvander@327: IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("AskPluginLoad"); dvander@211: dvander@211: if (!pFunction) dvander@211: { dvander@211: return true; dvander@211: } dvander@211: dvander@222: pFunction->PushCell(m_handle); dvander@211: pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0); dvander@211: pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK); dvander@211: pFunction->PushCell(maxlength); dvander@211: if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) dvander@211: { dvander@211: return false; dvander@211: } dvander@211: dvander@282: if (!result || m_status != Plugin_Loaded) dvander@211: { dvander@211: return false; dvander@211: } dvander@211: dvander@211: return true; dvander@211: } dvander@211: dvander@615: const sp_plugin_t *CPlugin::GetPluginStructure() dvander@173: { dvander@173: return m_plugin; dvander@173: } dvander@173: dvander@615: IPluginContext *CPlugin::GetBaseContext() dvander@173: { dvander@209: return m_ctx.base; dvander@173: } dvander@173: dvander@615: sp_context_t *CPlugin::GetContext() dvander@173: { dvander@209: return m_ctx.ctx; dvander@173: } dvander@173: dvander@615: const char *CPlugin::GetFilename() dvander@173: { dvander@173: return m_filename; dvander@173: } dvander@173: dvander@615: PluginType CPlugin::GetType() dvander@173: { dvander@174: return m_type; dvander@173: } dvander@173: dvander@615: const sm_plugininfo_t *CPlugin::GetPublicInfo() dvander@173: { dvander@173: return &m_info; dvander@173: } dvander@173: dvander@615: unsigned int CPlugin::GetSerial() dvander@173: { dvander@173: return m_serial; dvander@173: } dvander@173: dvander@615: PluginStatus CPlugin::GetStatus() dvander@173: { dvander@173: return m_status; dvander@173: } dvander@173: dvander@615: bool CPlugin::IsDebugging() dvander@173: { dvander@209: if (!m_ctx.ctx) dvander@209: { dvander@209: return false; dvander@209: } dvander@209: dvander@209: return ((m_ctx.ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG); dvander@173: } dvander@173: dvander@173: bool CPlugin::SetPauseState(bool paused) dvander@173: { dvander@173: if (paused && GetStatus() != Plugin_Paused) dvander@173: { dvander@173: return false; dvander@173: } else if (!paused && GetStatus() != Plugin_Running) { dvander@173: return false; dvander@173: } dvander@282: dvander@327: IPluginFunction *pFunction = m_ctx.base->GetFunctionByName("OnPluginPauseChange"); dvander@282: if (pFunction) dvander@282: { dvander@282: cell_t result; dvander@282: pFunction->PushCell(paused ? 1 : 0); dvander@282: pFunction->Execute(&result); dvander@282: } dvander@173: dvander@525: if (paused) dvander@525: { dvander@525: m_status = Plugin_Paused; dvander@525: m_ctx.ctx->flags |= SPFLAG_PLUGIN_PAUSED; dvander@525: } else { dvander@525: m_status = Plugin_Running; dvander@525: m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED; dvander@525: } dvander@525: faluco@430: g_PluginSys._SetPauseState(this, paused); faluco@430: dvander@173: return true; dvander@173: } dvander@174: dvander@615: IdentityToken_t *CPlugin::GetIdentity() dvander@220: { dvander@229: return m_ident; dvander@220: } dvander@220: dvander@309: bool CPlugin::ToggleDebugMode(bool debug, char *error, size_t maxlength) faluco@290: { faluco@290: int err; faluco@290: faluco@290: if (!IsRunnable()) faluco@290: { dvander@309: if (error) dvander@309: { dvander@309: snprintf(error, maxlength, "Plugin is not runnable."); dvander@309: } faluco@290: return false; faluco@290: } faluco@290: dvander@309: if (debug && IsDebugging()) faluco@290: { dvander@309: if (error) dvander@309: { dvander@309: snprintf(error, maxlength, "Plugin is already in debug mode."); dvander@309: } dvander@309: return false; dvander@309: } else if (!debug && !IsDebugging()) { dvander@309: if (error) dvander@309: { dvander@309: snprintf(error, maxlength, "Plugins is already in production mode."); dvander@309: } faluco@290: return false; faluco@290: } dvander@309: faluco@290: ICompilation *co = g_pVM->StartCompilation(m_ctx.ctx->plugin); faluco@290: if (!g_pVM->SetCompilationOption(co, "debug", (debug) ? "1" : "0")) faluco@290: { dvander@309: if (error) dvander@309: { dvander@309: snprintf(error, maxlength, "Failed to change plugin mode (JIT failure)."); dvander@309: } faluco@290: return false; faluco@290: } dvander@309: faluco@290: sp_context_t *new_ctx = g_pVM->CompileToContext(co, &err); faluco@290: dvander@309: if (new_ctx) dvander@309: { dvander@309: memcpy(new_ctx->memory, m_ctx.ctx->memory, m_ctx.ctx->mem_size); dvander@309: new_ctx->hp = m_ctx.ctx->hp; dvander@309: new_ctx->sp = m_ctx.ctx->sp; dvander@309: new_ctx->frm = m_ctx.ctx->frm; dvander@309: new_ctx->dbreak = m_ctx.ctx->dbreak; dvander@309: new_ctx->context = m_ctx.ctx->context; dvander@309: memcpy(new_ctx->user, m_ctx.ctx->user, sizeof(m_ctx.ctx->user)); faluco@290: dvander@693: uint32_t nativeCount = m_plugin->info.natives_num; dvander@693: for (uint32_t i=0; inatives[i].pfn = m_ctx.ctx->natives[i].pfn; dvander@693: new_ctx->natives[i].status = m_ctx.ctx->natives[i].status; dvander@693: } dvander@693: dvander@309: g_pVM->FreeContext(m_ctx.ctx); dvander@309: m_ctx.ctx = new_ctx; dvander@309: m_ctx.base->SetContext(new_ctx); faluco@290: dvander@309: UpdateInfo(); dvander@309: } else { dvander@309: if (error) dvander@309: { dvander@309: snprintf(error, maxlength, "Failed to recompile plugin (JIT error %d).", err); dvander@309: } dvander@309: return false; dvander@309: } faluco@290: faluco@290: return true; faluco@290: } faluco@290: dvander@615: bool CPlugin::IsRunnable() faluco@290: { faluco@290: return (m_status <= Plugin_Paused) ? true : false; faluco@290: } faluco@290: faluco@333: time_t CPlugin::GetFileTimeStamp() faluco@333: { ds@652: char path[PLATFORM_MAX_PATH]; faluco@333: g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename); faluco@333: #ifdef PLATFORM_WINDOWS faluco@333: struct _stat s; faluco@333: if (_stat(path, &s) != 0) dvander@351: #elif defined PLATFORM_POSIX faluco@333: struct stat s; faluco@333: if (stat(path, &s) != 0) faluco@333: #endif faluco@333: { faluco@333: return 0; faluco@333: } else { faluco@333: return s.st_mtime; faluco@333: } faluco@333: } faluco@333: dvander@615: time_t CPlugin::GetTimeStamp() faluco@333: { faluco@333: return m_LastAccess; faluco@333: } faluco@333: faluco@333: void CPlugin::SetTimeStamp(time_t t) faluco@333: { faluco@333: m_LastAccess = t; faluco@333: } faluco@333: faluco@389: void CPlugin::AddLangFile(unsigned int index) faluco@389: { faluco@389: m_PhraseFiles.push_back(index); faluco@389: } faluco@389: dvander@615: size_t CPlugin::GetLangFileCount() faluco@389: { faluco@389: return m_PhraseFiles.size(); faluco@389: } faluco@389: dvander@615: unsigned int CPlugin::GetLangFileByIndex(unsigned int index) faluco@389: { faluco@389: return m_PhraseFiles.at(index); faluco@389: } faluco@389: dvander@605: void CPlugin::DependencyDropped(CPlugin *pOwner) dvander@605: { dvander@605: if (!m_ctx.ctx) dvander@605: { dvander@605: return; dvander@605: } dvander@605: dvander@605: List::iterator iter; dvander@605: FakeNative *pNative; dvander@605: sp_native_t *native; dvander@605: uint32_t idx; dvander@605: unsigned int unbound = 0; dvander@605: dvander@605: for (iter = pOwner->m_fakeNatives.begin(); dvander@605: iter != pOwner->m_fakeNatives.end(); dvander@605: iter++) dvander@605: { dvander@605: pNative = (*iter); dvander@605: /* Find this native! */ dvander@605: if (m_ctx.base->FindNativeByName(pNative->name.c_str(), &idx) != SP_ERROR_NONE) dvander@605: { dvander@605: continue; dvander@605: } dvander@605: /* Unbind it */ dvander@605: m_ctx.base->GetNativeByIndex(idx, &native); dvander@605: native->pfn = NULL; dvander@605: native->status = SP_NATIVE_UNBOUND; dvander@605: unbound++; dvander@605: } dvander@605: dvander@605: /* :IDEA: in the future, add native trapping? */ dvander@605: if (unbound) dvander@605: { dvander@605: m_FakeNativesMissing = true; dvander@605: SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename()); dvander@605: } dvander@605: dvander@605: m_dependsOn.remove(pOwner); dvander@605: } dvander@605: dvander@174: /******************* dvander@174: * PLUGIN ITERATOR * dvander@174: *******************/ dvander@190: dvander@213: CPluginManager::CPluginIterator::CPluginIterator(List *_mylist) dvander@174: { dvander@174: mylist = _mylist; faluco@216: Reset(); dvander@174: } dvander@174: dvander@174: IPlugin *CPluginManager::CPluginIterator::GetPlugin() dvander@174: { dvander@174: return (*current); dvander@174: } dvander@174: dvander@174: bool CPluginManager::CPluginIterator::MorePlugins() dvander@174: { dvander@174: return (current != mylist->end()); dvander@174: } dvander@174: dvander@174: void CPluginManager::CPluginIterator::NextPlugin() dvander@174: { dvander@174: current++; dvander@174: } dvander@174: dvander@174: void CPluginManager::CPluginIterator::Release() dvander@174: { dvander@211: g_PluginSys.ReleaseIterator(this); dvander@174: } dvander@174: dvander@174: CPluginManager::CPluginIterator::~CPluginIterator() dvander@174: { dvander@174: } dvander@174: dvander@174: void CPluginManager::CPluginIterator::Reset() dvander@174: { dvander@174: current = mylist->begin(); dvander@174: } dvander@174: dvander@174: /****************** dvander@174: * PLUGIN MANAGER * dvander@174: ******************/ dvander@174: dvander@211: CPluginManager::CPluginManager() dvander@211: { dvander@211: m_LoadLookup = sm_trie_create(); dvander@211: m_AllPluginsLoaded = false; dvander@229: m_MyIdent = NULL; dvander@601: m_pNativeLookup = sm_trie_create(); dvander@211: } dvander@211: dvander@211: CPluginManager::~CPluginManager() dvander@211: { dvander@282: /* :NOTICE: dvander@282: * Ignore the fact that there might be plugins in the cache. dvander@282: * This usually means that Core is not being unloaded properly, and everything dvander@282: * will crash anyway. YAY dvander@282: */ dvander@211: sm_trie_destroy(m_LoadLookup); dvander@601: sm_trie_destroy(m_pNativeLookup); faluco@641: faluco@641: CStack::iterator iter; faluco@641: for (iter=m_iters.begin(); iter!=m_iters.end(); iter++) faluco@641: { faluco@641: delete (*iter); faluco@641: } faluco@641: m_iters.popall(); dvander@211: } dvander@211: dvander@213: void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir) dvander@190: { dvander@203: /* First read in the database of plugin settings */ dvander@203: SMCParseError err; dvander@203: unsigned int line, col; faluco@216: m_AllPluginsLoaded = false; dvander@273: if ((err=g_TextParser.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay) dvander@203: { dvander@305: g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config); dvander@282: const char *err_msg = g_TextParser.GetSMCErrorString(err); dvander@282: if (err_msg) dvander@282: { dvander@305: g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg); dvander@282: } dvander@203: } dvander@203: dvander@209: LoadPluginsFromDir(basedir, NULL); dvander@209: } dvander@203: dvander@209: void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath) dvander@209: { ds@652: char base_path[PLATFORM_MAX_PATH]; dvander@209: dvander@209: /* Form the current path to start reading from */ dvander@209: if (localpath == NULL) dvander@209: { dvander@209: g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir); dvander@209: } else { dvander@209: g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath); dvander@209: } dvander@209: dvander@209: IDirectory *dir = g_LibSys.OpenDirectory(base_path); dvander@209: dvander@209: if (!dir) dvander@209: { dvander@282: char error[256]; dvander@282: g_LibSys.GetPlatformError(error, sizeof(error)); dvander@305: g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath); dvander@305: g_Logger.LogError("[SM] Platform returned error: %s", error); dvander@209: return; dvander@209: } dvander@209: dvander@190: while (dir->MoreFiles()) dvander@190: { dvander@209: if (dir->IsEntryDirectory() dvander@209: && (strcmp(dir->GetEntryName(), ".") != 0) dvander@209: && (strcmp(dir->GetEntryName(), "..") != 0) dvander@209: && (strcmp(dir->GetEntryName(), "disabled") != 0) dvander@209: && (strcmp(dir->GetEntryName(), "optional") != 0)) dvander@190: { ds@652: char new_local[PLATFORM_MAX_PATH]; dvander@209: if (localpath == NULL) dvander@209: { dvander@209: /* If no path yet, don't add a former slash */ dvander@209: snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName()); dvander@209: } else { dvander@209: g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName()); dvander@209: } dvander@209: LoadPluginsFromDir(basedir, new_local); dvander@209: } else if (dir->IsEntryFile()) { dvander@209: const char *name = dir->GetEntryName(); dvander@209: size_t len = strlen(name); dvander@211: if (len >= 4 dvander@211: && strcmp(&name[len-4], ".smx") == 0) dvander@209: { dvander@211: /* If the filename matches, load the plugin */ ds@652: char plugin[PLATFORM_MAX_PATH]; dvander@211: if (localpath == NULL) dvander@211: { dvander@211: snprintf(plugin, sizeof(plugin), "%s", name); dvander@211: } else { dvander@211: g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name); dvander@211: } dvander@211: LoadAutoPlugin(plugin); dvander@209: } dvander@209: } dvander@209: dir->NextEntry(); dvander@209: } dvander@209: g_LibSys.CloseDirectory(dir); dvander@209: } dvander@209: dvander@211: //well i have discovered that gabe newell is very fat, so i wrote this comment now ds@755: LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength) dvander@209: { dvander@211: /** dvander@211: * Does this plugin already exist? dvander@211: */ dvander@211: CPlugin *pPlugin; dvander@298: if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin)) dvander@211: { dvander@211: /* Check to see if we should try reloading it */ dvander@211: if (pPlugin->GetStatus() == Plugin_BadLoad dvander@282: || pPlugin->GetStatus() == Plugin_Error dvander@282: || pPlugin->GetStatus() == Plugin_Failed) dvander@211: { dvander@211: UnloadPlugin(pPlugin); faluco@383: } else { faluco@383: if (_plugin) faluco@383: { faluco@383: *_plugin = pPlugin; faluco@383: } faluco@383: return LoadRes_AlreadyLoaded; dvander@211: } dvander@211: } dvander@211: ds@755: pPlugin = CPlugin::CreatePlugin(path, error, maxlength); dvander@209: dvander@209: assert(pPlugin != NULL); dvander@209: dvander@209: pPlugin->m_type = PluginType_MapUpdated; dvander@209: dvander@209: ICompilation *co = NULL; dvander@209: dvander@209: if (pPlugin->m_status == Plugin_Uncompiled) dvander@209: { dvander@209: co = pPlugin->StartMyCompile(g_pVM); dvander@209: } dvander@209: dvander@209: PluginSettings *pset; dvander@209: unsigned int setcount = m_PluginInfo.GetSettingsNum(); dvander@209: for (unsigned int i=0; im_type = pset->type_val; dvander@209: if (co) dvander@209: { dvander@209: for (unsigned int j=0; jopts_num; j++) dvander@209: { dvander@209: const char *key, *val; dvander@209: m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val); dvander@209: if (!key || !val) dvander@209: { dvander@209: continue; dvander@209: } dvander@209: if (!g_pVM->SetCompilationOption(co, key, val)) dvander@209: { dvander@298: if (error) dvander@298: { ds@755: snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val); dvander@298: } dvander@209: pPlugin->CancelMyCompile(); dvander@209: co = NULL; dvander@209: break; dvander@209: } dvander@209: } dvander@190: } dvander@190: } dvander@209: dvander@298: /* Do the actual compiling */ dvander@209: if (co) dvander@209: { dvander@209: pPlugin->FinishMyCompile(NULL, 0); dvander@209: co = NULL; dvander@209: } dvander@209: dvander@298: /* Get the status */ dvander@211: if (pPlugin->GetStatus() == Plugin_Created) dvander@211: { dvander@211: AddCoreNativesToPlugin(pPlugin); dvander@229: pPlugin->InitIdentity(); ds@755: if (pPlugin->Call_AskPluginLoad(error, maxlength)) dvander@315: { dvander@315: /* Autoload any modules */ ds@755: LoadOrRequireExtensions(pPlugin, 1, error, maxlength); dvander@315: } dvander@211: } dvander@211: faluco@333: /* Save the time stamp */ faluco@333: time_t t = pPlugin->GetFileTimeStamp(); faluco@333: pPlugin->SetTimeStamp(t); faluco@333: dvander@298: if (_plugin) dvander@298: { dvander@298: *_plugin = pPlugin; dvander@298: } dvander@298: faluco@383: return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure; dvander@190: } dvander@190: ds@755: IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded) dvander@174: { dvander@298: CPlugin *pl; faluco@383: LoadRes res; dvander@211: faluco@383: *wasloaded = false; ds@755: if ((res=_LoadPlugin(&pl, path, debug, type, error, maxlength)) == LoadRes_Failure) dvander@211: { dvander@298: delete pl; dvander@174: return NULL; dvander@174: } dvander@174: faluco@383: if (res == LoadRes_AlreadyLoaded) faluco@383: { faluco@383: *wasloaded = true; faluco@383: return pl; faluco@383: } faluco@383: dvander@298: AddPlugin(pl); dvander@298: dvander@315: /* Run second pass if we need to */ dvander@315: if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded) dvander@315: { ds@755: if (!RunSecondPass(pl, error, maxlength)) dvander@315: { dvander@315: UnloadPlugin(pl); dvander@315: return NULL; dvander@315: } dvander@315: } dvander@315: dvander@298: return pl; dvander@298: } dvander@298: dvander@298: void CPluginManager::LoadAutoPlugin(const char *plugin) dvander@298: { dvander@298: CPlugin *pl; faluco@383: LoadRes res; dvander@298: char error[255] = "Unknown error"; dvander@298: faluco@383: if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure) dvander@209: { dvander@305: g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error); dvander@298: pl->SetErrorState(Plugin_Failed, "%s", error); dvander@209: } dvander@209: faluco@383: if (res == LoadRes_Successful) faluco@383: { faluco@383: AddPlugin(pl); faluco@383: } dvander@209: } dvander@209: dvander@211: void CPluginManager::AddPlugin(CPlugin *pPlugin) dvander@209: { dvander@174: List::iterator iter; dvander@174: IPluginsListener *pListener; dvander@298: dvander@174: for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) dvander@174: { dvander@174: pListener = (*iter); dvander@174: pListener->OnPluginCreated(pPlugin); dvander@174: } dvander@174: dvander@298: m_plugins.push_back(pPlugin); dvander@298: sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin); dvander@211: } dvander@211: dvander@213: void CPluginManager::LoadAll_SecondPass() dvander@213: { dvander@213: List::iterator iter; dvander@213: CPlugin *pPlugin; dvander@213: dvander@315: char error[256]; dvander@213: for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) dvander@213: { dvander@213: pPlugin = (*iter); dvander@213: if (pPlugin->GetStatus() == Plugin_Loaded) dvander@213: { dvander@315: error[0] = '\0'; dvander@315: if (!RunSecondPass(pPlugin, error, sizeof(error))) dvander@315: { dvander@315: g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error); dvander@315: pPlugin->SetErrorState(Plugin_Failed, "%s", error); dvander@315: } dvander@213: } dvander@213: } dvander@298: faluco@216: m_AllPluginsLoaded = true; dvander@213: } dvander@213: dvander@315: bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength) dvander@211: { dvander@298: /* Find any extensions this plugin needs */ dvander@298: struct _ext dvander@298: { dvander@298: cell_t name; dvander@298: cell_t file; dvander@298: cell_t autoload; dvander@298: cell_t required; dvander@298: } *ext; dvander@298: dvander@298: IPluginContext *pBase = pPlugin->GetBaseContext(); dvander@298: uint32_t num = pBase->GetPubVarsNum(); dvander@298: sp_pubvar_t *pubvar; dvander@298: IExtension *pExt; ds@652: char path[PLATFORM_MAX_PATH]; dvander@298: char *file, *name; dvander@298: for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if (strncmp(pubvar->name, "__ext_", 6) == 0) dvander@298: { dvander@298: ext = (_ext *)pubvar->offs; dvander@298: if (!ext->required && !ext->autoload) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE) dvander@298: { dvander@298: continue; dvander@298: } dvander@315: if (pass == 1) dvander@298: { dvander@315: /* Attempt to auto-load if necessary */ dvander@315: if (ext->autoload) dvander@315: { dvander@315: g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); dvander@315: g_Extensions.LoadAutoExtension(path); dvander@315: } dvander@315: } else if (pass == 2) { dvander@315: /* Is this required? */ dvander@315: if (ext->required) dvander@298: { dvander@315: g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); dvander@315: if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL) dvander@315: { dvander@315: pExt = g_Extensions.FindExtensionByName(name); dvander@315: } dvander@315: /* :TODO: should we bind to unloaded extensions? dvander@315: * Currently the extension manager will ignore this. dvander@315: */ dvander@315: if (!pExt || !pExt->IsRunning(NULL, 0)) dvander@315: { dvander@315: if (error) dvander@315: { dvander@315: snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file); dvander@315: } dvander@315: return false; dvander@315: } else { dvander@315: g_Extensions.BindChildPlugin(pExt, pPlugin); dvander@315: } dvander@298: } dvander@298: } dvander@315: } dvander@315: } dvander@315: dvander@315: return true; dvander@315: } dvander@315: dvander@315: bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength) dvander@315: { dvander@315: /* Second pass for extension requirements */ dvander@315: if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength)) dvander@315: { dvander@315: return false; dvander@315: } dvander@315: dvander@315: /* Bind all extra natives */ dvander@315: g_Extensions.BindAllNativesToPlugin(pPlugin); dvander@601: AddFakeNativesToPlugin(pPlugin); dvander@315: dvander@315: /* Find any unbound natives dvander@315: * Right now, these are not allowed dvander@315: */ dvander@315: IPluginContext *pContext = pPlugin->GetBaseContext(); dvander@315: uint32_t num = pContext->GetNativesNum(); dvander@315: sp_native_t *native; dvander@315: for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) dvander@315: { dvander@315: break; dvander@315: } dvander@315: if (native->status == SP_NATIVE_UNBOUND) dvander@315: { dvander@315: if (error) dvander@298: { dvander@315: snprintf(error, maxlength, "Native \"%s\" was not found.", native->name); dvander@298: } dvander@315: return false; dvander@298: } dvander@298: } dvander@211: dvander@211: /* Finish by telling all listeners */ dvander@211: List::iterator iter; dvander@211: IPluginsListener *pListener; dvander@211: for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) dvander@211: { dvander@211: pListener = (*iter); dvander@211: pListener->OnPluginLoaded(pPlugin); dvander@211: } dvander@298: dvander@298: /* Tell this plugin to finish initializing itself */ ds@426: pPlugin->Call_OnPluginStart(); dvander@298: dvander@605: /* Now, if we have fake natives, go through all plugins that might need rebinding */ dvander@605: if (pPlugin->m_fakeNatives.size()) dvander@605: { dvander@605: List::iterator pl_iter; dvander@605: CPlugin *pOther; dvander@605: for (pl_iter = m_plugins.begin(); dvander@605: pl_iter != m_plugins.end(); dvander@605: pl_iter++) dvander@605: { dvander@605: pOther = (*pl_iter); dvander@605: if (pOther->GetStatus() == Plugin_Error dvander@605: && pOther->m_FakeNativesMissing) dvander@605: { dvander@605: TryRefreshNatives(pOther); dvander@605: } dvander@605: } dvander@605: } dvander@605: dvander@298: return true; dvander@211: } dvander@211: dvander@211: void CPluginManager::AddCoreNativesToPlugin(CPlugin *pPlugin) dvander@211: { dvander@211: List::iterator iter; dvander@211: dvander@211: for (iter=m_natives.begin(); iter!=m_natives.end(); iter++) dvander@211: { dvander@211: sp_nativeinfo_t *natives = (*iter); dvander@211: IPluginContext *ctx = pPlugin->GetBaseContext(); dvander@211: unsigned int i=0; dvander@211: /* Attempt to bind every native! */ dvander@211: while (natives[i].func != NULL) dvander@211: { dvander@211: ctx->BindNative(&natives[i++]); dvander@211: } dvander@211: } dvander@174: } dvander@174: dvander@605: void CPluginManager::TryRefreshNatives(CPlugin *pPlugin) dvander@605: { dvander@605: assert(pPlugin->GetBaseContext() != NULL); dvander@605: dvander@605: AddFakeNativesToPlugin(pPlugin); dvander@605: dvander@605: /* Find any unbound natives dvander@605: * Right now, these are not allowed dvander@605: */ dvander@605: IPluginContext *pContext = pPlugin->GetBaseContext(); dvander@605: uint32_t num = pContext->GetNativesNum(); dvander@605: sp_native_t *native; dvander@605: for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) dvander@605: { dvander@605: break; dvander@605: } dvander@605: if (native->status == SP_NATIVE_UNBOUND) dvander@605: { dvander@605: pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name); dvander@605: return; dvander@605: } dvander@605: } dvander@605: dvander@605: /* If we got here, all natives are okay again! */ dvander@605: pPlugin->m_status = Plugin_Running; dvander@605: if ((pPlugin->m_ctx.ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED) dvander@605: { dvander@605: pPlugin->m_ctx.ctx->flags &= ~SPFLAG_PLUGIN_PAUSED; dvander@605: _SetPauseState(pPlugin, false); dvander@605: } dvander@605: } dvander@605: dvander@601: void CPluginManager::AddFakeNativesToPlugin(CPlugin *pPlugin) dvander@601: { dvander@601: IPluginContext *pContext = pPlugin->GetBaseContext(); dvander@601: sp_nativeinfo_t native; dvander@601: dvander@601: List::iterator iter; dvander@601: FakeNative *pNative; dvander@601: sp_context_t *ctx; dvander@601: for (iter = m_Natives.begin(); iter != m_Natives.end(); iter++) dvander@601: { dvander@601: pNative = (*iter); dvander@601: ctx = pNative->ctx->GetContext(); dvander@601: if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED) dvander@601: { dvander@601: /* Ignore natives in paused plugins */ dvander@601: continue; dvander@601: } dvander@601: native.name = pNative->name.c_str(); dvander@601: native.func = pNative->func; dvander@601: if (pContext->BindNative(&native) == SP_ERROR_NONE) dvander@601: { dvander@605: /* Add us as a dependency, but we're careful not to do this circularly! */ dvander@605: if (pNative->ctx != pContext) dvander@605: { dvander@605: CPlugin *pOther = GetPluginByCtx(ctx); dvander@605: if (pOther->m_dependents.find(pPlugin) == pOther->m_dependents.end()) dvander@605: { dvander@605: pOther->m_dependents.push_back(pPlugin); dvander@605: } dvander@605: if (pPlugin->m_dependsOn.find(pOther) == pPlugin->m_dependsOn.end()) dvander@605: { dvander@605: pPlugin->m_dependsOn.push_back(pOther); dvander@605: } dvander@605: } dvander@601: } dvander@601: } dvander@601: } dvander@601: dvander@174: bool CPluginManager::UnloadPlugin(IPlugin *plugin) dvander@174: { dvander@174: CPlugin *pPlugin = (CPlugin *)plugin; dvander@298: dvander@298: /* This prevents removal during insertion or anything else weird */ dvander@298: if (m_plugins.find(pPlugin) == m_plugins.end()) dvander@298: { dvander@298: return false; dvander@298: } dvander@298: dvander@605: /* Remove us from the lookup table and linked list */ dvander@605: m_plugins.remove(pPlugin); dvander@605: sm_trie_delete(m_LoadLookup, pPlugin->m_filename); dvander@605: dvander@605: /* Go through all dependent plugins and tell them this plugin is now gone */ dvander@605: List::iterator pl_iter; dvander@605: CPlugin *pOther; dvander@605: for (pl_iter = pPlugin->m_dependents.begin(); dvander@605: pl_iter != pPlugin->m_dependents.end(); dvander@605: pl_iter++) dvander@605: { dvander@605: pOther = (*pl_iter); dvander@605: pOther->DependencyDropped(pPlugin); dvander@605: } dvander@605: dvander@605: /* Tell everyone we depend on that we no longer exist */ dvander@605: for (pl_iter = pPlugin->m_dependsOn.begin(); dvander@605: pl_iter != pPlugin->m_dependsOn.end(); dvander@605: pl_iter++) dvander@605: { dvander@605: pOther = (*pl_iter); dvander@605: pOther->m_dependents.remove(pPlugin); dvander@605: } dvander@605: dvander@605: /* Remove all of our native functions */ dvander@605: List::iterator fn_iter; dvander@605: FakeNative *pNative; dvander@605: for (fn_iter = pPlugin->m_fakeNatives.begin(); dvander@605: fn_iter != pPlugin->m_fakeNatives.end(); dvander@605: fn_iter++) dvander@605: { dvander@605: pNative = (*fn_iter); dvander@605: m_Natives.remove(pNative); dvander@605: sm_trie_delete(m_pNativeLookup, pNative->name.c_str()); dvander@605: g_pVM->DestroyFakeNative(pNative->func); dvander@605: delete pNative; dvander@605: } dvander@605: dvander@174: List::iterator iter; dvander@174: IPluginsListener *pListener; dvander@282: dvander@311: if (pPlugin->GetStatus() <= Plugin_Error) dvander@282: { dvander@282: /* Notify listeners of unloading */ dvander@282: for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) dvander@282: { dvander@282: pListener = (*iter); dvander@282: pListener->OnPluginUnloaded(pPlugin); dvander@282: } dvander@282: /* Notify plugin */ ds@426: pPlugin->Call_OnPluginEnd(); dvander@282: } dvander@282: dvander@174: for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) dvander@174: { dvander@282: /* Notify listeners of destruction */ dvander@174: pListener = (*iter); dvander@174: pListener->OnPluginDestroyed(pPlugin); dvander@174: } dvander@176: dvander@282: /* Tell the plugin to delete itself */ dvander@174: delete pPlugin; dvander@174: dvander@174: return true; dvander@174: } dvander@174: dvander@174: IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx) dvander@174: { dvander@174: IPlugin *pl = (IPlugin *)ctx->user[SM_CONTEXTVAR_MYSELF]; dvander@174: return pl; dvander@174: } dvander@174: dvander@174: unsigned int CPluginManager::GetPluginCount() dvander@174: { dvander@174: return m_plugins.size(); dvander@174: } dvander@174: dvander@174: void CPluginManager::AddPluginsListener(IPluginsListener *listener) dvander@174: { dvander@174: m_listeners.push_back(listener); dvander@174: } dvander@174: dvander@174: void CPluginManager::RemovePluginsListener(IPluginsListener *listener) dvander@174: { dvander@174: m_listeners.remove(listener); dvander@174: } dvander@174: dvander@174: IPluginIterator *CPluginManager::GetPluginIterator() dvander@174: { dvander@174: if (m_iters.empty()) dvander@174: { dvander@174: return new CPluginIterator(&m_plugins); dvander@174: } else { dvander@174: CPluginIterator *iter = m_iters.front(); dvander@174: m_iters.pop(); faluco@216: iter->Reset(); dvander@174: return iter; dvander@174: } dvander@174: } dvander@174: dvander@174: void CPluginManager::ReleaseIterator(CPluginIterator *iter) dvander@174: { dvander@174: m_iters.push(iter); dvander@174: } dvander@176: dvander@203: bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath) dvander@203: { dvander@203: /* As an optimization, we do not call strlen, but compute the length in the first pass */ dvander@203: size_t alias_len = 0; dvander@203: size_t local_len = 0; dvander@203: dvander@203: const char *ptr = alias; dvander@203: unsigned int alias_explicit_paths = 0; dvander@203: unsigned int alias_path_end = 0; dvander@203: while (*ptr != '\0') dvander@203: { dvander@203: if (*ptr == '\\' || *ptr == '/') dvander@203: { dvander@203: alias_explicit_paths++; dvander@203: alias_path_end = alias_len; dvander@203: } dvander@203: alias_len++; dvander@203: ptr++; dvander@203: } dvander@203: dvander@209: if (alias_explicit_paths && alias_path_end == alias_len - 1) dvander@203: { dvander@203: /* Trailing slash is totally invalid here */ dvander@203: return false; dvander@203: } dvander@203: dvander@203: ptr = localpath; dvander@203: unsigned int local_explicit_paths = 0; dvander@203: unsigned int local_path_end = 0; dvander@203: while (*ptr != '\0') dvander@203: { dvander@203: if (*ptr == '\\' || *ptr == '/') dvander@203: { dvander@203: local_explicit_paths++; dvander@203: local_path_end = local_len; dvander@203: } dvander@203: local_len++; dvander@203: ptr++; dvander@203: } dvander@203: dvander@203: /* If the alias has more explicit paths than the real path, dvander@203: * no match will be possible. dvander@203: */ dvander@203: if (alias_explicit_paths > local_explicit_paths) dvander@203: { dvander@203: return false; dvander@203: } dvander@203: dvander@203: if (alias_explicit_paths) dvander@203: { dvander@203: /* We need to find if the paths match now. For example, these should all match: dvander@203: * csdm csdm dvander@203: * csdm optional/csdm dvander@203: * csdm/ban optional/crab/csdm/ban dvander@203: */ dvander@203: const char *aliasptr = alias; dvander@203: const char *localptr = localpath; dvander@203: bool match = true; dvander@203: do dvander@203: { dvander@203: if (*aliasptr != *localptr) dvander@203: { dvander@203: /* We have to knock one path off */ dvander@203: local_explicit_paths--; dvander@203: if (alias_explicit_paths > local_explicit_paths) dvander@203: { dvander@203: /* Skip out if we're gonna have an impossible match */ dvander@203: return false; dvander@203: } dvander@203: /* Eat up localptr tokens until we get a result */ dvander@203: while (((localptr - localpath) < (int)local_path_end) dvander@203: && *localptr != '/' dvander@203: && *localptr != '\\') dvander@203: { dvander@203: localptr++; dvander@203: } dvander@203: /* Check if we hit the end of our searchable area. dvander@203: * This probably isn't possible because of the path dvander@203: * count check, but it's a good idea anyway. dvander@203: */ dvander@203: if ((localptr - localpath) >= (int)local_path_end) dvander@203: { dvander@203: return false; dvander@203: } else { dvander@203: /* Consume the slash token */ dvander@203: localptr++; dvander@203: } dvander@203: /* Reset the alias pointer so we can continue consuming */ dvander@203: aliasptr = alias; dvander@203: match = false; dvander@203: continue; dvander@203: } dvander@203: /* Note: dvander@203: * This is safe because if localptr terminates early, aliasptr will too dvander@203: */ dvander@203: do dvander@203: { dvander@203: /* We should never reach the end of the string because of this check. */ dvander@203: bool aliasend = (aliasptr - alias) > (int)alias_path_end; dvander@203: bool localend = (localptr - localpath) > (int)local_path_end; dvander@203: if (aliasend || localend) dvander@203: { dvander@203: if (aliasend && localend) dvander@203: { dvander@203: /* we matched, and we can break out now */ dvander@203: match = true; dvander@203: break; dvander@203: } dvander@203: /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */ dvander@203: match = false; dvander@203: break; dvander@203: } dvander@203: dvander@203: /* If we got here, it's safe to compare the next two tokens */ dvander@203: if (*localptr != *aliasptr) dvander@203: { dvander@203: match = false; dvander@203: break; dvander@203: } dvander@203: localptr++; dvander@203: aliasptr++; dvander@203: } while (true); dvander@203: } while (!match); dvander@203: } dvander@203: dvander@203: /* If we got here, it's time to compare filenames */ dvander@203: const char *aliasptr = alias; dvander@203: const char *localptr = localpath; dvander@203: dvander@203: if (alias_explicit_paths) dvander@203: { dvander@203: aliasptr = &alias[alias_path_end + 1]; dvander@203: } dvander@203: dvander@203: if (local_explicit_paths) dvander@203: { dvander@203: localptr = &localpath[local_path_end + 1]; dvander@203: } dvander@203: dvander@203: while (true) dvander@203: { dvander@203: if (*aliasptr == '*') dvander@203: { dvander@203: /* First, see if this is the last character */ dvander@351: if ((unsigned)(aliasptr - alias) == alias_len - 1) dvander@203: { dvander@203: /* If so, there's no need to match anything else */ dvander@203: return true; dvander@203: } dvander@203: /* Otherwise, we need to search for an appropriate matching sequence in local. dvander@203: * Note that we only need to search up to the next asterisk. dvander@203: */ dvander@203: aliasptr++; dvander@203: bool match = true; dvander@203: const char *local_orig = localptr; dvander@203: do dvander@203: { dvander@203: match = true; dvander@203: while (*aliasptr != '\0' && *aliasptr != '*') dvander@203: { dvander@203: /* Since aliasptr is never '\0', localptr hitting the end will fail */ dvander@203: if (*aliasptr != *localptr) dvander@203: { dvander@203: match = false; dvander@203: break; dvander@203: } dvander@203: aliasptr++; dvander@203: localptr++; dvander@203: } dvander@203: if (!match) dvander@203: { dvander@203: /* If we didn't get a match, we need to advance the search stream. dvander@203: * This will let us skip tokens while still searching for another match. dvander@203: */ dvander@203: localptr = ++local_orig; dvander@203: /* Make sure we don't go out of bounds */ dvander@203: if (*localptr == '\0') dvander@203: { dvander@203: break; dvander@203: } dvander@203: } dvander@203: } while (!match); dvander@203: dvander@203: if (!match) dvander@203: { dvander@203: return false; dvander@203: } else { dvander@203: /* If we got a match, move on to the next token */ dvander@203: continue; dvander@203: } dvander@203: } else if (*aliasptr == '\0') { dvander@203: if (*localptr == '\0' dvander@203: || dvander@203: strcmp(localptr, ".smx") == 0) dvander@203: { dvander@203: return true; dvander@203: } else { dvander@203: return false; dvander@203: } dvander@203: } else if (*aliasptr != *localptr) { dvander@203: return false; dvander@203: } dvander@203: aliasptr++; dvander@203: localptr++; dvander@203: } dvander@203: dvander@203: return true; dvander@203: } dvander@203: faluco@290: bool CPluginManager::IsLateLoadTime() const dvander@176: { dvander@315: return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading()); dvander@176: } dvander@220: dvander@220: void CPluginManager::OnSourceModAllInitialized() dvander@220: { dvander@229: m_MyIdent = g_ShareSys.CreateCoreIdentity(); dvander@229: dvander@262: HandleAccess sec; dvander@262: g_HandleSys.InitAccessDefaults(NULL, &sec); dvander@220: dvander@262: sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY; dvander@262: sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY; dvander@262: dvander@262: g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL); dvander@229: g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN"); dvander@309: dvander@309: g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this); dvander@336: dvander@336: g_ShareSys.AddInterface(NULL, this); dvander@220: } dvander@220: dvander@220: void CPluginManager::OnSourceModShutdown() dvander@220: { dvander@309: g_RootMenu.RemoveRootConsoleCommand("plugins", this); dvander@309: dvander@298: List::iterator iter; dvander@298: while ( (iter = m_plugins.begin()) != m_plugins.end() ) dvander@298: { dvander@298: UnloadPlugin((*iter)); dvander@298: } dvander@298: dvander@229: g_HandleSys.RemoveType(g_PluginType, m_MyIdent); dvander@229: g_ShareSys.DestroyIdentType(g_PluginIdent); dvander@229: g_ShareSys.DestroyIdentity(m_MyIdent); dvander@220: } dvander@220: dvander@220: void CPluginManager::OnHandleDestroy(HandleType_t type, void *object) dvander@220: { dvander@220: /* We don't care about the internal object, actually */ dvander@220: } dvander@233: dvander@233: void CPluginManager::RegisterNativesFromCore(sp_nativeinfo_t *natives) dvander@233: { dvander@233: m_natives.push_back(natives); dvander@233: } dvander@247: dvander@247: IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err) dvander@247: { dvander@247: IPlugin *pPlugin; dvander@247: HandleError _err; dvander@247: dvander@262: HandleSecurity sec; dvander@262: dvander@262: sec.pOwner = NULL; dvander@262: sec.pIdentity = m_MyIdent; dvander@262: dvander@262: if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None) dvander@247: { dvander@247: pPlugin = NULL; dvander@247: } dvander@247: dvander@247: if (err) dvander@247: { dvander@247: *err = _err; dvander@247: } dvander@247: dvander@247: return pPlugin; dvander@247: } faluco@290: faluco@290: CPlugin *CPluginManager::GetPluginByOrder(int num) faluco@290: { faluco@290: if (num < 1 || num > (int)GetPluginCount()) faluco@290: { faluco@290: return NULL; faluco@290: } faluco@290: faluco@290: CPlugin *pl; faluco@290: int id = 1; faluco@290: faluco@290: IPluginIterator *iter = GetPluginIterator(); faluco@290: for (; iter->MorePlugins() && idNextPlugin(), id++) {} faluco@290: faluco@290: pl = (CPlugin *)(iter->GetPlugin()); faluco@290: iter->Release(); faluco@290: faluco@290: return pl; dvander@298: } dvander@309: dvander@309: const char *CPluginManager::GetStatusText(PluginStatus st) dvander@309: { dvander@309: switch (st) dvander@309: { dvander@309: case Plugin_Running: dvander@309: return "Running"; dvander@309: case Plugin_Paused: dvander@309: return "Paused"; dvander@309: case Plugin_Error: dvander@309: return "Error"; dvander@309: case Plugin_Uncompiled: dvander@309: return "Uncompiled"; dvander@309: case Plugin_BadLoad: dvander@309: return "Bad Load"; dvander@309: case Plugin_Failed: dvander@309: return "Failed"; dvander@309: default: dvander@309: assert(false); dvander@309: return "-"; dvander@309: } dvander@309: } dvander@309: dvander@309: void CPluginManager::OnRootConsoleCommand(const char *command, unsigned int argcount) dvander@309: { dvander@309: if (argcount >= 3) dvander@309: { dvander@309: const char *cmd = g_RootMenu.GetArgument(2); dvander@309: if (strcmp(cmd, "list") == 0) dvander@309: { dvander@309: char buffer[256]; dvander@309: unsigned int id = 1; dvander@309: int plnum = GetPluginCount(); dvander@309: dvander@309: if (!plnum) dvander@309: { dvander@309: g_RootMenu.ConsolePrint("[SM] No plugins loaded"); dvander@309: return; dvander@309: } else { ds@582: g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : ""); dvander@309: } dvander@309: dvander@309: IPluginIterator *iter = GetPluginIterator(); dvander@309: for (; iter->MorePlugins(); iter->NextPlugin(), id++) dvander@309: { dvander@309: IPlugin *pl = iter->GetPlugin(); dvander@309: assert(pl->GetStatus() != Plugin_Created); dvander@309: int len = 0; dvander@309: const sm_plugininfo_t *info = pl->GetPublicInfo(); dvander@351: if (pl->GetStatus() != Plugin_Running) dvander@340: { dvander@340: len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus())); dvander@340: } else { dvander@340: len += UTIL_Format(buffer, sizeof(buffer), " %02d", id); dvander@340: } dvander@309: len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); dvander@309: if (IS_STR_FILLED(info->version)) dvander@309: { dvander@309: len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version); dvander@309: } dvander@309: if (IS_STR_FILLED(info->author)) dvander@309: { dvander@309: UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author); dvander@309: } dvander@309: g_RootMenu.ConsolePrint("%s", buffer); dvander@309: } dvander@309: dvander@309: iter->Release(); dvander@309: return; dvander@309: } else if (strcmp(cmd, "load") == 0) { dvander@309: if (argcount < 4) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load "); dvander@309: return; dvander@309: } dvander@309: dvander@309: char error[128]; faluco@383: bool wasloaded; dvander@309: const char *filename = g_RootMenu.GetArgument(3); faluco@383: IPlugin *pl = LoadPlugin(filename, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded); faluco@383: faluco@383: if (wasloaded) faluco@383: { faluco@383: g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", filename); faluco@383: return; faluco@383: } dvander@309: dvander@309: if (pl) dvander@309: { dvander@309: g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", filename); dvander@309: } else { dvander@309: g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", filename, error); dvander@309: } dvander@309: dvander@309: return; dvander@309: } else if (strcmp(cmd, "unload") == 0) { dvander@309: if (argcount < 4) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#>"); dvander@309: return; dvander@309: } dvander@309: dvander@309: int id = 1; dvander@309: int num = atoi(g_RootMenu.GetArgument(3)); dvander@309: if (num < 1 || num > (int)GetPluginCount()) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", num); dvander@309: return; dvander@309: } dvander@309: dvander@309: IPluginIterator *iter = GetPluginIterator(); dvander@309: for (; iter->MorePlugins() && idNextPlugin(), id++) dvander@309: ; dvander@309: IPlugin *pl = iter->GetPlugin(); dvander@309: ds@652: char name[PLATFORM_MAX_PATH]; dvander@309: const sm_plugininfo_t *info = pl->GetPublicInfo(); dvander@309: strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); dvander@309: dvander@309: if (UnloadPlugin(pl)) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name); dvander@309: } else { dvander@310: g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name); dvander@309: } dvander@309: dvander@309: iter->Release(); dvander@309: return; dvander@309: } else if (strcmp(cmd, "info") == 0) { dvander@309: if (argcount < 4) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>"); dvander@309: return; dvander@309: } dvander@309: dvander@309: int num = atoi(g_RootMenu.GetArgument(3)); dvander@309: if (num < 1 || num > (int)GetPluginCount()) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Plugin index not found."); dvander@309: return; dvander@309: } dvander@309: dvander@315: CPlugin *pl = GetPluginByOrder(num); dvander@309: const sm_plugininfo_t *info = pl->GetPublicInfo(); dvander@309: dvander@309: g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename()); dvander@315: if (pl->GetStatus() <= Plugin_Error) dvander@309: { dvander@315: if (IS_STR_FILLED(info->name)) dvander@315: { dvander@605: if (IS_STR_FILLED(info->description)) dvander@605: { dvander@605: g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description); dvander@605: } else { dvander@605: g_RootMenu.ConsolePrint(" Title: %s", info->name); dvander@605: } dvander@605: } dvander@605: if (IS_STR_FILLED(info->author)) dvander@605: { dvander@605: g_RootMenu.ConsolePrint(" Author: %s", info->author); dvander@315: } dvander@315: if (IS_STR_FILLED(info->version)) dvander@315: { dvander@315: g_RootMenu.ConsolePrint(" Version: %s", info->version); dvander@315: } dvander@315: if (IS_STR_FILLED(info->url)) dvander@315: { dvander@315: g_RootMenu.ConsolePrint(" URL: %s", info->url); dvander@315: } dvander@605: if (pl->GetStatus() == Plugin_Error) dvander@315: { dvander@605: g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg); dvander@605: } else { dvander@605: g_RootMenu.ConsolePrint(" Debugging: %s", pl->IsDebugging() ? "yes" : "no"); dvander@605: g_RootMenu.ConsolePrint(" Running: %s", pl->GetStatus() == Plugin_Running ? "yes" : "no"); dvander@315: } dvander@315: } else { dvander@315: g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg); dvander@315: g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")", dvander@315: info->name ? info->name : "", dvander@315: info->version ? info->version : ""); dvander@315: if (IS_STR_FILLED(info->url)) dvander@315: { dvander@315: g_RootMenu.ConsolePrint(" File URL: %s", info->url); dvander@315: } dvander@309: } dvander@309: dvander@309: return; dvander@309: } else if (strcmp(cmd, "debug") == 0) { dvander@309: if (argcount < 5) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Usage: sm plugins debug <#> [on|off]"); dvander@309: return; dvander@309: } dvander@309: dvander@309: int num = atoi(g_RootMenu.GetArgument(3)); dvander@309: if (num < 1 || num > (int)GetPluginCount()) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Plugin index not found."); dvander@309: return; dvander@309: } dvander@309: dvander@309: int res; dvander@309: const char *mode = g_RootMenu.GetArgument(4); dvander@309: if ((res=strcmp("on", mode)) && strcmp("off", mode)) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] The only possible options are \"on\" and \"off.\""); dvander@309: return; dvander@309: } dvander@309: dvander@309: bool debug; dvander@309: if (!res) dvander@309: { dvander@309: debug = true; dvander@309: } else { dvander@309: debug = false; dvander@309: } dvander@309: dvander@309: CPlugin *pl = GetPluginByOrder(num); dvander@309: if (debug && pl->IsDebugging()) dvander@309: { dvander@309: g_RootMenu.ConsolePrint("[SM] This plugin is already in debug mode."); dvander@309: return; dvander@309: } else if (!debug && !pl->IsDebugging()) { dvander@309: g_RootMenu.ConsolePrint("[SM] Debug mode is already disabled in this plugin."); dvander@309: return; dvander@309: } dvander@309: dvander@309: char error[256]; dvander@309: if (pl->ToggleDebugMode(debug, error, sizeof(error))) dvander@309: { dvander@310: g_RootMenu.ConsolePrint("[SM] Successfully toggled debug mode on plugin %s.", pl->GetFilename()); dvander@309: return; dvander@309: } else { dvander@309: g_RootMenu.ConsolePrint("[SM] Could not toggle debug mode on plugin %s.", pl->GetFilename()); dvander@309: g_RootMenu.ConsolePrint("[SM] Plugin returned error: %s", error); dvander@309: return; dvander@309: } dvander@309: } dvander@309: } dvander@309: dvander@309: /* Draw the main menu */ dvander@309: g_RootMenu.ConsolePrint("SourceMod Plugins Menu:"); dvander@309: g_RootMenu.DrawGenericOption("list", "Show loaded plugins"); dvander@309: g_RootMenu.DrawGenericOption("load", "Load a plugin"); dvander@309: g_RootMenu.DrawGenericOption("unload", "Unload a plugin"); dvander@309: g_RootMenu.DrawGenericOption("info", "Information about a plugin"); dvander@309: g_RootMenu.DrawGenericOption("debug", "Toggle debug mode on a plugin"); dvander@309: } faluco@333: faluco@333: void CPluginManager::ReloadOrUnloadPlugins() faluco@333: { faluco@333: List::iterator iter; faluco@333: List tmp_list = m_plugins; faluco@333: CPlugin *pl; faluco@333: time_t t; faluco@333: faluco@333: for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++) faluco@333: { faluco@333: pl = (*iter); faluco@333: if (pl->GetType() == PluginType_MapOnly) faluco@333: { faluco@333: UnloadPlugin((IPlugin *)pl); faluco@333: } else if (pl->GetType() == PluginType_MapUpdated) { dvander@450: t = pl->GetFileTimeStamp(); dvander@450: if (!t || t > pl->GetTimeStamp()) faluco@333: { faluco@333: pl->SetTimeStamp(t); faluco@333: UnloadPlugin((IPlugin *)pl); faluco@333: } faluco@333: } faluco@333: } faluco@333: } faluco@430: faluco@430: void CPluginManager::_SetPauseState(CPlugin *pl, bool paused) faluco@430: { faluco@430: List::iterator iter; faluco@430: IPluginsListener *pListener; faluco@430: for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) faluco@430: { faluco@430: pListener = (*iter); faluco@430: pListener->OnPluginPauseChange(pl, paused); faluco@430: } dvander@464: } dvander@464: dvander@601: bool CPluginManager::AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func) dvander@601: { dvander@601: if (sm_trie_retrieve(m_pNativeLookup, name, NULL)) dvander@601: { dvander@601: return false; dvander@601: } dvander@601: dvander@601: FakeNative *pNative = new FakeNative; dvander@601: dvander@601: pNative->func = g_pVM->CreateFakeNative(func, pNative); dvander@601: if (!pNative->func) dvander@601: { dvander@601: delete pNative; dvander@601: return false; dvander@601: } dvander@601: dvander@601: pNative->call = pFunction; dvander@601: pNative->name.assign(name); dvander@601: pNative->ctx = pFunction->GetParentContext(); dvander@601: dvander@601: m_Natives.push_back(pNative); dvander@601: sm_trie_insert(m_pNativeLookup, name,pNative); dvander@601: dvander@605: CPlugin *pPlugin = GetPluginByCtx(pNative->ctx->GetContext()); dvander@605: pPlugin->m_fakeNatives.push_back(pNative); dvander@605: dvander@601: return true; dvander@601: } ds@611: ds@611: void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward) ds@611: { ds@611: List::iterator iter; ds@611: CPlugin *pPlugin; ds@611: IPluginFunction *pFunction; ds@611: ds@611: for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++) ds@611: { ds@611: pPlugin = (*iter); ds@611: ds@611: if (pPlugin->GetStatus() <= Plugin_Paused) ds@611: { ds@611: pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name); ds@611: ds@611: if (pFunction) ds@611: { ds@611: pForward->AddFunction(pFunction); ds@611: } ds@611: } ds@611: } ds@611: }