dvander@173: #include dvander@173: #include "PluginSys.h" dvander@190: #include "LibrarySys.h" dvander@197: #include "sourcemm_api.h" dvander@209: #include "sourcemod.h" dvander@203: #include "CTextParsers.h" dvander@173: dvander@211: CPluginManager g_PluginSys; 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_funcsnum = 0; dvander@209: m_priv_funcs = NULL; dvander@209: m_pub_funcs = NULL; dvander@209: m_errormsg[256] = '\0'; dvander@209: snprintf(m_filename, sizeof(m_filename), "%s", file); dvander@209: } dvander@209: dvander@209: CPlugin::~CPlugin() dvander@173: { dvander@209: if (m_ctx.base) dvander@209: { dvander@209: g_pSourcePawn->FreeBaseContext(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@209: if (m_plugin) dvander@209: { dvander@209: g_pSourcePawn->FreeFromMemory(m_plugin); dvander@209: m_plugin = NULL; dvander@209: } dvander@209: dvander@209: if (m_pub_funcs) dvander@209: { dvander@209: for (uint32_t i=0; iinfo.publics_num; i++) dvander@209: { dvander@211: g_PluginSys.ReleaseFunctionToPool(m_pub_funcs[i]); dvander@209: } dvander@209: delete [] m_pub_funcs; dvander@209: m_pub_funcs = NULL; dvander@209: } dvander@209: dvander@209: if (m_priv_funcs) dvander@209: { dvander@209: for (unsigned int i=0; im_errormsg, sizeof(pPlugin->m_errormsg), "Unable to open file"); dvander@209: pPlugin->m_status = Plugin_BadLoad; dvander@209: return pPlugin; dvander@209: } 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: return NULL; dvander@209: } else { dvander@209: CPlugin *pPlugin = new CPlugin(file); dvander@209: snprintf(pPlugin->m_errormsg, sizeof(pPlugin->m_errormsg), "Error %d while parsing plugin", err); dvander@209: pPlugin->m_status = Plugin_BadLoad; dvander@209: return pPlugin; dvander@209: } dvander@173: } dvander@173: dvander@173: fclose(fp); dvander@173: dvander@209: CPlugin *pPlugin = new CPlugin(file); dvander@209: pPlugin->m_plugin = pl; dvander@209: return pPlugin; dvander@209: } dvander@209: 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@209: if (!error) dvander@209: { dvander@209: SetErrorState(Plugin_Error, "Failed to compile (error %d)", err); dvander@209: } else { dvander@209: snprintf(error, maxlength, "Failed to compile (error %d)", err); dvander@209: } dvander@209: return false; dvander@176: } dvander@176: dvander@209: m_ctx.base = g_pSourcePawn->CreateBaseContext(m_ctx.ctx); dvander@209: m_ctx.ctx->user[SM_CONTEXTVAR_MYSELF] = (void *)this; dvander@209: dvander@209: m_funcsnum = m_ctx.vm->FunctionCount(m_ctx.ctx); dvander@209: dvander@209: /** dvander@209: * Note: Since the m_plugin member will never change, dvander@209: * it is safe to assume the function count will never change dvander@209: */ dvander@209: if (m_funcsnum && m_priv_funcs == NULL) dvander@209: { dvander@209: m_priv_funcs = new CFunction *[m_funcsnum]; dvander@209: memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum); dvander@209: } else { dvander@209: m_priv_funcs = NULL; dvander@209: } dvander@209: dvander@209: if (m_plugin->info.publics_num && m_pub_funcs == NULL) dvander@209: { dvander@209: m_pub_funcs = new CFunction *[m_plugin->info.publics_num]; dvander@209: memset(m_pub_funcs, 0, sizeof(CFunction *) * m_plugin->info.publics_num); dvander@209: } else { dvander@209: m_pub_funcs = NULL; dvander@209: } dvander@209: dvander@211: m_status = Plugin_Created; 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@209: m_status = status; dvander@209: 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@173: dvander@176: IPluginFunction *CPlugin::GetFunctionById(funcid_t func_id) dvander@176: { dvander@176: CFunction *pFunc = NULL; dvander@176: funcid_t save = func_id; dvander@176: dvander@176: if (func_id & 1) dvander@176: { dvander@176: func_id >>= 1; dvander@176: if (func_id >= m_plugin->info.publics_num) dvander@176: { dvander@176: return NULL; dvander@176: } dvander@176: pFunc = m_pub_funcs[func_id]; dvander@176: if (!pFunc) dvander@176: { dvander@211: pFunc = g_PluginSys.GetFunctionFromPool(save, this); dvander@176: m_pub_funcs[func_id] = pFunc; dvander@176: } dvander@176: } else { dvander@176: func_id >>= 1; dvander@176: unsigned int index; dvander@209: if (!g_pVM->FunctionLookup(m_ctx.ctx, func_id, &index)) dvander@176: { dvander@176: return NULL; dvander@176: } dvander@176: pFunc = m_priv_funcs[func_id]; dvander@176: if (!pFunc) dvander@176: { dvander@211: pFunc = g_PluginSys.GetFunctionFromPool(save, this); dvander@176: m_priv_funcs[func_id] = pFunc; dvander@176: } dvander@176: } dvander@176: dvander@176: return pFunc; dvander@176: } dvander@176: dvander@176: IPluginFunction *CPlugin::GetFunctionByName(const char *public_name) dvander@176: { dvander@176: uint32_t index; dvander@209: IPluginContext *base = m_ctx.base; dvander@176: dvander@176: if (base->FindPublicByName(public_name, &index) != SP_ERROR_NONE) dvander@176: { dvander@176: return NULL; dvander@176: } dvander@176: dvander@176: CFunction *pFunc = m_pub_funcs[index]; dvander@176: if (!pFunc) dvander@176: { dvander@176: sp_public_t *pub = NULL; dvander@176: base->GetPublicByIndex(index, &pub); dvander@176: if (pub) dvander@176: { dvander@211: pFunc = g_PluginSys.GetFunctionFromPool(pub->funcid, this); dvander@176: m_pub_funcs[index] = pFunc; dvander@176: } dvander@176: } dvander@176: dvander@176: return pFunc; 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@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: dvander@211: void CPlugin::Call_OnPluginInit() 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: int err; dvander@211: cell_t result; dvander@211: IPluginFunction *pFunction = GetFunctionByName("OnPluginInit"); dvander@211: if (!pFunction) dvander@211: { dvander@211: return; dvander@211: } dvander@211: dvander@211: /* :TODO: push our own handle */ dvander@211: pFunction->PushCell(0); dvander@211: if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) dvander@211: { dvander@211: /* :TODO: log into debugger instead */ dvander@211: SetErrorState(Plugin_Error, "Runtime error %d", err); dvander@211: } 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: dvander@211: if (!error) dvander@211: { dvander@211: error = m_errormsg; dvander@211: maxlength = sizeof(m_errormsg); dvander@211: } dvander@211: dvander@211: int err; dvander@211: cell_t result; dvander@211: IPluginFunction *pFunction = GetFunctionByName("AskPluginLoad"); dvander@211: dvander@211: if (!pFunction) dvander@211: { dvander@211: return true; dvander@211: } dvander@211: dvander@211: pFunction->PushCell(0); //:TODO: handle to ourself 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: /* :TODO: debugging system */ dvander@211: snprintf(error, maxlength, "Plugin load returned run time error %d", err); dvander@211: m_status = Plugin_Error; dvander@211: return false; dvander@211: } dvander@211: dvander@211: if (!result) dvander@211: { dvander@211: m_status = Plugin_Error; dvander@211: return false; dvander@211: } dvander@211: dvander@211: m_status = Plugin_Loaded; dvander@211: dvander@211: return true; dvander@211: } dvander@211: dvander@173: const sp_plugin_t *CPlugin::GetPluginStructure() const dvander@173: { dvander@173: return m_plugin; dvander@173: } dvander@173: dvander@173: IPluginContext *CPlugin::GetBaseContext() const dvander@173: { dvander@209: return m_ctx.base; dvander@173: } dvander@173: dvander@173: sp_context_t *CPlugin::GetContext() const dvander@173: { dvander@209: return m_ctx.ctx; dvander@173: } dvander@173: dvander@173: const char *CPlugin::GetFilename() const dvander@173: { dvander@173: return m_filename; dvander@173: } dvander@173: dvander@174: PluginType CPlugin::GetType() const dvander@173: { dvander@174: return m_type; dvander@173: } dvander@173: dvander@173: const sm_plugininfo_t *CPlugin::GetPublicInfo() const dvander@173: { dvander@173: return &m_info; dvander@173: } dvander@173: dvander@173: unsigned int CPlugin::GetSerial() const dvander@173: { dvander@173: return m_serial; dvander@173: } dvander@173: dvander@173: PluginStatus CPlugin::GetStatus() const dvander@173: { dvander@173: return m_status; dvander@173: } dvander@173: dvander@173: bool CPlugin::IsDebugging() const 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@173: dvander@173: /* :TODO: execute some forwards or some crap */ dvander@173: dvander@173: return true; dvander@173: } dvander@174: dvander@174: /******************* dvander@174: * PLUGIN ITERATOR * dvander@174: *******************/ dvander@190: dvander@174: CPluginManager::CPluginIterator::CPluginIterator(List *_mylist) dvander@174: { dvander@174: mylist = _mylist; 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@211: } dvander@211: dvander@211: CPluginManager::~CPluginManager() dvander@211: { dvander@211: //:TODO: we need a good way to free what we're holding dvander@211: sm_trie_destroy(m_LoadLookup); dvander@211: } dvander@211: dvander@211: dvander@211: 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; dvander@203: if ((err=g_TextParse.ParseFile_SMC(config, &m_PluginInfo, &line, &col)) != SMCParse_Okay) dvander@203: { dvander@203: /* :TODO: log the error, don't bail out though */ 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: { dvander@209: char base_path[PLATFORM_MAX_PATH+1]; 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@209: //:TODO: write a logger and LOG THIS UP, BABY dvander@209: //g_LibSys.GetPlatformError(error, err_max); 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: { dvander@209: char new_local[PLATFORM_MAX_PATH+1]; 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 */ dvander@211: char plugin[PLATFORM_MAX_PATH+1]; 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 dvander@211: //:TODO: remove this function, create a better wrapper for LoadPlugin()/LoadAutoPlugin() dvander@209: void CPluginManager::LoadAutoPlugin(const char *file) dvander@209: { dvander@211: /** dvander@211: * Does this plugin already exist? dvander@211: */ dvander@211: CPlugin *pPlugin; dvander@211: if (sm_trie_retrieve(m_LoadLookup, file, (void **)&pPlugin)) dvander@211: { dvander@211: /* First check the type */ dvander@211: PluginType type = pPlugin->GetType(); dvander@211: if (type == PluginType_Private dvander@211: || type == PluginType_Global) dvander@211: { dvander@211: return; dvander@211: } dvander@211: /* Check to see if we should try reloading it */ dvander@211: if (pPlugin->GetStatus() == Plugin_BadLoad dvander@211: || pPlugin->GetStatus() == Plugin_Error) dvander@211: { dvander@211: UnloadPlugin(pPlugin); dvander@211: } dvander@211: } dvander@211: dvander@211: pPlugin = CPlugin::CreatePlugin(file, NULL, 0); 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@209: pPlugin->SetErrorState(Plugin_Error, "Unable to set option (key \"%s\") (value \"%s\")", key, val); dvander@209: pPlugin->CancelMyCompile(); dvander@209: co = NULL; dvander@209: break; dvander@209: } dvander@209: } dvander@190: } dvander@190: } dvander@209: dvander@209: if (co) dvander@209: { dvander@209: pPlugin->FinishMyCompile(NULL, 0); dvander@209: co = NULL; dvander@209: } dvander@209: dvander@211: /* We don't care about the return value */ dvander@211: if (pPlugin->GetStatus() == Plugin_Created) dvander@211: { dvander@211: AddCoreNativesToPlugin(pPlugin); dvander@211: pPlugin->Call_AskPluginLoad(NULL, 0); dvander@211: } dvander@211: dvander@211: AddPlugin(pPlugin); dvander@190: } dvander@190: dvander@174: IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t err_max) dvander@174: { dvander@211: /* See if this plugin is already loaded... reformat to get sep chars right */ dvander@211: char checkpath[PLATFORM_MAX_PATH+1]; dvander@211: g_LibSys.PathFormat(checkpath, sizeof(checkpath), "%s", path); dvander@211: dvander@211: /** dvander@211: * In manually loading a plugin, any sort of load error causes a deletion. dvander@211: * This is because it's assumed manually loaded plugins will not be managed. dvander@211: * For managed plugins, we need the UI to report them properly. dvander@211: */ dvander@211: dvander@211: CPlugin *pPlugin; dvander@211: if (sm_trie_retrieve(m_LoadLookup, checkpath, (void **)&pPlugin)) dvander@211: { dvander@211: snprintf(error, err_max, "Plugin file is alread loaded"); dvander@211: return NULL; dvander@211: } dvander@211: dvander@211: pPlugin = CPlugin::CreatePlugin(path, error, err_max); dvander@174: dvander@174: if (!pPlugin) dvander@174: { dvander@174: return NULL; dvander@174: } dvander@174: dvander@209: ICompilation *co = pPlugin->StartMyCompile(g_pVM); dvander@209: if (!co || (debug && !g_pVM->SetCompilationOption(co, "debug", "1"))) dvander@209: { dvander@209: snprintf(error, err_max, "Unable to start%s compilation", debug ? " debug" : ""); dvander@209: pPlugin->CancelMyCompile(); dvander@209: delete pPlugin; dvander@209: return NULL; dvander@209: } dvander@209: dvander@209: if (!pPlugin->FinishMyCompile(error, err_max)) dvander@209: { dvander@209: delete pPlugin; dvander@209: return NULL; dvander@209: } dvander@209: dvander@209: pPlugin->m_type = type; dvander@209: dvander@211: AddCoreNativesToPlugin(pPlugin); dvander@211: dvander@211: /* Finally, ask the plugin if it wants to be loaded */ dvander@211: if (!pPlugin->Call_AskPluginLoad(error, err_max)) dvander@211: { dvander@211: delete pPlugin; dvander@211: return NULL; dvander@211: } dvander@211: dvander@211: AddPlugin(pPlugin); dvander@209: dvander@209: return pPlugin; dvander@209: } dvander@209: dvander@211: void CPluginManager::AddPlugin(CPlugin *pPlugin) dvander@209: { dvander@174: m_plugins.push_back(pPlugin); dvander@211: sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin); dvander@174: dvander@174: List::iterator iter; dvander@174: IPluginsListener *pListener; 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@211: /* If the second pass was already completed, we have to run the pass on this plugin */ dvander@211: if (m_AllPluginsLoaded && pPlugin->GetStatus() == Plugin_Loaded) dvander@211: { dvander@211: RunSecondPass(pPlugin); dvander@211: } dvander@211: } dvander@211: dvander@211: void CPluginManager::RunSecondPass(CPlugin *pPlugin) dvander@211: { dvander@211: /* Tell this plugin to finish initializing itself */ dvander@211: pPlugin->Call_OnPluginInit(); 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@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@174: bool CPluginManager::UnloadPlugin(IPlugin *plugin) dvander@174: { dvander@174: CPlugin *pPlugin = (CPlugin *)plugin; dvander@174: dvander@174: /* :TODO: More */ dvander@174: dvander@174: List::iterator iter; dvander@174: IPluginsListener *pListener; dvander@174: for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) dvander@174: { dvander@174: pListener = (*iter); dvander@174: pListener->OnPluginDestroyed(pPlugin); dvander@174: } dvander@211: dvander@211: m_plugins.remove(plugin); dvander@211: sm_trie_delete(m_LoadLookup, pPlugin->m_filename); dvander@176: 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(); 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@176: void CPluginManager::ReleaseFunctionToPool(CFunction *func) dvander@176: { dvander@177: if (!func) dvander@177: { dvander@177: return; dvander@177: } dvander@177: func->Cancel(); dvander@176: m_funcpool.push(func); dvander@176: } dvander@176: dvander@176: CFunction *CPluginManager::GetFunctionFromPool(funcid_t f, CPlugin *plugin) dvander@176: { dvander@176: if (m_funcpool.empty()) dvander@176: { dvander@176: return new CFunction(f, plugin); dvander@176: } else { dvander@176: CFunction *func = m_funcpool.front(); dvander@176: m_funcpool.pop(); dvander@176: func->Set(f, plugin); dvander@176: return func; dvander@176: } dvander@176: } 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@203: if (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: dvander@211: bool CPluginManager::IsLateLoadTime() dvander@176: { dvander@211: return (m_AllPluginsLoaded || g_SourceMod.IsLateLoadInMap()); dvander@176: }