# HG changeset patch # User Scott Ehlert # Date 1227537586 21600 # Node ID 31f49899c0ae974a3dc31093f3621bed3c413493 # Parent 9981b8ecf9358c098c80228633ccb8bc8df2367c# Parent d8c659d4aae429934fa375b52f4f7e5bc6ccdd39 Merge. diff -r 9981b8ecf935 -r 31f49899c0ae core/ExtensionSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ExtensionSys.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,1208 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include "ExtensionSys.h" +#include "LibrarySys.h" +#include "ShareSys.h" +#include "Logger.h" +#include "sourcemm_api.h" +#include "sm_srvcmds.h" +#include "sm_stringutil.h" + +CExtensionManager g_Extensions; +IdentityType_t g_ExtType; + +void CExtension::Initialize(const char *filename, const char *path) +{ + m_pAPI = NULL; + m_pIdentToken = NULL; + unload_code = 0; + m_bFullyLoaded = false; + m_File.assign(filename); + m_Path.assign(path); +} + +CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path) +{ + Initialize(filename, path); + m_pAPI = pAPI; +} + +CLocalExtension::CLocalExtension(const char *filename) +{ + m_PlId = 0; + m_pLib = NULL; + + char path[PLATFORM_MAX_PATH]; + + /* First see if there is an engine specific build! */ + g_SourceMod.BuildPath(Path_SM, + path, + PLATFORM_MAX_PATH, +#if defined METAMOD_PLAPI_VERSION +#if defined ORANGEBOX_BUILD + "extensions/auto.2.ep2/%s", +#else + "extensions/auto.2.ep1/%s", +#endif //ORANGEBOX_BUILD +#else //METAMOD_PLAPI_VERSION + "extensions/auto.1.ep1/%s", +#endif //METAMOD_PLAPI_VERSION + filename); + + /* Try the "normal" version */ + if (!g_LibSys.IsPathFile(path)) + { + g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename); + } + + Initialize(filename, path); +} + +bool CRemoteExtension::Load(char *error, size_t maxlength) +{ + if (!PerformAPICheck(error, maxlength)) + { + m_pAPI = NULL; + return false; + } + + if (!CExtension::Load(error, maxlength)) + { + m_pAPI = NULL; + return false; + } + + return true; +} + +bool CLocalExtension::Load(char *error, size_t maxlength) +{ + m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength); + + if (m_pLib == NULL) + { + return false; + } + + typedef IExtensionInterface *(*GETAPI)(); + GETAPI pfnGetAPI = NULL; + + if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL) + { + m_pLib->CloseLibrary(); + m_pLib = NULL; + snprintf(error, maxlength, "Unable to find extension entry point"); + return false; + } + + m_pAPI = pfnGetAPI(); + + /* Check pointer and version */ + if (!PerformAPICheck(error, maxlength)) + { + m_pLib->CloseLibrary(); + m_pLib = NULL; + m_pAPI = NULL; + return false; + } + + /* Load as MM:S */ + if (m_pAPI->IsMetamodExtension()) + { + bool already; + m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength); + } + + if (!CExtension::Load(error, maxlength)) + { + if (m_pAPI->IsMetamodExtension()) + { + if (m_PlId) + { + char dummy[255]; + g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy)); + m_PlId = 0; + } + } + m_pLib->CloseLibrary(); + m_pLib = NULL; + m_pAPI = NULL; + return false; + } + + return true; +} + +void CRemoteExtension::Unload() +{ +} + +void CLocalExtension::Unload() +{ + if (m_pAPI != NULL && m_PlId) + { + char temp_buffer[255]; + g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer)); + m_PlId = 0; + } + + if (m_pLib != NULL) + { + m_pLib->CloseLibrary(); + m_pLib = NULL; + } +} + +bool CRemoteExtension::IsExternal() +{ + return true; +} + +bool CLocalExtension::IsExternal() +{ + return false; +} + +CExtension::~CExtension() +{ + DestroyIdentity(); +} + +bool CExtension::PerformAPICheck(char *error, size_t maxlength) +{ + if (!m_pAPI) + { + snprintf(error, maxlength, "No IExtensionInterface instance provided"); + return false; + } + + if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) + { + snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION); + return false; + } + + return true; +} + +bool CExtension::Load(char *error, size_t maxlength) +{ + CreateIdentity(); + if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading())) + { + DestroyIdentity(); + return false; + } + else + { + /* Check if we're past load time */ + if (!g_SourceMod.IsMapLoading()) + { + m_pAPI->OnExtensionsAllLoaded(); + } + } + + return true; +} + +void CExtension::CreateIdentity() +{ + if (m_pIdentToken != NULL) + { + return; + } + + m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this); +} + +void CExtension::DestroyIdentity() +{ + if (m_pIdentToken == NULL) + { + return; + } + + g_ShareSys.DestroyIdentity(m_pIdentToken); + m_pIdentToken = NULL; +} + +void CExtension::MarkAllLoaded() +{ + assert(m_bFullyLoaded == false); + if (!m_bFullyLoaded) + { + m_bFullyLoaded = true; + m_pAPI->OnExtensionsAllLoaded(); + } +} + +void CExtension::AddPlugin(CPlugin *pPlugin) +{ + /* Unfortunately we have to do this :( */ + if (m_Dependents.find(pPlugin) != m_Dependents.end()) + { + m_Dependents.push_back(pPlugin); + } +} + +void CExtension::SetError(const char *error) +{ + m_Error.assign(error); +} + +IExtensionInterface *CExtension::GetAPI() +{ + return m_pAPI; +} + +const char *CExtension::GetFilename() +{ + return m_File.c_str(); +} + +IdentityToken_t *CExtension::GetIdentity() +{ + return m_pIdentToken; +} + +bool CRemoteExtension::IsLoaded() +{ + return (m_pAPI != NULL); +} + +bool CLocalExtension::IsLoaded() +{ + return (m_pLib != NULL); +} + +void CExtension::AddDependency(const IfaceInfo *pInfo) +{ + if (m_Deps.find(*pInfo) == m_Deps.end()) + { + m_Deps.push_back(*pInfo); + } +} + +bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2) +{ + return (i1.iface == i2.iface) && (i1.owner == i2.owner); +} + +void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface) +{ + IfaceInfo info; + info.iface = iface; + info.owner = pOther; + + List::iterator iter; + for (iter = m_ChildDeps.begin(); + iter != m_ChildDeps.end(); + iter++) + { + IfaceInfo &other = (*iter); + if (other == info) + { + return; + } + } + + m_ChildDeps.push_back(info); +} + +ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) +{ + List::iterator iter = m_Deps.begin(); + + if (iter == m_Deps.end()) + { + return NULL; + } + + if (pOwner) + { + *pOwner = (*iter).owner; + } + if (pInterface) + { + *pInterface = (*iter).iface; + } + + List::iterator *pIter = new List::iterator(iter); + + return (ITERATOR *)pIter; +} + +bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) +{ + List::iterator *pIter = (List::iterator *)iter; + List::iterator _iter; + + if (_iter == m_Deps.end()) + { + return false; + } + + _iter++; + + if (pOwner) + { + *pOwner = (*_iter).owner; + } + if (pInterface) + { + *pInterface = (*_iter).iface; + } + + *pIter = _iter; + + if (_iter == m_Deps.end()) + { + return false; + } + + return true; +} + +void CExtension::FreeDependencyIterator(ITERATOR *iter) +{ + List::iterator *pIter = (List::iterator *)iter; + + delete pIter; +} + +void CExtension::AddInterface(SMInterface *pInterface) +{ + m_Interfaces.push_back(pInterface); +} + +bool CExtension::IsRunning(char *error, size_t maxlength) +{ + if (!IsLoaded()) + { + if (error) + { + snprintf(error, maxlength, "%s", m_Error.c_str()); + } + return false; + } + + return m_pAPI->QueryRunning(error, maxlength); +} + +void CExtension::AddLibrary(const char *library) +{ + m_Libraries.push_back(library); +} + +/********************* + * EXTENSION MANAGER * + *********************/ + +void CExtensionManager::OnSourceModAllInitialized() +{ + g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); + g_PluginSys.AddPluginsListener(this); + g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this); + g_ShareSys.AddInterface(NULL, this); +} + +void CExtensionManager::OnSourceModShutdown() +{ + g_RootMenu.RemoveRootConsoleCommand("exts", this); + g_ShareSys.DestroyIdentType(g_ExtType); +} + +void CExtensionManager::Shutdown() +{ + List::iterator iter; + + while ((iter = m_Libs.begin()) != m_Libs.end()) + { + UnloadExtension((*iter)); + } +} + +void CExtensionManager::TryAutoload() +{ + char path[PLATFORM_MAX_PATH]; + + g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions"); + + IDirectory *pDir = g_LibSys.OpenDirectory(path); + if (!pDir) + { + return; + } + + const char *lfile; + size_t len; + while (pDir->MoreFiles()) + { + if (pDir->IsEntryDirectory()) + { + pDir->NextEntry(); + continue; + } + + lfile = pDir->GetEntryName(); + len = strlen(lfile); + if (len <= 9) /* size of ".autoload" */ + { + pDir->NextEntry(); + continue; + } + + if (strcmp(&lfile[len - 9], ".autoload") != 0) + { + pDir->NextEntry(); + continue; + } + + char file[PLATFORM_MAX_PATH]; + len = UTIL_Format(file, sizeof(file), "%s", lfile); + strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT); + + LoadAutoExtension(file); + + pDir->NextEntry(); + } +} + +IExtension *CExtensionManager::LoadAutoExtension(const char *path) +{ + if (!strstr(path, "." PLATFORM_LIB_EXT)) + { + char newpath[PLATFORM_MAX_PATH]; + g_LibSys.PathFormat(newpath, sizeof(newpath), "%s.%s", path, PLATFORM_LIB_EXT); + return LoadAutoExtension(newpath); + } + + IExtension *pAlready; + if ((pAlready=FindExtensionByFile(path)) != NULL) + { + return pAlready; + } + + char error[256]; + CExtension *p = new CLocalExtension(path); + + /* We put us in the list beforehand so extensions that check for each other + * won't recursively load each other. + */ + m_Libs.push_back(p); + + if (!p->Load(error, sizeof(error)) || !p->IsLoaded()) + { + g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error); + p->SetError(error); + } + + return p; +} + +IExtension *CExtensionManager::FindExtensionByFile(const char *file) +{ + List::iterator iter; + CExtension *pExt; + + /* Chomp off the path */ + char lookup[PLATFORM_MAX_PATH]; + g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file); + + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) + { + pExt = (*iter); + char short_file[PLATFORM_MAX_PATH]; + g_LibSys.GetFileFromPath(short_file, sizeof(short_file), pExt->GetFilename()); + if (strcmp(lookup, short_file) == 0) + { + return pExt; + } + } + + /* If we got no results, test if there was a platform extension. + * If not, add one. + */ + if (!strstr(file, "." PLATFORM_LIB_EXT)) + { + char path[PLATFORM_MAX_PATH]; + UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT); + return FindExtensionByFile(path); + } + + return NULL; +} + +IExtension *CExtensionManager::FindExtensionByName(const char *ext) +{ + List::iterator iter; + CExtension *pExt; + IExtensionInterface *pAPI; + const char *name; + + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) + { + pExt = (*iter); + if (!pExt->IsLoaded()) + { + continue; + } + if ((pAPI = pExt->GetAPI()) == NULL) + { + continue; + } + name = pAPI->GetExtensionName(); + if (!name) + { + continue; + } + if (strcmp(name, ext) == 0) + { + return pExt; + } + } + + return NULL; +} + +IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength) +{ + IExtension *pAlready; + if ((pAlready=FindExtensionByFile(file)) != NULL) + { + return pAlready; + } + + CExtension *pExt = new CLocalExtension(file); + + if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) + { + pExt->Unload(); + delete pExt; + return NULL; + } + + /* :TODO: do QueryRunning check if the map is loaded */ + + m_Libs.push_back(pExt); + + return pExt; +} + +void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo) +{ + CExtension *pExt = (CExtension *)pRequester; + CExtension *pOwner = (CExtension *)pInfo->owner; + + pExt->AddDependency(pInfo); + + IExtensionInterface *pAPI = pExt->GetAPI(); + if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface)) + { + pOwner->AddChildDependent(pExt, pInfo->iface); + } +} + +void CExtensionManager::AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface) +{ + CExtension *pExt = (CExtension *)ext; + CExtension *pOwner = GetExtensionFromIdent(other); + + IfaceInfo info; + + info.iface = (SMInterface *)iface; + info.owner = pOwner; + + pExt->AddDependency(&info); + pOwner->AddChildDependent(pExt, (SMInterface *)iface); +} + +void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface) +{ + CExtension *pExt = (CExtension *)pOwner; + + pExt->AddInterface(pInterface); +} + +void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin ) +{ + CExtension *pExt = (CExtension *)pParent; + + pExt->AddPlugin(pPlugin); +} + +void CExtensionManager::OnPluginDestroyed(IPlugin *plugin) +{ + List::iterator iter; + + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) + { + (*iter)->DropRefsTo((CPlugin *)plugin); + } +} + +CExtension *CExtensionManager::FindByOrder(unsigned int num) +{ + if (num < 1 || num > m_Libs.size()) + { + return NULL; + } + + List::iterator iter = m_Libs.begin(); + + while (iter != m_Libs.end()) + { + if (--num == 0) + { + return (*iter); + } + iter++; + } + + return NULL; +} + +bool CExtensionManager::UnloadExtension(IExtension *_pExt) +{ + if (!_pExt) + { + return false; + } + + CExtension *pExt = (CExtension *)_pExt; + + if (m_Libs.find(pExt) == m_Libs.end()) + { + return false; + } + + /* First remove us from internal lists */ + g_ShareSys.RemoveInterfaces(_pExt); + m_Libs.remove(pExt); + + List UnloadQueue; + + /* Handle dependencies */ + if (pExt->IsLoaded()) + { + /* Unload any dependent plugins */ + List::iterator p_iter = pExt->m_Dependents.begin(); + while (p_iter != pExt->m_Dependents.end()) + { + /* We have to manually unlink ourselves here, since we're no longer being managed */ + g_PluginSys.UnloadPlugin((*p_iter)); + p_iter = pExt->m_Dependents.erase(p_iter); + } + + List::iterator s_iter; + for (s_iter = pExt->m_Libraries.begin(); + s_iter != pExt->m_Libraries.end(); + s_iter++) + { + g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true); + } + + /* Notify and/or unload all dependencies */ + List::iterator c_iter; + CExtension *pDep; + IExtensionInterface *pAPI; + for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++) + { + pDep = (*c_iter); + if ((pAPI=pDep->GetAPI()) == NULL) + { + continue; + } + if (pDep == pExt) + { + continue; + } + /* Now, get its dependency list */ + bool dropped = false; + List::iterator i_iter = pDep->m_Deps.begin(); + while (i_iter != pDep->m_Deps.end()) + { + if ((*i_iter).owner == _pExt) + { + if (!pAPI->QueryInterfaceDrop((*i_iter).iface)) + { + if (!dropped) + { + dropped = true; + UnloadQueue.push_back(pDep); + } + } + pAPI->NotifyInterfaceDrop((*i_iter).iface); + i_iter = pDep->m_Deps.erase(i_iter); + } + else + { + i_iter++; + } + } + /* Flush out any back references to this plugin */ + i_iter = pDep->m_ChildDeps.begin(); + while (i_iter != pDep->m_ChildDeps.end()) + { + if ((*i_iter).owner == pExt) + { + i_iter = pDep->m_ChildDeps.erase(i_iter); + } + else + { + i_iter++; + } + } + } + + /* Unbind our natives from Core */ + pExt->DropEverything(); + } + + IdentityToken_t *pIdentity; + if ((pIdentity = pExt->GetIdentity()) != NULL) + { + SMGlobalClass *glob = SMGlobalClass::head; + while (glob) + { + glob->OnSourceModIdentityDropped(pIdentity); + glob = glob->m_pGlobalClassNext; + } + } + + /* Tell it to unload */ + if (pExt->IsLoaded()) + { + IExtensionInterface *pAPI = pExt->GetAPI(); + pAPI->OnExtensionUnload(); + } + + pExt->Unload(); + delete pExt; + + List::iterator iter; + for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++) + { + /* NOTE: This is safe because the unload function backs out of anything not present */ + UnloadExtension((*iter)); + } + + return true; +} + +void CExtensionManager::MarkAllLoaded() +{ + List::iterator iter; + CExtension *pExt; + + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) + { + pExt = (*iter); + if (!pExt->IsLoaded()) + { + continue; + } + if (pExt->m_bFullyLoaded) + { + continue; + } + pExt->MarkAllLoaded(); + } +} + +void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload) +{ + /* This function doesn't really need to do anything now. We make sure the + * other extension is loaded, but handling of dependencies is really done + * by the interface fetcher. + */ + if (required || autoload) + { + LoadAutoExtension(file); + } +} + +void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) +{ + int argcount = command.ArgC(); + if (argcount >= 3) + { + const char *cmd = command.Arg(2); + if (strcmp(cmd, "list") == 0) + { + List::iterator iter; + CExtension *pExt; + unsigned int num = 1; + switch (m_Libs.size()) + { + case 1: + { + g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:"); + break; + } + case 0: + { + g_RootMenu.ConsolePrint("[SM] No extensions are loaded."); + break; + } + default: + { + g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size()); + break; + } + } + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++) + { + pExt = (*iter); + if (pExt->IsLoaded()) + { + char error[255]; + if (!pExt->IsRunning(error, sizeof(error))) + { + g_RootMenu.ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), error); + } + else + { + IExtensionInterface *pAPI = pExt->GetAPI(); + const char *name = pAPI->GetExtensionName(); + const char *version = pAPI->GetExtensionVerString(); + const char *descr = pAPI->GetExtensionDescription(); + g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr); + } + } else { + g_RootMenu.ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str()); + } + } + return; + } + else if (strcmp(cmd, "info") == 0) + { + if (argcount < 4) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>"); + return; + } + + const char *sId = command.Arg(3); + unsigned int id = atoi(sId); + if (id <= 0) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>"); + return; + } + + if (m_Libs.size() == 0) + { + g_RootMenu.ConsolePrint("[SM] No extensions are loaded."); + return; + } + + if (id > m_Libs.size()) + { + g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id); + return; + } + + List::iterator iter = m_Libs.begin(); + CExtension *pExt = NULL; + while (iter != m_Libs.end()) + { + if (--id == 0) + { + pExt = (*iter); + break; + } + iter++; + } + /* This should never happen */ + if (!pExt) + { + g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id); + return; + } + + if (!pExt->IsLoaded()) + { + g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); + g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str()); + } + else + { + char error[255]; + if (!pExt->IsRunning(error, sizeof(error))) + { + g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); + g_RootMenu.ConsolePrint(" Loaded: Yes"); + g_RootMenu.ConsolePrint(" Running: No (%s)", error); + } + else + { + IExtensionInterface *pAPI = pExt->GetAPI(); + g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); + g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString()); + g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription()); + g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL()); + g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString()); + + if (pExt->IsExternal()) + { + g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod"); + } + else if (pAPI->IsMetamodExtension()) + { + g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source"); + } + else + { + g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod"); + } + } + } + return; + } + else if (strcmp(cmd, "unload") == 0) + { + if (argcount < 4) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]"); + return; + } + + const char *arg = command.Arg(3); + unsigned int num = atoi(arg); + CExtension *pExt = FindByOrder(num); + + if (!pExt) + { + g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num); + return; + } + + if (argcount > 4 && pExt->unload_code) + { + const char *unload = command.Arg(4); + if (pExt->unload_code == (unsigned)atoi(unload)) + { + char filename[PLATFORM_MAX_PATH]; + snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); + UnloadExtension(pExt); + g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename); + } + else + { + g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code); + } + return; + } + + if (!pExt->IsLoaded() + || (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size())) + { + char filename[PLATFORM_MAX_PATH]; + snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); + UnloadExtension(pExt); + g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename); + return; + } + else + { + List plugins; + if (pExt->m_ChildDeps.size()) + { + g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename()); + List::iterator iter; + CExtension *pOther; + /* Get list of all extensions */ + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) + { + List::iterator i_iter; + pOther = (*iter); + if (!pOther->IsLoaded() || pOther == pExt) + { + continue; + } + /* Get their dependencies */ + for (i_iter=pOther->m_Deps.begin(); + i_iter!=pOther->m_Deps.end(); + i_iter++) + { + /* Is this dependency to us? */ + if ((*i_iter).owner != pExt) + { + continue; + } + /* Will our dependent care? */ + if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface)) + { + g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename()); + /* Add to plugin unload list */ + List::iterator p_iter; + for (p_iter=pOther->m_Dependents.begin(); + p_iter!=pOther->m_Dependents.end(); + p_iter++) + { + if (plugins.find((*p_iter)) == plugins.end()) + { + plugins.push_back((*p_iter)); + } + } + } + } + } + } + if (pExt->m_Dependents.size()) + { + g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename()); + List::iterator iter; + CPlugin *pPlugin; + for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++) + { + pPlugin = (*iter); + if (plugins.find(pPlugin) == plugins.end()) + { + plugins.push_back(pPlugin); + } + } + for (iter = plugins.begin(); iter != plugins.end(); iter++) + { + pPlugin = (*iter); + g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename()); + } + } + srand(static_cast(time(NULL))); + pExt->unload_code = (rand() % 877) + 123; //123 to 999 + g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename()); + g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code); + + return; + } + } + } + + g_RootMenu.ConsolePrint("SourceMod Extensions Menu:"); + g_RootMenu.DrawGenericOption("info", "Extra extension information"); + g_RootMenu.DrawGenericOption("list", "List extensions"); + g_RootMenu.DrawGenericOption("unload", "Unload an extension"); +} + +CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr) +{ + if (ptr->type == g_ExtType) + { + return (CExtension *)(ptr->ptr); + } + + return NULL; +} + +CExtensionManager::CExtensionManager() +{ +} + +CExtensionManager::~CExtensionManager() +{ +} + +void CExtensionManager::AddLibrary(IExtension *pSource, const char *library) +{ + CExtension *pExt = (CExtension *)pSource; + pExt->AddLibrary(library); + g_PluginSys.OnLibraryAction(library, false, false); +} + +bool CExtensionManager::LibraryExists(const char *library) +{ + CExtension *pExt; + + for (List::iterator iter = m_Libs.begin(); + iter != m_Libs.end(); + iter++) + { + pExt = (*iter); + for (List::iterator s_iter = pExt->m_Libraries.begin(); + s_iter != pExt->m_Libraries.end(); + s_iter++) + { + if ((*s_iter).compare(library) == 0) + { + return true; + } + } + } + + return false; +} + +IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface, + const char *filepath, + const char *filename, + char *error, + size_t maxlength) +{ + IExtension *pAlready; + if ((pAlready=FindExtensionByFile(filename)) != NULL) + { + return pAlready; + } + + CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath); + + if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) + { + pExt->Unload(); + delete pExt; + return NULL; + } + + m_Libs.push_back(pExt); + + return pExt; +} + +void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) +{ + IExtensionInterface *pAPI; + List::iterator iter; + + for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) + { + if ((pAPI = (*iter)->GetAPI()) == NULL) + { + continue; + } + if (pAPI->GetExtensionVersion() > 3) + { + pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax); + } + } +} diff -r 9981b8ecf935 -r 31f49899c0ae core/ExtensionSys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ExtensionSys.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,186 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_EXTENSION_SYSTEM_H_ +#define _INCLUDE_SOURCEMOD_EXTENSION_SYSTEM_H_ + +#include +#include +#include +#include +#include +#include "sm_globals.h" +#include "ShareSys.h" +#include +#include +#include +#include "PluginSys.h" +#include "NativeOwner.h" + +using namespace SourceMod; +using namespace SourceHook; + +class CExtension; + +struct sm_extnative_t +{ + CExtension *owner; + const sp_nativeinfo_t *info; +}; + +class CExtension : + public IExtension, + public CNativeOwner +{ + friend class CExtensionManager; +public: + virtual ~CExtension(); +public: //IExtension + IExtensionInterface *GetAPI(); + const char *GetFilename(); + IdentityToken_t *GetIdentity(); + ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface); + bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface); + void FreeDependencyIterator(ITERATOR *iter); + bool IsRunning(char *error, size_t maxlength); +public: + void SetError(const char *error); + void AddDependency(const IfaceInfo *pInfo); + void AddChildDependent(CExtension *pOther, SMInterface *iface); + void AddInterface(SMInterface *pInterface); + void AddPlugin(CPlugin *pPlugin); + void MarkAllLoaded(); + void AddLibrary(const char *library); +public: + virtual bool Load(char *error, size_t maxlength); + virtual bool IsLoaded() =0; + virtual void Unload() =0; +protected: + void Initialize(const char *filename, const char *path); + bool PerformAPICheck(char *error, size_t maxlength); + void CreateIdentity(); + void DestroyIdentity(); +protected: + IdentityToken_t *m_pIdentToken; + IExtensionInterface *m_pAPI; + String m_File; + String m_Path; + String m_Error; + List m_Deps; /** Dependencies */ + List m_ChildDeps; /** Children who might depend on us */ + List m_Interfaces; + List m_Libraries; + unsigned int unload_code; + bool m_bFullyLoaded; +}; + +class CLocalExtension : public CExtension +{ +public: + CLocalExtension(const char *filename); +public: + bool Load(char *error, size_t maxlength); + bool IsLoaded(); + void Unload(); + bool IsExternal(); +private: + PluginId m_PlId; + ILibrary *m_pLib; +}; + +class CRemoteExtension : public CExtension +{ +public: + CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path); +public: + bool Load(char *error, size_t maxlength); + bool IsLoaded(); + void Unload(); + bool IsExternal(); +}; + +class CExtensionManager : + public IExtensionManager, + public SMGlobalClass, + public IPluginsListener, + public IRootConsoleCommand +{ +public: + CExtensionManager(); + ~CExtensionManager(); +public: //SMGlobalClass + void OnSourceModAllInitialized(); + void OnSourceModShutdown(); +public: //IExtensionManager + IExtension *LoadExtension(const char *path, + char *error, + size_t maxlength); + bool UnloadExtension(IExtension *pExt); + IExtension *FindExtensionByFile(const char *file); + IExtension *FindExtensionByName(const char *ext); + IExtension *LoadExternal(IExtensionInterface *pInterface, + const char *filepath, + const char *filename, + char *error, + size_t maxlength); +public: //IPluginsListener + void OnPluginDestroyed(IPlugin *plugin); +public: //IRootConsoleCommand + void OnRootConsoleCommand(const char *cmdname, const CCommand &command); +public: + IExtension *LoadAutoExtension(const char *path); + void BindDependency(IExtension *pOwner, IfaceInfo *pInfo); + void AddInterface(IExtension *pOwner, SMInterface *pInterface); + void BindChildPlugin(IExtension *pParent, CPlugin *pPlugin); + void MarkAllLoaded(); + void AddDependency(IExtension *pSource, const char *file, bool required, bool autoload); + void TryAutoload(); + void AddLibrary(IExtension *pSource, const char *library); + bool LibraryExists(const char *library); + void CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax); + void AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface); +public: + CExtension *GetExtensionFromIdent(IdentityToken_t *ptr); + void Shutdown(); + CNativeOwner *GetNativeOwner(IExtension *pExt) + { + CExtension *p = (CExtension *)pExt; + return p; + } +private: + CExtension *FindByOrder(unsigned int num); +private: + List m_Libs; +}; + +extern CExtensionManager g_Extensions; + +#endif //_INCLUDE_SOURCEMOD_EXTENSION_SYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/ForwardSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ForwardSys.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,751 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include "ForwardSys.h" +#include "PluginSys.h" +#include "ShareSys.h" +#include "DebugReporter.h" + +CForwardManager g_Forwards; + +/** + * Genesis turns to its source, reduction occurs stepwise although the essence is all one. + * End of line. FTL system check. + * + * :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST + * VARARG FUNCTIONS: + * - Pushing no varargs + */ + +// :TODO: IMPORTANT!!! The result pointer arg in the execute function maybe invalid if the forward fails +// so later evaluation of this result may cause problems on higher levels of abstraction. DOCUMENT OR FIX ALL FORWARDS! + +CForwardManager::~CForwardManager() +{ + CStack::iterator iter; + for (iter=m_FreeForwards.begin(); iter!=m_FreeForwards.end(); iter++) + { + delete (*iter); + } + m_FreeForwards.popall(); +} + +void CForwardManager::OnSourceModAllInitialized() +{ + g_PluginSys.AddPluginsListener(this); + g_ShareSys.AddInterface(NULL, this); +} + +IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, ...) +{ + CForward *fwd; + va_list ap; + va_start(ap, types); + + fwd = CForward::CreateForward(name, et, num_params, types, ap); + + va_end(ap); + + if (fwd) + { + g_PluginSys.AddFunctionsToForward(name, fwd); + + m_managed.push_back(fwd); + } + + return fwd; +} + +IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType et, int num_params, const ParamType *types, ...) +{ + CForward *fwd; + va_list ap; + va_start(ap, types); + + fwd = CForward::CreateForward(name, et, num_params, types, ap); + + va_end(ap); + + if (fwd) + { + m_unmanaged.push_back(fwd); + } + + return fwd; +} + +void CForwardManager::OnPluginLoaded(IPlugin *plugin) +{ + /* Attach any globally managed forwards */ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName()); + if (pFunc) + { + fwd->AddFunction(pFunc); + } + } +} + +void CForwardManager::OnPluginUnloaded(IPlugin *plugin) +{ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + fwd->RemoveFunctionsOfPlugin(plugin); + } + + for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++) + { + fwd = (*iter); + fwd->RemoveFunctionsOfPlugin(plugin); + } +} + +IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng) +{ + List::iterator iter; + CForward *fwd; + + for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) + { + fwd = (*iter); + if (strcmp(fwd->GetForwardName(), name) == 0) + { + if (ifchng) + { + *ifchng = NULL; + } + return fwd; + } + } + + for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++) + { + fwd = (*iter); + if (strcmp(fwd->GetForwardName(), name) == 0) + { + if (ifchng) + { + *ifchng = fwd; + } + return fwd; + } + } + + if (ifchng) + { + *ifchng = NULL; + } + + return NULL; +} + +void CForwardManager::ReleaseForward(IForward *forward) +{ + ForwardFree(static_cast(forward)); +} + +void CForwardManager::ForwardFree(CForward *fwd) +{ + if (fwd == NULL) + { + return; + } + + m_FreeForwards.push(fwd); + m_managed.remove(fwd); +} + +CForward *CForwardManager::ForwardMake() +{ + CForward *fwd; + if (m_FreeForwards.empty()) + { + fwd = new CForward; + } else { + fwd = m_FreeForwards.front(); + m_FreeForwards.pop(); + } + return fwd; +} + +void CForwardManager::OnPluginPauseChange(IPlugin *plugin, bool paused) +{ + /* No longer used */ +} + +/************************************* + * ACTUAL FORWARD API IMPLEMENTATION * + *************************************/ + +CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, va_list ap) +{ + ParamType _types[SP_MAX_EXEC_PARAMS]; + + if (num_params > SP_MAX_EXEC_PARAMS) + { + return NULL; + } + + if (types == NULL && num_params) + { + for (unsigned int i=0; im_curparam = 0; + pForward->m_ExecType = et; + snprintf(pForward->m_name, FORWARDS_NAME_MAX, "%s", name ? name : ""); + + for (unsigned int i=0; im_types[i] = _types[i]; + } + + if (num_params && _types[num_params-1] == Param_VarArgs) + { + pForward->m_varargs = num_params--; + } else { + pForward->m_varargs = false; + } + + pForward->m_numparams = num_params; + pForward->m_errstate = SP_ERROR_NONE; + + pForward->m_functions.clear(); + + return pForward; +} + +int CForward::Execute(cell_t *result, IForwardFilter *filter) +{ + if (m_errstate) + { + int err = m_errstate; + Cancel(); + return err; + } + + FuncIter iter = m_functions.begin(); + IPluginFunction *func; + cell_t cur_result = 0; + cell_t high_result = 0; + cell_t low_result = 0; + int err; + unsigned int failed=0, success=0; + unsigned int num_params = m_curparam; + FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS]; + FwdParamInfo *param; + ParamType type; + + /* Save local, reset */ + memcpy(temp_info, m_params, sizeof(m_params)); + m_curparam = 0; + + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + func = (*iter); + + for (unsigned int i=0; i= m_numparams || m_types[i] == Param_Any) + { + type = param->pushedas; + } + else + { + type = m_types[i]; + } + + if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF)) + { + /* If we're byref or we're vararg, we always push everything by ref. + * Even if they're byval, we must push them byref. + */ + if (type == Param_String) + { + err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags); + } + else if (type == Param_Float || type == Param_Cell) + { + err = func->PushCellByRef(¶m->val); + } + else + { + err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); + assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef); + } + } + else + { + /* If we're not byref or not vararg, our job is a bit easier. */ + assert(type == Param_Cell || type == Param_Float); + err = func->PushCell(param->val); + } + + if (err != SP_ERROR_NONE) + { + g_DbgReporter.GenerateError(func->GetParentContext(), + func->GetFunctionID(), + err, + "Failed to push parameter while executing forward"); + continue; + } + } + + /* Call the function and deal with the return value. */ + if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE) + { + failed++; + } + else + { + success++; + switch (m_ExecType) + { + case ET_Event: + { + if (cur_result > high_result) + { + high_result = cur_result; + } + break; + } + case ET_Hook: + { + if (cur_result > high_result) + { + high_result = cur_result; + if ((ResultType)high_result == Pl_Stop) + { + goto done; + } + } + break; + } + case ET_LowEvent: + { + /* Check if the current result is the lowest so far (or if it's the first result) */ + if (cur_result < low_result || success == 1) + { + low_result = cur_result; + } + break; + } + default: + { + break; + } + } + } + } + +done: + + if (success) + { + switch (m_ExecType) + { + case ET_Ignore: + { + cur_result = 0; + break; + } + case ET_Event: + case ET_Hook: + { + cur_result = high_result; + break; + } + case ET_LowEvent: + { + cur_result = low_result; + break; + } + default: + { + break; + } + } + + if (result) + { + *result = cur_result; + } + } + + return SP_ERROR_NONE; +} + +int CForward::PushCell(cell_t cell) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_Cell; + } else if (m_types[m_curparam] != Param_Cell) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_Cell; + } + + m_params[m_curparam++].val = cell; + + return SP_ERROR_NONE; +} + +int CForward::PushFloat(float number) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_Float; + } else if (m_types[m_curparam] != Param_Float) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_Float; + } + + m_params[m_curparam++].val = *(cell_t *)&number; + + return SP_ERROR_NONE; +} + +int CForward::PushCellByRef(cell_t *cell, int flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_CellByRef; + } else if (m_types[m_curparam] != Param_CellByRef) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_CellByRef; + } + + _Int_PushArray(cell, 1, flags); + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushFloatByRef(float *num, int flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_FloatByRef; + } else if (m_types[m_curparam] != Param_FloatByRef) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_FloatByRef; + } + + _Int_PushArray((cell_t *)num, 1, flags); + m_curparam++; + + return SP_ERROR_NONE; +} + +void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags) +{ + m_params[m_curparam].byref.cells = cells; + m_params[m_curparam].byref.flags = flags; + m_params[m_curparam].byref.orig_addr = inarray; +} + +int CForward::PushArray(cell_t *inarray, unsigned int cells, int flags) +{ + /* We don't allow this here */ + if (!inarray) + { + return SetError(SP_ERROR_PARAM); + } + + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_Array; + } else if (m_types[m_curparam] != Param_Array) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_Array; + } + + _Int_PushArray(inarray, cells, flags); + + m_curparam++; + + return SP_ERROR_NONE; +} + +void CForward::_Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags) +{ + m_params[m_curparam].byref.cells = cells; /* Notice this contains the char count not cell count */ + m_params[m_curparam].byref.flags = cp_flags; + m_params[m_curparam].byref.orig_addr = inarray; + m_params[m_curparam].byref.sz_flags = sz_flags; +} + +int CForward::PushString(const char *string) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_String; + } else if (m_types[m_curparam] != Param_String) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_String; + } + + _Int_PushString((cell_t *)string, strlen(string)+1, SM_PARAM_STRING_COPY, 0); + m_curparam++; + + return SP_ERROR_NONE; +} + +int CForward::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags) +{ + if (m_curparam < m_numparams) + { + if (m_types[m_curparam] == Param_Any) + { + m_params[m_curparam].pushedas = Param_String; + } else if (m_types[m_curparam] != Param_String) { + return SetError(SP_ERROR_PARAM); + } + } else { + if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) + { + return SetError(SP_ERROR_PARAMS_MAX); + } + m_params[m_curparam].pushedas = Param_String; + } + + _Int_PushString((cell_t *)buffer, length, sz_flags, cp_flags); + m_curparam++; + + return SP_ERROR_NONE; +} + +void CForward::Cancel() +{ + if (!m_curparam) + { + return; + } + + m_curparam = 0; + m_errstate = SP_ERROR_NONE; +} + +bool CForward::AddFunction(IPluginContext *pContext, funcid_t index) +{ + IPluginFunction *pFunc = pContext->GetFunctionById(index); + + if (!pFunc) + { + return false; + } + + return AddFunction(pFunc); +} + +bool CForward::RemoveFunction(IPluginContext *pContext, funcid_t index) +{ + IPluginFunction *pFunc = pContext->GetFunctionById(index); + + if (!pFunc) + { + return false; + } + + return RemoveFunction(pFunc); +} + +bool CForward::RemoveFunction(IPluginFunction *func) +{ + bool found = false; + FuncIter iter; + List *lst; + + if (func->IsRunnable()) + { + lst = &m_functions; + } else { + lst = &m_paused; + } + + for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) + { + if ((*iter) == func) + { + found = true; + lst->erase(iter); + break; + } + } + + /* Cancel a call, if any */ + if (found || m_curparam) + { + func->Cancel(); + } + + return found; +} + +unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin) +{ + FuncIter iter; + IPluginFunction *func; + unsigned int removed = 0; + IPluginContext *pContext = plugin->GetBaseContext(); + for (iter=m_functions.begin(); iter!=m_functions.end();) + { + func = (*iter); + if (func->GetParentContext() == pContext) + { + iter = m_functions.erase(iter); + removed++; + } else { + iter++; + } + } + + return removed; +} + +bool CForward::AddFunction(IPluginFunction *func) +{ + if (m_curparam) + { + return false; + } + + if (func->IsRunnable()) + { + m_functions.push_back(func); + } else { + m_paused.push_back(func); + } + + return true; +} + +const char *CForward::GetForwardName() +{ + return m_name; +} + +unsigned int CForward::GetFunctionCount() +{ + return m_functions.size(); +} + +ExecType CForward::GetExecType() +{ + return m_ExecType; +} diff -r 9981b8ecf935 -r 31f49899c0ae core/ForwardSys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ForwardSys.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,158 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ +#define _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ + +#include +#include +#include "sm_globals.h" +#include +#include +#include "sourcemod.h" + +using namespace SourceHook; + +typedef List::iterator FuncIter; + +/* :TODO: a global name max define for sourcepawn, should mirror compiler's sNAMEMAX */ +#define FORWARDS_NAME_MAX 64 + +struct ByrefInfo +{ + unsigned int cells; + cell_t *orig_addr; + int flags; + int sz_flags; +}; + +struct FwdParamInfo +{ + cell_t val; + ByrefInfo byref; + ParamType pushedas; +}; + +class CForward : public IChangeableForward +{ +public: //ICallable + virtual int PushCell(cell_t cell); + virtual int PushCellByRef(cell_t *cell, int flags); + virtual int PushFloat(float number); + virtual int PushFloatByRef(float *number, int flags); + virtual int PushArray(cell_t *inarray, unsigned int cells, int flags); + virtual int PushString(const char *string); + virtual int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags); + virtual void Cancel(); +public: //IForward + virtual const char *GetForwardName(); + virtual unsigned int GetFunctionCount(); + virtual ExecType GetExecType(); + virtual int Execute(cell_t *result, IForwardFilter *filter); +public: //IChangeableForward + virtual bool RemoveFunction(IPluginFunction *func); + virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin); + virtual bool AddFunction(IPluginFunction *func); + virtual bool AddFunction(IPluginContext *ctx, funcid_t index); + virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index); +public: + static CForward *CreateForward(const char *name, + ExecType et, + unsigned int num_params, + const ParamType *types, + va_list ap); +private: + void _Int_PushArray(cell_t *inarray, unsigned int cells, int flags); + void _Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags); + inline int SetError(int err) + { + m_errstate = err; + return err; + } +protected: + /* :TODO: I want a caching list type here. + * Destroying these things and using new/delete for their members feels bad. + */ + mutable List m_functions; + mutable List m_paused; + + /* Type and name information */ + FwdParamInfo m_params[SP_MAX_EXEC_PARAMS]; + ParamType m_types[SP_MAX_EXEC_PARAMS]; + char m_name[FORWARDS_NAME_MAX+1]; + unsigned int m_numparams; + unsigned int m_varargs; + ExecType m_ExecType; + + /* State information */ + unsigned int m_curparam; + int m_errstate; +}; + +class CForwardManager : + public IForwardManager, + public IPluginsListener, + public SMGlobalClass +{ + friend class CForward; +public: + ~CForwardManager(); +public: //IForwardManager + IForward *CreateForward(const char *name, + ExecType et, + unsigned int num_params, + const ParamType *types, + ...); + IChangeableForward *CreateForwardEx(const char *name, + ExecType et, + int num_params, + const ParamType *types, + ...); + IForward *FindForward(const char *name, IChangeableForward **ifchng); + void ReleaseForward(IForward *forward); +public: //IPluginsListener + void OnPluginLoaded(IPlugin *plugin); + void OnPluginUnloaded(IPlugin *plugin); + void OnPluginPauseChange(IPlugin *plugin, bool paused); +public: //SMGlobalClass + void OnSourceModAllInitialized(); +protected: + CForward *ForwardMake(); + void ForwardFree(CForward *fwd); +private: + CStack m_FreeForwards; + List m_managed; + List m_unmanaged; +}; + +extern CForwardManager g_Forwards; + +#endif //_INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/HandleSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/HandleSys.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,1076 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "HandleSys.h" +#include "ShareSys.h" +#include "PluginSys.h" +#include "ExtensionSys.h" +#include "Logger.h" +#include +#include +#include "sm_stringutil.h" + +HandleSystem g_HandleSys; + +QHandle *ignore_handle; + +inline HandleType_t TypeParent(HandleType_t type) +{ + return (type & ~HANDLESYS_SUBTYPE_MASK); +} + +inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index) +{ + return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index); +} + +HandleSystem::HandleSystem() +{ + m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1]; + memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1)); + + m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE]; + memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE); + + m_TypeLookup = sm_trie_create(); + m_strtab = new BaseStringTable(512); + + m_TypeTail = 0; +} + +HandleSystem::~HandleSystem() +{ + delete [] m_Handles; + delete [] m_Types; + sm_trie_destroy(m_TypeLookup); + delete m_strtab; +} + + +HandleType_t HandleSystem::CreateType(const char *name, + IHandleTypeDispatch *dispatch, + HandleType_t parent, + const TypeAccess *typeAccess, + const HandleAccess *hndlAccess, + IdentityToken_t *ident, + HandleError *err) +{ + if (!dispatch) + { + if (err) + { + *err = HandleError_Parameter; + } + return 0; + } + + if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) + { + if (err) + { + *err = HandleError_Version; + } + return 0; + } + + if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) + { + if (err) + { + *err = HandleError_Version; + } + return 0; + } + + bool isChild = false; + + if (parent != 0) + { + isChild = true; + if (parent & HANDLESYS_SUBTYPE_MASK) + { + if (err) + { + *err = HandleError_NoInherit; + } + return 0; + } + if (parent >= HANDLESYS_TYPEARRAY_SIZE + || m_Types[parent].dispatch == NULL) + { + if (err) + { + *err = HandleError_Parameter; + } + return 0; + } + if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false + && (m_Types[parent].typeSec.ident != ident)) + { + if (err) + { + *err = HandleError_Access; + } + return 0; + } + } + + if (name && name[0] != '\0') + { + if (sm_trie_retrieve(m_TypeLookup, name, NULL)) + { + if (err) + { + *err = HandleError_Parameter; + } + return 0; + } + } + + unsigned int index; + + if (isChild) + { + QHandleType *pParent = &m_Types[parent]; + if (pParent->children >= HANDLESYS_MAX_SUBTYPES) + { + if (err) + { + *err = HandleError_Limit; + } + return 0; + } + index = 0; + for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) + { + if (m_Types[parent + i].dispatch == NULL) + { + index = parent + i; + break; + } + } + if (!index) + { + if (err) + { + *err = HandleError_Limit; + } + return 0; + } + pParent->children++; + } else { + if (m_FreeTypes == 0) + { + /* Reserve another index */ + if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE) + { + if (err) + { + *err = HandleError_Limit; + } + return 0; + } else { + m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1); + index = m_TypeTail; + } + } else { + /* The "free array" is compacted into the normal array for easiness */ + index = m_Types[m_FreeTypes--].freeID; + } + } + + QHandleType *pType = &m_Types[index]; + + pType->dispatch = dispatch; + if (name && name[0] != '\0') + { + pType->nameIdx = m_strtab->AddString(name); + sm_trie_insert(m_TypeLookup, name, (void *)pType); + } else { + pType->nameIdx = -1; + } + + pType->opened = 0; + + if (typeAccess) + { + pType->typeSec = *typeAccess; + } else { + InitAccessDefaults(&pType->typeSec, NULL); + pType->typeSec.ident = ident; + } + + if (hndlAccess) + { + pType->hndlSec = *hndlAccess; + } else { + InitAccessDefaults(NULL, &pType->hndlSec); + } + + if (!isChild) + { + pType->children = 0; + } + + return index; +} + +bool HandleSystem::FindHandleType(const char *name, HandleType_t *type) +{ + QHandleType *_type; + + if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type)) + { + return false; + } + + unsigned int offset = _type - m_Types; + + if (type) + { + *type = offset; + } + + return true; +} + +HandleError HandleSystem::TryAllocHandle(unsigned int *handle) +{ + if (m_FreeHandles == 0) + { + if (m_HandleTail >= HANDLESYS_MAX_HANDLES) + { + return HandleError_Limit;; + } + *handle = ++m_HandleTail; + } + else + { + *handle = m_Handles[m_FreeHandles--].freeID; + } + + return HandleError_None; +} + +HandleError HandleSystem::MakePrimHandle(HandleType_t type, + QHandle **in_pHandle, + unsigned int *in_index, + Handle_t *in_handle, + IdentityToken_t *owner, + bool identity) +{ + HandleError err; + unsigned int owner_index = 0; + + if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None)) + { + return HandleError_Identity; + } + + unsigned int handle; + if ((err = TryAllocHandle(&handle)) != HandleError_None) + { + if (!TryAndFreeSomeHandles() + || (err = TryAllocHandle(&handle)) != HandleError_None) + { + return err; + } + } + + QHandle *pHandle = &m_Handles[handle]; + + assert(pHandle->set == false); + + if (++m_HSerial >= HANDLESYS_MAX_SERIALS) + { + m_HSerial = 1; + } + + /* Set essential information */ + pHandle->set = identity ? HandleSet_Identity : HandleSet_Used; + pHandle->refcount = 1; + pHandle->type = type; + pHandle->serial = m_HSerial; + pHandle->owner = owner; + pHandle->ch_next = 0; + pHandle->access_special = false; + pHandle->is_destroying = false; + + /* Create the hash value */ + Handle_t hash = pHandle->serial; + hash <<= 16; + hash |= handle; + + /* Add a reference count to the type */ + m_Types[type].opened++; + + /* Output */ + *in_pHandle = pHandle; + *in_index = handle; + *in_handle = hash; + + /* Decode the identity token + * For now, we don't allow nested ownership + */ + if (owner && !identity) + { + QHandle *pIdentity = &m_Handles[owner_index]; + if (pIdentity->ch_prev == 0) + { + pIdentity->ch_prev = handle; + pIdentity->ch_next = handle; + pHandle->ch_prev = 0; + } + else + { + /* Link previous node to us (forward) */ + m_Handles[pIdentity->ch_next].ch_next = handle; + /* Link us to previous node (backwards) */ + pHandle->ch_prev = pIdentity->ch_next; + /* Set new tail */ + pIdentity->ch_next = handle; + } + pIdentity->refcount++; + } + else + { + pHandle->ch_prev = 0; + } + + return HandleError_None; +} + +void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken) +{ + if (!type + || type >= HANDLESYS_TYPEARRAY_SIZE + || m_Types[type].dispatch == NULL) + { + return; + } + + m_Types[type].typeSec.ident = pToken; +} + +Handle_t HandleSystem::CreateHandleInt(HandleType_t type, + void *object, + const HandleSecurity *pSec, + HandleError *err, + const HandleAccess *pAccess, + bool identity) +{ + IdentityToken_t *ident; + IdentityToken_t *owner; + + if (pSec) + { + ident = pSec->pIdentity; + owner = pSec->pOwner; + } else { + ident = NULL; + owner = NULL; + } + + if (!type + || type >= HANDLESYS_TYPEARRAY_SIZE + || m_Types[type].dispatch == NULL) + { + if (err) + { + *err = HandleError_Parameter; + } + return 0; + } + + /* Check to see if we're allowed to create this handle type */ + QHandleType *pType = &m_Types[type]; + if (!pType->typeSec.access[HTypeAccess_Create] + && (!pType->typeSec.ident + || pType->typeSec.ident != ident)) + { + if (err) + { + *err = HandleError_Access; + } + return 0; + } + + unsigned int index; + Handle_t handle; + QHandle *pHandle; + HandleError _err; + + if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None) + { + if (err) + { + *err = _err; + } + return 0; + } + + if (pAccess) + { + pHandle->access_special = true; + pHandle->sec = *pAccess; + } + + pHandle->object = object; + pHandle->clone = 0; + + return handle; +} + +Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err) +{ + return CreateHandleInt(type, object, pSec, err, pAccess, false); +} + +Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err) +{ + HandleSecurity sec; + + sec.pIdentity = ident; + sec.pOwner = owner; + + return CreateHandleEx(type, object, &sec, NULL, err); +} + +bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype) +{ + /* Check the type inheritance */ + if (intype & HANDLESYS_SUBTYPE_MASK) + { + if (intype != outtype + && (TypeParent(intype) != TypeParent(outtype))) + { + return false; + } + } else { + if (intype != outtype) + { + return false; + } + } + + return true; +} + +HandleError HandleSystem::GetHandle(Handle_t handle, + IdentityToken_t *ident, + QHandle **in_pHandle, + unsigned int *in_index, + bool ignoreFree) +{ + unsigned int serial = (handle >> 16); + unsigned int index = (handle & HANDLESYS_HANDLE_MASK); + + if (index == 0 || index > m_HandleTail || index > HANDLESYS_MAX_HANDLES) + { + return HandleError_Index; + } + + QHandle *pHandle = &m_Handles[index]; + + if (!pHandle->set + || (pHandle->set == HandleSet_Freed && !ignoreFree)) + { + return HandleError_Freed; + } else if (pHandle->set == HandleSet_Identity + && ident != g_ShareSys.GetIdentRoot()) + { + /* Only IdentityHandle() can read this! */ + return HandleError_Identity; + } + if (pHandle->serial != serial) + { + return HandleError_Changed; + } + + *in_pHandle = pHandle; + *in_index = index; + + return HandleError_None; +} + +bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity) +{ + QHandleType *pType = &m_Types[pHandle->type]; + unsigned int access; + + if (pHandle->access_special) + { + access = pHandle->sec.access[right]; + } else { + access = pType->hndlSec.access[right]; + } + + /* Check if the type's identity matches */ + if (access & HANDLE_RESTRICT_IDENTITY) + { + IdentityToken_t *owner = pType->typeSec.ident; + if (!owner + || (!pSecurity || pSecurity->pIdentity != owner)) + { + return false; + } + } + + /* Check if the owner is allowed */ + if (access & HANDLE_RESTRICT_OWNER) + { + IdentityToken_t *owner = pHandle->owner; + if (owner + && (!pSecurity || pSecurity->pOwner != owner)) + { + return false; + } + } + + return true; +} + +HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner) +{ + /* Get a new Handle ID */ + unsigned int new_index; + QHandle *pNewHandle; + Handle_t new_handle; + HandleError err; + + if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None) + { + return err; + } + + /* Assign permissions from parent */ + if (pHandle->access_special) + { + pNewHandle->access_special = true; + pNewHandle->sec = pHandle->sec; + } + + pNewHandle->clone = index; + pHandle->refcount++; + + *newhandle = new_handle; + + return HandleError_None; +} + +HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity) +{ + HandleError err; + QHandle *pHandle; + unsigned int index; + IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; + + if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) + { + return err; + } + + /* Identities cannot be cloned */ + if (pHandle->set == HandleSet_Identity) + { + return HandleError_Identity; + } + + /* Check if the handle can be cloned */ + if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity)) + { + return HandleError_Access; + } + + /* Make sure we're not cloning a clone */ + if (pHandle->clone) + { + QHandle *pParent = &m_Handles[pHandle->clone]; + return CloneHandle(pParent, pHandle->clone, newhandle, newOwner); + } + + return CloneHandle(pHandle, index, newhandle, newOwner); +} + +HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index) +{ + QHandleType *pType = &m_Types[pHandle->type]; + + if (pHandle->clone) + { + /* If we're a clone, decrease the parent reference count */ + QHandle *pMaster; + unsigned int master; + + /* Note that if we ever have per-handle security, we would need to re-check + * the access on this Handle. */ + master = pHandle->clone; + pMaster = &m_Handles[master]; + + /* Release the clone now */ + ReleasePrimHandle(index); + + /* Decrement the master's reference count */ + if (--pMaster->refcount == 0) + { + /* Type should be the same but do this anyway... */ + pType = &m_Types[pMaster->type]; + pMaster->is_destroying = true; + pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object); + ReleasePrimHandle(master); + } + } else if (pHandle->set == HandleSet_Identity) { + /* If we're an identity, skip all this stuff! + * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR + */ + ReleasePrimHandle(index); + } else { + /* Decrement, free if necessary */ + if (--pHandle->refcount == 0) + { + pHandle->is_destroying = true; + pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); + ReleasePrimHandle(index); + } else { + /* We must be cloned, so mark ourselves as freed */ + pHandle->set = HandleSet_Freed; + /* Now, unlink us, so we're not being tracked by the owner */ + if (pHandle->owner) + { + UnlinkHandleFromOwner(pHandle, index); + } + } + } + + return HandleError_None; +} + +HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity) +{ + unsigned int index; + QHandle *pHandle; + HandleError err; + IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; + + if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) + { + return err; + } + + if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity)) + { + return HandleError_Access; + } + + if (pHandle->is_destroying) + { + /* Someone tried to free this recursively. + * We'll just ignore this safely. + */ + return HandleError_None; + } + + return FreeHandle(pHandle, index); +} + +HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object) +{ + unsigned int index; + QHandle *pHandle; + HandleError err; + IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; + + if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) + { + return err; + } + + if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity)) + { + return HandleError_Access; + } + + /* Check the type inheritance */ + if (pHandle->type & HANDLESYS_SUBTYPE_MASK) + { + if (pHandle->type != type + && (TypeParent(pHandle->type) != TypeParent(type))) + { + return HandleError_Type; + } + } else if (type) { + if (pHandle->type != type) + { + return HandleError_Type; + } + } + + if (object) + { + /* if we're a clone, the rules change - object is ONLY in our reference */ + if (pHandle->clone) + { + pHandle = &m_Handles[pHandle->clone]; + } + *object = pHandle->object; + } + + return HandleError_None; +} + +void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index) +{ + /* Unlink us if necessary */ + unsigned int ident_index; + if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None) + { + /* Uh-oh! */ + assert(pHandle->owner == 0); + return; + } + /* Note that since 0 is an invalid handle, if any of these links are 0, + * the data can still be set. + */ + QHandle *pIdentity = &m_Handles[ident_index]; + + /* Unlink case: We're the head AND tail node */ + if (index == pIdentity->ch_prev && index == pIdentity->ch_next) + { + pIdentity->ch_prev = 0; + pIdentity->ch_next = 0; + } + /* Unlink case: We're the head node */ + else if (index == pIdentity->ch_prev) { + /* Link us to the next in the chain */ + pIdentity->ch_prev = pHandle->ch_next; + /* Patch up the previous link */ + m_Handles[pHandle->ch_next].ch_prev = 0; + } + /* Unlink case: We're the tail node */ + else if (index == pIdentity->ch_next) { + /* Link us to the previous in the chain */ + pIdentity->ch_next = pHandle->ch_prev; + /* Patch up the next link */ + m_Handles[pHandle->ch_prev].ch_next = 0; + } + /* Unlink case: We're in the middle! */ + else { + /* Patch the forward reference */ + m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev; + /* Patch the backward reference */ + m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next; + } + + /* Lastly, decrease the reference count */ + pIdentity->refcount--; +} + +void HandleSystem::ReleasePrimHandle(unsigned int index) +{ + QHandle *pHandle = &m_Handles[index]; + HandleSet set = pHandle->set; + + if (pHandle->owner && (set != HandleSet_Identity)) + { + UnlinkHandleFromOwner(pHandle, index); + } + + /* Were we an identity ourself? */ + QHandle *pLocal; + if (set == HandleSet_Identity) + { + /* Extra work to do. We need to find everything connected to this identity and release it. */ + unsigned int ch_index; +#if defined _DEBUG + unsigned int old_index = 0; +#endif + while ((ch_index = pHandle->ch_next) != 0) + { + pLocal = &m_Handles[ch_index]; +#if defined _DEBUG + assert(old_index != ch_index); + assert(pLocal->set == HandleSet_Used); + old_index = ch_index; +#endif + FreeHandle(pLocal, ch_index); + } + } + + pHandle->set = HandleSet_None; + m_Types[pHandle->type].opened--; + m_Handles[++m_FreeHandles].freeID = index; +} + +bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident) +{ + if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE) + { + return false; + } + + QHandleType *pType = &m_Types[type]; + + if (pType->typeSec.ident + && pType->typeSec.ident != ident) + { + return false; + } + + if (pType->dispatch == NULL) + { + return false; + } + + /* Remove children if we have to */ + if (!(type & HANDLESYS_SUBTYPE_MASK)) + { + QHandleType *childType; + for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) + { + childType = &m_Types[type + i]; + if (childType->dispatch) + { + RemoveType(type + i, childType->typeSec.ident); + } + } + /* Link us into the free chain */ + m_Types[++m_FreeTypes].freeID = type; + } + + /* Invalidate the type now */ + IHandleTypeDispatch *dispatch = pType->dispatch; + pType->dispatch = NULL; + + /* Make sure nothing is using this type. */ + if (pType->opened) + { + QHandle *pHandle; + for (unsigned int i=1; i<=m_HandleTail; i++) + { + pHandle = &m_Handles[i]; + if (!pHandle->set || pHandle->type != type) + { + continue; + } + if (pHandle->clone) + { + /* Get parent */ + QHandle *pOther = &m_Handles[pHandle->clone]; + if (--pOther->refcount == 0) + { + /* Free! */ + dispatch->OnHandleDestroy(type, pOther->object); + ReleasePrimHandle(pHandle->clone); + } + /* Unlink ourselves since we don't have a reference count */ + ReleasePrimHandle(i); + } else { + /* If it's not a clone, we still have to check the reference count. + * Either way, we'll be destroyed eventually because the handle types do not change. + */ + if (--pHandle->refcount == 0) + { + /* Free! */ + dispatch->OnHandleDestroy(type, pHandle->object); + ReleasePrimHandle(i); + } + } + if (pType->opened == 0) + { + break; + } + } + } + + /* Remove it from the type cache. */ + if (pType->nameIdx != -1) + { + const char *typeName; + + typeName = m_strtab->GetString(pType->nameIdx); + sm_trie_delete(m_TypeLookup, typeName); + } + + return true; +} + +bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess) +{ + if (pTypeAccess) + { + if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) + { + return false; + } + pTypeAccess->access[HTypeAccess_Create] = false; + pTypeAccess->access[HTypeAccess_Inherit] = false; + pTypeAccess->ident = NULL; + } + + if (pHandleAccess) + { + if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) + { + return false; + } + + pHandleAccess->access[HandleAccess_Clone] = 0; + pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER; + pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY; + } + + return true; +} + +bool HandleSystem::TryAndFreeSomeHandles() +{ + IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator(); + IPlugin *highest_owner = NULL; + unsigned int highest_handle_count = 0; + + /* Search all plugins */ + while (pl_iter->MorePlugins()) + { + IPlugin *plugin = pl_iter->GetPlugin(); + IdentityToken_t *identity = plugin->GetIdentity(); + unsigned int handle_count = 0; + + if (identity == NULL) + { + continue; + } + + /* Search all handles */ + for (unsigned int i = 1; i <= m_HandleTail; i++) + { + if (m_Handles[i].set != HandleSet_Used) + { + continue; + } + if (m_Handles[i].owner == identity) + { + handle_count++; + } + } + + if (handle_count > highest_handle_count) + { + highest_owner = plugin; + highest_handle_count = handle_count; + } + + pl_iter->NextPlugin(); + } + + if (highest_owner == NULL || highest_handle_count == 0) + { + return false; + } + + g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename()); + g_Logger.LogFatal("[SM] Reloading plugin to free %d handles.", highest_handle_count); + g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count); + + highest_owner->GetBaseContext()->ThrowNativeErrorEx(SP_ERROR_MEMACCESS, "Memory leak"); + + return g_PluginSys.UnloadPlugin(highest_owner); +} + +void HandleSystem::Dump(HANDLE_REPORTER rep) +{ + unsigned int total_size = 0; + rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory"); + rep("--------------------------------------------------------------------------"); + for (unsigned int i = 1; i <= m_HandleTail; i++) + { + if (m_Handles[i].set != HandleSet_Used) + { + continue; + } + /* Get the index */ + unsigned int index = (m_Handles[i].serial << 16) | i; + /* Determine the owner */ + const char *owner = "UNKNOWN"; + if (m_Handles[i].owner) + { + IdentityToken_t *pOwner = m_Handles[i].owner; + if (pOwner == g_pCoreIdent) + { + owner = "CORE"; + } + else if (pOwner == g_PluginSys.GetIdentity()) + { + owner = "PLUGINSYS"; + } + else + { + CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner); + if (ext) + { + owner = ext->GetFilename(); + } + else + { + CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner); + if (pPlugin) + { + owner = pPlugin->GetFilename(); + } + } + } + } + else + { + owner = "NONE"; + } + const char *type = "ANON"; + QHandleType *pType = &m_Types[m_Handles[i].type]; + unsigned int size = 0; + if (pType->nameIdx != -1) + { + type = m_strtab->GetString(pType->nameIdx); + } + if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION + || !pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size)) + { + rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1"); + } + else + { + char buffer[32]; + UTIL_Format(buffer, sizeof(buffer), "%d", size); + rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer); + total_size += size; + } + } + rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size); +} + diff -r 9981b8ecf935 -r 31f49899c0ae core/HandleSys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/HandleSys.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,225 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ +#define _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ + +#include +#include +#include "sm_globals.h" +#include "sm_trie.h" +#include "sourcemod.h" +#include "sm_memtable.h" + +#define HANDLESYS_MAX_HANDLES (1<<14) +#define HANDLESYS_MAX_TYPES (1<<9) +#define HANDLESYS_MAX_SUBTYPES 0xF +#define HANDLESYS_SUBTYPE_MASK 0xF +#define HANDLESYS_TYPEARRAY_SIZE (HANDLESYS_MAX_TYPES * (HANDLESYS_MAX_SUBTYPES + 1)) +#define HANDLESYS_MAX_SERIALS 0xFFFF +#define HANDLESYS_SERIAL_MASK 0xFFFF0000 +#define HANDLESYS_HANDLE_MASK 0x0000FFFF + +#define HANDLESYS_MEMUSAGE_MIN_VERSION 3 + +/** + * The QHandle is a nasty structure that compacts the handle system into a big vector. + * The members of the vector each encapsulate one Handle, however, they also act as nodes + * in an inlined linked list and an inlined vector. + * + * The first of these lists is the 'freeID' list. Each node from 1 to N (where N + * is the number of free nodes) has a 'freeID' that specifies a free Handle ID. This + * is a quick hack to get around allocating a second base vector. + * + * The second vector is the identity linked list. An identity has its own handle, so + * these handles are used as sentinel nodes for index linking. They point to the first and last + * index into the handle array. Each subsequent Handle who is owned by that identity is mapped into + * that list. This lets owning identities be unloaded in O(n) time. + * + * Eventually, there may be a third list for type chains. + */ + +enum HandleSet +{ + HandleSet_None = 0, + HandleSet_Used, /* The Handle is in use */ + HandleSet_Freed, /* The "master" Handle of a clone chain is freed */ + HandleSet_Identity, /* The Handle is a special identity */ +}; + +struct QHandle +{ + HandleType_t type; /* Handle type */ + void *object; /* Unmaintained object pointer */ + IdentityToken_t *owner; /* Identity of object which owns this */ + unsigned int serial; /* Serial no. for sanity checking */ + unsigned int refcount; /* Reference count for safe destruction */ + unsigned int clone; /* If non-zero, this is our cloned parent index */ + HandleSet set; /* Information about the handle's state */ + bool access_special; /* Whether or not access rules are special or type-derived */ + bool is_destroying; /* Whether or not the handle is being destroyed */ + HandleAccess sec; /* Security rules */ + /* The following variables are unrelated to the Handle array, and used + * as an inlined chain of information */ + unsigned int freeID; /* ID of a free handle in the free handle chain */ + /* Indexes into the handle array for owner membership. + * For identity roots, these are treated as the head/tail. */ + unsigned int ch_prev; /* chained previous handle */ + unsigned int ch_next; /* chained next handle */ +}; + +struct QHandleType +{ + IHandleTypeDispatch *dispatch; + unsigned int freeID; + unsigned int children; + TypeAccess typeSec; + HandleAccess hndlSec; + unsigned int opened; + int nameIdx; +}; + +typedef void (HANDLE_REPORTER)(const char *str, ...); + +class HandleSystem : + public IHandleSys +{ + friend HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index); + friend class ShareSystem; +public: + HandleSystem(); + ~HandleSystem(); +public: //IHandleSystem + + HandleType_t CreateType(const char *name, + IHandleTypeDispatch *dispatch, + HandleType_t parent, + const TypeAccess *typeAccess, + const HandleAccess *hndlAccess, + IdentityToken_t *ident, + HandleError *err); + + bool RemoveType(HandleType_t type, IdentityToken_t *ident); + + bool FindHandleType(const char *name, HandleType_t *type); + + Handle_t CreateHandle(HandleType_t type, + void *object, + IdentityToken_t *owner, + IdentityToken_t *ident, + HandleError *err); + + HandleError FreeHandle(Handle_t handle, const HandleSecurity *pSecurity); + + HandleError CloneHandle(Handle_t handle, + Handle_t *newhandle, + IdentityToken_t *newOwner, + const HandleSecurity *pSecurity); + + HandleError ReadHandle(Handle_t handle, + HandleType_t type, + const HandleSecurity *pSecurity, + void **object); + + bool InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess); + + bool TypeCheck(HandleType_t intype, HandleType_t outtype); + + virtual Handle_t CreateHandleEx(HandleType_t type, + void *object, + const HandleSecurity *pSec, + const HandleAccess *pAccess, + HandleError *err); + + void Dump(HANDLE_REPORTER rep); +protected: + /** + * Decodes a handle with sanity and security checking. + */ + HandleError GetHandle(Handle_t handle, + IdentityToken_t *ident, + QHandle **pHandle, + unsigned int *index, + bool ignoreFree=false); + + /** + * Creates a basic handle and sets its reference count to 1. + * Does not do any type or security checking. + */ + HandleError MakePrimHandle(HandleType_t type, + QHandle **pHandle, + unsigned int *index, + HandleType_t *handle, + IdentityToken_t *owner, + bool identity=false); + + /** + * Frees a primitive handle. Does no object freeing, only reference count, bookkeeping, + * and linked list maintenance. + * If used on an Identity handle, destroys all Handles under that identity. + */ + void ReleasePrimHandle(unsigned int index); + + /** + * Sets the security owner of a type. + */ + void SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken); + + /** + * Helper function to check access rights. + */ + bool CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity); + + /** + * Some wrappers for internal functions, so we can pass indexes instead of encoded handles. + */ + HandleError FreeHandle(QHandle *pHandle, unsigned int index); + void UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index); + HandleError CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner); + Handle_t CreateHandleInt(HandleType_t type, void *object, const HandleSecurity *pSec, HandleError *err, const HandleAccess *pAccess, bool identity); + + bool TryAndFreeSomeHandles(); + HandleError TryAllocHandle(unsigned int *handle); +private: + QHandle *m_Handles; + QHandleType *m_Types; + Trie *m_TypeLookup; + unsigned int m_TypeTail; + unsigned int m_FreeTypes; + unsigned int m_HandleTail; + unsigned int m_FreeHandles; + unsigned int m_HSerial; + BaseStringTable *m_strtab; +}; + +extern HandleSystem g_HandleSys; + +#endif //_INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/LibrarySys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/LibrarySys.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,441 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include +#include +#include +#include "sm_stringutil.h" +#include "LibrarySys.h" + +LibrarySystem g_LibSys; + +CLibrary::~CLibrary() +{ + if (m_lib) + { +#if defined PLATFORM_WINDOWS + FreeLibrary(m_lib); +#elif defined PLATFORM_POSIX + dlclose(m_lib); +#endif + m_lib = NULL; + } +} + +CLibrary::CLibrary(LibraryHandle me) +{ + m_lib = me; +} + +void CLibrary::CloseLibrary() +{ + delete this; +} + +void *CLibrary::GetSymbolAddress(const char *symname) +{ +#if defined PLATFORM_WINDOWS + return GetProcAddress(m_lib, symname); +#elif defined PLATFORM_POSIX + return dlsym(m_lib, symname); +#endif +} + + +/******************** + ** Directory Code ** + ********************/ + + +CDirectory::CDirectory(const char *path) +{ +#if defined PLATFORM_WINDOWS + char newpath[PLATFORM_MAX_PATH]; + snprintf(newpath, sizeof(newpath), "%s\\*.*", path); + m_dir = FindFirstFileA(newpath, &m_fd); + if (!IsValid()) + { + m_fd.cFileName[0] = '\0'; + } +#elif defined PLATFORM_POSIX + m_dir = opendir(path); + if (IsValid()) + { + /* :TODO: we need to read past "." and ".."! */ + ep = readdir(m_dir); + snprintf(m_origpath, PLATFORM_MAX_PATH, "%s", path); + } else { + ep = NULL; + } +#endif +} + +CDirectory::~CDirectory() +{ + if (IsValid()) + { +#if defined PLATFORM_WINDOWS + FindClose(m_dir); +#elif defined PLATFORM_POSIX + closedir(m_dir); +#endif + } +} + +void CDirectory::NextEntry() +{ +#if defined PLATFORM_WINDOWS + if (FindNextFile(m_dir, &m_fd) == 0) + { + FindClose(m_dir); + m_dir = INVALID_HANDLE_VALUE; + } +#elif defined PLATFORM_POSIX + if ((ep=readdir(m_dir)) == NULL) + { + closedir(m_dir); + m_dir = NULL; + } +#endif +} + +bool CDirectory::IsEntryValid() +{ + return IsValid(); +} + +bool CDirectory::IsEntryDirectory() +{ +#if defined PLATFORM_WINDOWS + return ((m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY); +#elif defined PLATFORM_LINUX + char temppath[PLATFORM_MAX_PATH]; + snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName()); + return g_LibSys.IsPathDirectory(temppath); +#endif +} + +bool CDirectory::IsEntryFile() +{ +#if defined PLATFORM_WINDOWS + return !(m_fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)); +#elif defined PLATFORM_POSIX + char temppath[PLATFORM_MAX_PATH]; + snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName()); + return g_LibSys.IsPathFile(temppath); +#endif +} + +const char *CDirectory::GetEntryName() +{ +#if defined PLATFORM_WINDOWS + return m_fd.cFileName; +#elif defined PLATFORM_LINUX + return ep ? ep->d_name : ""; +#endif +} + +bool CDirectory::MoreFiles() +{ + return IsValid(); +} + +bool CDirectory::IsValid() +{ +#if defined PLATFORM_WINDOWS + return (m_dir != INVALID_HANDLE_VALUE); +#elif defined PLATFORM_LINUX + return (m_dir != NULL); +#endif +} + + +/************************* + ** Library System Code ** + *************************/ + + +bool LibrarySystem::PathExists(const char *path) +{ +#if defined PLATFORM_WINDOWS + DWORD attr = GetFileAttributesA(path); + + return (attr != INVALID_FILE_ATTRIBUTES); +#elif defined PLATFORM_POSIX + struct stat s; + + return (stat(path, &s) == 0); +#endif +} + +bool LibrarySystem::IsPathFile(const char *path) +{ +#if defined PLATFORM_WINDOWS + DWORD attr = GetFileAttributes(path); + + if (attr == INVALID_FILE_ATTRIBUTES) + { + return false; + } + + if (attr & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) + { + return false; + } + + return true; +#elif defined PLATFORM_LINUX + struct stat s; + + if (stat(path, &s) != 0) + { + return false; + } + + return S_ISREG(s.st_mode) ? true : false; +#endif +} + +bool LibrarySystem::IsPathDirectory(const char *path) +{ +#if defined PLATFORM_WINDOWS + DWORD attr = GetFileAttributes(path); + + if (attr == INVALID_FILE_ATTRIBUTES) + { + return false; + } + + if (attr & FILE_ATTRIBUTE_DIRECTORY) + { + return true; + } + +#elif defined PLATFORM_LINUX + struct stat s; + + if (stat(path, &s) != 0) + { + return false; + } + + if (S_ISDIR(s.st_mode)) + { + return true; + } +#endif + + return false; +} + +IDirectory *LibrarySystem::OpenDirectory(const char *path) +{ + CDirectory *dir = new CDirectory(path); + + if (!dir->IsValid()) + { + delete dir; + return NULL; + } + + return dir; +} + +void LibrarySystem::GetPlatformError(char *error, size_t maxlength) +{ + if (error && maxlength) + { +#if defined PLATFORM_WINDOWS + DWORD dw = GetLastError(); + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)error, + maxlength, + NULL); +#elif defined PLATFORM_POSIX + UTIL_Format(error, maxlength, "%s", strerror(errno)); +#endif + } +} + +void LibrarySystem::GetLoaderError(char *buffer, size_t maxlength) +{ +#if defined PLATFORM_WINDOWS + GetPlatformError(buffer, maxlength); +#elif defined PLATFORM_POSIX + if (buffer != NULL && maxlength) + { + strncopy(buffer, dlerror(), maxlength); + } +#endif +} + +void LibrarySystem::CloseDirectory(IDirectory *dir) +{ + delete dir; +} + +ILibrary *LibrarySystem::OpenLibrary(const char *path, char *error, size_t maxlength) +{ + LibraryHandle lib; +#if defined PLATFORM_WINDOWS + lib = LoadLibraryA(path); +#elif defined PLATFORM_POSIX + lib = dlopen(path, RTLD_NOW); +#endif + + if (lib == NULL) + { + GetLoaderError(error, maxlength); + return NULL; + } + + return new CLibrary(lib); +} + +size_t LibrarySystem::PathFormat(char *buffer, size_t len, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + size_t mylen = vsnprintf(buffer, len, fmt, ap); + va_end(ap); + + if (mylen >= len) + { + mylen = len - 1; + buffer[mylen] = '\0'; + } + + for (size_t i=0; i= 0 && i <= length - 1; + i--) + { + if (path[i] == '/' +#if defined PLATFORM_WINDOWS + || path[i] == '\\' +#endif + ) + { + return UTIL_Format(buffer, maxlength, "%s", &path[i+1]); + } + } + + /* We scanned and found no path separator */ + return UTIL_Format(buffer, maxlength, "%s", path); +} + +bool LibrarySystem::FileTime(const char *path, FileTimeType type, time_t *pTime) +{ +#ifdef PLATFORM_WINDOWS + struct _stat s; + if (_stat(path, &s) != 0) +#elif defined PLATFORM_POSIX + struct stat s; + if (stat(path, &s) != 0) +#endif + { + return false; + } + + if (type == FileTime_LastAccess) + { + *pTime = s.st_atime; + } + else if (type == FileTime_Created) + { + *pTime = s.st_ctime; + } + else if (type == FileTime_LastChange) + { + *pTime = s.st_mtime; + } + + return true; +} diff -r 9981b8ecf935 -r 31f49899c0ae core/LibrarySys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/LibrarySys.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,103 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ +#define _INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ + +#include +#include "sm_platform.h" + +using namespace SourceMod; + +#if defined PLATFORM_WINDOWS +typedef HMODULE LibraryHandle; +#elif defined PLATFORM_POSIX +typedef void * LibraryHandle; +#endif + +class CDirectory : public IDirectory +{ +public: + CDirectory(const char *path); + ~CDirectory(); +public: + bool MoreFiles(); + void NextEntry(); + const char *GetEntryName(); + bool IsEntryDirectory(); + bool IsEntryFile(); + bool IsEntryValid(); +public: + bool IsValid(); +private: +#if defined PLATFORM_WINDOWS + HANDLE m_dir; + WIN32_FIND_DATAA m_fd; +#elif defined PLATFORM_POSIX + DIR *m_dir; + struct dirent *ep; + char m_origpath[PLATFORM_MAX_PATH]; +#endif +}; + +class CLibrary : public ILibrary +{ +public: + CLibrary(LibraryHandle me); + ~CLibrary(); +public: + void CloseLibrary(); + void *GetSymbolAddress(const char *symname); +private: + LibraryHandle m_lib; +}; + +class LibrarySystem : public ILibrarySys +{ +public: + ILibrary *OpenLibrary(const char *path, char *error, size_t maxlength); + IDirectory *OpenDirectory(const char *path); + void CloseDirectory(IDirectory *dir); + bool PathExists(const char *path); + bool IsPathFile(const char *path); + bool IsPathDirectory(const char *path); + void GetPlatformError(char *error, size_t maxlength); + size_t PathFormat(char *buffer, size_t len, const char *fmt, ...); + const char *GetFileExtension(const char *filename); + bool CreateFolder(const char *path); + size_t GetFileFromPath(char *buffer, size_t maxlength, const char *path); + bool FileTime(const char *path, FileTimeType type, time_t *pTime); + void GetLoaderError(char *buffer, size_t maxlength); +}; + +extern LibrarySystem g_LibSys; + +#endif //_INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/Logger.cpp --- a/core/Logger.cpp Mon Nov 24 08:25:36 2008 -0600 +++ b/core/Logger.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -34,7 +34,7 @@ #include "sourcemm_api.h" #include "sm_stringutil.h" #include "Logger.h" -#include "systems/LibrarySys.h" +#include "LibrarySys.h" #include "TimerSys.h" #include "sm_version.h" diff -r 9981b8ecf935 -r 31f49899c0ae core/Makefile --- a/core/Makefile Mon Nov 24 08:25:36 2008 -0600 +++ b/core/Makefile Mon Nov 24 08:39:46 2008 -0600 @@ -27,9 +27,13 @@ smn_lang.cpp smn_player.cpp smn_string.cpp smn_sorting.cpp smn_textparse.cpp smn_timers.cpp \ smn_usermsgs.cpp smn_menus.cpp smn_database.cpp smn_vector.cpp smn_adt_array.cpp \ smn_adt_trie.cpp smn_hudtext.cpp smn_adt_stack.cpp smn_nextmap.cpp -OBJECTS += systems/ExtensionSys.cpp systems/ForwardSys.cpp systems/HandleSys.cpp \ - systems/LibrarySys.cpp systems/PluginInfoDatabase.cpp systems/PluginSys.cpp \ - systems/ShareSys.cpp +OBJECTS += ExtensionSys.cpp \ + ForwardSys.cpp \ + HandleSys.cpp \ + LibrarySys.cpp \ + PluginInfoDatabase.cpp \ + PluginSys.cpp \ + ShareSys.cpp OBJECTS += thread/ThreadWorker.cpp thread/BaseWorker.cpp thread/PosixThreads.cpp ThreadSupport.cpp ############################################## diff -r 9981b8ecf935 -r 31f49899c0ae core/PlayerManager.cpp --- a/core/PlayerManager.cpp Mon Nov 24 08:25:36 2008 -0600 +++ b/core/PlayerManager.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -46,7 +46,7 @@ #include #include #include "GameConfigs.h" -#include "systems/ExtensionSys.h" +#include "ExtensionSys.h" PlayerManager g_Players; bool g_OnMapStarted = false; diff -r 9981b8ecf935 -r 31f49899c0ae core/PluginInfoDatabase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/PluginInfoDatabase.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,349 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include +#include "PluginInfoDatabase.h" +#include "PluginSys.h" + +void PluginSettings::Init() +{ + name = -1; + pause_val = false; + type_val = PluginType_MapUpdated; + optarray = -1; + opts_num = 0; + opts_size = 0; + blockload_val = false; +} + +/** + * :TODO: write the logger, make these errors log instead of being static + * NOTE: once we do that, we will have to change some code to ignore sections + */ + +CPluginInfoDatabase::CPluginInfoDatabase() +{ + m_strtab = NULL; + m_infodb_count = 0; + m_infodb_size = 0; + m_infodb = -1; +} + +CPluginInfoDatabase::~CPluginInfoDatabase() +{ + delete m_strtab; +} + +void CPluginInfoDatabase::ReadSMC_ParseStart() +{ + /* Create or reset our string table */ + if (m_strtab) + { + m_strtab->Reset(); + } else { + m_strtab = new BaseStringTable(1024); + } + + /* Set our internal states to the beginning */ + in_plugins = false; + cur_plugin = -1; + in_options = false; + m_infodb_size = 0; + m_infodb_count = 0; + m_infodb = -1; +} + +SMCResult CPluginInfoDatabase::MakeError(const char *fmt, ...) +{ + char buffer[512]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + m_errmsg = m_strtab->AddString(buffer); + + return SMCResult_HaltFail; +} + +unsigned int CPluginInfoDatabase::GetSettingsNum() +{ + return m_infodb_count; +} + +PluginSettings *CPluginInfoDatabase::GetSettingsIfMatch(unsigned int index, const char *filename) +{ + BaseMemTable *memtab = m_strtab->GetMemTable(); + int *table = (int *)memtab->GetAddress(m_infodb); + + if (!table || index >= m_infodb_count) + { + return NULL; + } + + PluginSettings *plugin = (PluginSettings *)memtab->GetAddress(table[index]); + + const char *name = m_strtab->GetString(plugin->name); + + if (!name) + { + return NULL; + } + + if (!g_PluginSys.TestAliasMatch(name, filename)) + { + return NULL; + } + + return plugin; +} + +void CPluginInfoDatabase::GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val) +{ + PluginOpts *table = (PluginOpts *)m_strtab->GetMemTable()->GetAddress(settings->optarray); + if (!table) + { + *key = NULL; + *val = NULL; + return; + } + + if (opt_num >= settings->opts_num) + { + *key = NULL; + *val = NULL; + return; + } + + *key = m_strtab->GetString(table[opt_num].key); + *val = m_strtab->GetString(table[opt_num].val); +} + +SMCResult CPluginInfoDatabase::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) +{ + if (cur_plugin != -1) + { + PluginSettings *plugin = (PluginSettings *)m_strtab->GetMemTable()->GetAddress(cur_plugin); + if (!in_options) + { + if (strcmp(key, "pause") == 0) + { + if (strcasecmp(value, "yes") == 0) + { + plugin->pause_val = true; + } + else + { + plugin->pause_val = false; + } + } + else if (strcmp(key, "lifetime") == 0) + { + if (strcasecmp(value, "private") == 0) + { + plugin->type_val = PluginType_Private; + } + else if (strcasecmp(value, "mapsync") == 0) + { + plugin->type_val = PluginType_MapUpdated; + } + else if (strcasecmp(value, "maponly") == 0) + { + plugin->type_val = PluginType_MapOnly; + } + else if (strcasecmp(value, "global") == 0) + { + plugin->type_val = PluginType_Global; + } + else + { + return MakeError("Unknown value for key \"lifetime\": \"%s\"", value); + } + } + else if (strcmp(key, "blockload") == 0) + { + plugin->blockload_val = true; + } + else + { + return MakeError("Unknown property key: \"%s\"", key); + } + } + else + { + /* Cache every option, valid or not */ + int keyidx = m_strtab->AddString(key); + int validx = m_strtab->AddString(value); + PluginOpts *table; + BaseMemTable *memtab = m_strtab->GetMemTable(); + plugin = (PluginSettings *)memtab->GetAddress(cur_plugin); + if (plugin->opts_num + 1 > plugin->opts_size) + { + unsigned int oldsize = plugin->opts_size; + if (oldsize == 0) + { + //right now we don't have many + plugin->opts_size = 2; + } + else + { + plugin->opts_size *= 2; + } + int newidx = memtab->CreateMem(plugin->opts_size * sizeof(PluginOpts), (void **)&table); + /* in case it resized */ + plugin = (PluginSettings *)memtab->GetAddress(cur_plugin); + if (plugin->optarray != -1) + { + void *oldtable = memtab->GetAddress(plugin->optarray); + memcpy(table, oldtable, oldsize * sizeof(PluginOpts)); + } + plugin->optarray = newidx; + } + else + { + table = (PluginOpts *)memtab->GetAddress(plugin->optarray); + } + PluginOpts *opt = &table[plugin->opts_num++]; + opt->key = keyidx; + opt->val = validx; + } + } + else if (in_plugins) + { + return MakeError("Unknown property key: \"%s\"", key); + } + else + { + /* Ignore anything we don't know about! */ + } + + return SMCResult_Continue; +} + +SMCResult CPluginInfoDatabase::ReadSMC_LeavingSection(const SMCStates *states) +{ + if (in_plugins) + { + if (cur_plugin != -1) + { + if (in_options) + { + in_options = false; + } + else + { + /* If the plugin is ending, add it to the table */ + BaseMemTable *memtab = m_strtab->GetMemTable(); + int *table; + if (m_infodb_count + 1 > m_infodb_size) + { + unsigned int oldsize = m_infodb_size; + if (!m_infodb_size) + { + m_infodb_size = 8; + } + else + { + m_infodb_size *= 2; + } + int newidx = memtab->CreateMem(m_infodb_size, (void **)&table); + if (m_infodb != -1) + { + void *oldtable = (int *)memtab->GetAddress(m_infodb); + memcpy(table, oldtable, oldsize * sizeof(int)); + } + m_infodb = newidx; + } + else + { + table = (int *)memtab->GetAddress(m_infodb); + } + /* Assign to table and scrap the current plugin */ + table[m_infodb_count++] = cur_plugin; + cur_plugin = -1; + } + } + else + { + in_plugins = false; + } + } + + return SMCResult_Continue; +} + +SMCResult CPluginInfoDatabase::ReadSMC_NewSection(const SMCStates *states, const char *name) +{ + if (!in_plugins) + { + /* If we're not in the main Plugins section, and we don't get it for the name, error out */ + if (strcmp(name, "Plugins") != 0) + { + return MakeError("Unknown root section: \"%s\"", name); + } + else + { + /* Otherwise set our states */ + in_plugins = true; + cur_plugin = -1; + in_options = false; + } + } + else + { + if (cur_plugin == -1) + { + /* If we get a plugin node and we don't have a current plugin, create a new one */ + PluginSettings *plugin; + int i_name = m_strtab->AddString(name); + cur_plugin = m_strtab->GetMemTable()->CreateMem(sizeof(PluginSettings), (void **)&plugin); + plugin->Init(); + plugin->name = i_name; + in_options = false; + } + else + { + if (!in_options && strcmp(name, "Options") == 0) + { + in_options = true; + } + else + { + return MakeError("Unknown plugin sub-section: \"%s\"", name); + } + } + } + + return SMCResult_Continue; +} + diff -r 9981b8ecf935 -r 31f49899c0ae core/PluginInfoDatabase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/PluginInfoDatabase.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,106 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_ +#define _INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_ + +/** + * This file parses plugin_settings.cfg and stores the information in cached memory. + * It provides simplistic abstraction to retrieving the info. + * :TODO: currently untested + */ + +#include "sm_memtable.h" +#include "ITextParsers.h" +#include "IPluginSys.h" +#include "sm_globals.h" + +struct PluginOpts +{ + int key; + int val; +}; + +struct PluginSettings +{ + void Init(); + int name; + bool pause_val; + PluginType type_val; + int optarray; + size_t opts_num; + size_t opts_size; + bool blockload_val; +}; + +class CPluginInfoDatabase : public ITextListener_SMC +{ +public: + CPluginInfoDatabase(); + ~CPluginInfoDatabase(); +public: //ITextListener_SMC + void ReadSMC_ParseStart(); + SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name); + SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value); + SMCResult ReadSMC_LeavingSection(const SMCStates *states); +public: + /** + * Returns the number of plugin settings available. + */ + unsigned int GetSettingsNum(); + + /** + * Given an index, returns the plugin settings block if the filename matches. + * Otherwise, returns NULL. + */ + PluginSettings *GetSettingsIfMatch(unsigned int index, const char *filename); + + /** + * Given a plugin settings struct and an index, + * returns the given JIT key/value option pair at that index. + * If the input is invalid, key and val will be set to NULL. + */ + void GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val); +private: + SMCResult MakeError(const char *fmt, ...); +private: + BaseStringTable *m_strtab; + int m_errmsg; + bool in_plugins; + bool in_options; + int m_infodb; + size_t m_infodb_count; + size_t m_infodb_size; + int cur_plugin; +}; + + +#endif //_INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/PluginSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/PluginSys.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,2626 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include +#include "PluginSys.h" +#include "ShareSys.h" +#include "LibrarySys.h" +#include "HandleSys.h" +#include "ForwardSys.h" +#include "sourcemm_api.h" +#include "sourcemod.h" +#include "TextParsers.h" +#include "Logger.h" +#include "ExtensionSys.h" +#include "sm_srvcmds.h" +#include "sm_stringutil.h" +#include "ConCmdManager.h" +#include "PlayerManager.h" +#include "CoreConfig.h" +#include "Translator.h" + +CPluginManager g_PluginSys; +HandleType_t g_PluginType = 0; +IdentityType_t g_PluginIdent = 0; + +CPlugin::CPlugin(const char *file) +{ + static int MySerial = 0; + + m_type = PluginType_Private; + m_status = Plugin_Uncompiled; + m_serial = ++MySerial; + m_pRuntime = NULL; + m_errormsg[256] = '\0'; + snprintf(m_filename, sizeof(m_filename), "%s", file); + m_handle = 0; + m_ident = NULL; + m_pProps = sm_trie_create(); + m_FakeNativesMissing = false; + m_LibraryMissing = false; + m_bGotAllLoaded = false; + m_pPhrases = g_Translator.CreatePhraseCollection(); + m_MaxClientsVar = NULL; +} + +CPlugin::~CPlugin() +{ + if (m_handle) + { + HandleSecurity sec; + sec.pOwner = g_PluginSys.GetIdentity(); + sec.pIdentity = sec.pOwner; + + g_HandleSys.FreeHandle(m_handle, &sec); + g_ShareSys.DestroyIdentity(m_ident); + } + + if (m_pRuntime != NULL) + { + delete m_pRuntime; + m_pRuntime = NULL; + } + + if (m_pProps) + { + sm_trie_destroy(m_pProps); + } + for (size_t i=0; iDestroy(); + m_pPhrases = NULL; + } +} + +void CPlugin::InitIdentity() +{ + if (!m_handle) + { + m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this); + m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL); + m_pRuntime->GetDefaultContext()->SetKey(1, m_ident); + m_pRuntime->GetDefaultContext()->SetKey(2, (IPlugin *)this); + } +} + +unsigned int CPlugin::CalcMemUsage() +{ + unsigned int base_size = + sizeof(CPlugin) + + sizeof(IdentityToken_t) + + (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig))) + + sm_trie_mem_usage(m_pProps); + + for (unsigned int i = 0; i < m_configs.size(); i++) + { + base_size += m_configs[i]->autocfg.size(); + base_size += m_configs[i]->folder.size(); + } + + for (List::iterator i = m_Libraries.begin(); + i != m_Libraries.end(); + i++) + { + base_size += (*i).size(); + } + + for (List::iterator i = m_RequiredLibs.begin(); + i != m_RequiredLibs.end(); + i++) + { + base_size += (*i).size(); + } + + return base_size; +} + +Handle_t CPlugin::GetMyHandle() +{ + return m_handle; +} + +CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength) +{ + char fullpath[PLATFORM_MAX_PATH]; + g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file); + FILE *fp = fopen(fullpath, "rb"); + + CPlugin *pPlugin = new CPlugin(file); + + if (!fp) + { + if (error) + { + snprintf(error, maxlength, "Unable to open file"); + } + pPlugin->m_status = Plugin_BadLoad; + return pPlugin; + } + + fclose(fp); + + return pPlugin; +} + +bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */) +{ + bool exists = sm_trie_retrieve(m_pProps, prop, ptr); + + if (exists && remove) + { + sm_trie_delete(m_pProps, prop); + } + + return exists; +} + +bool CPlugin::SetProperty(const char *prop, void *ptr) +{ + return sm_trie_insert(m_pProps, prop, ptr); +} + +IPluginRuntime *CPlugin::GetRuntime() +{ + return m_pRuntime; +} + +void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...) +{ + PluginStatus old_status = m_status; + m_status = status; + + if (old_status == Plugin_Running) + { + /* Tell everyone we're now paused */ + g_PluginSys._SetPauseState(this, true); + } + + va_list ap; + va_start(ap, error_fmt); + vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap); + va_end(ap); + + if (m_pRuntime != NULL) + { + m_pRuntime->SetPauseState(true); + } +} + +bool CPlugin::UpdateInfo() +{ + /* Now grab the info */ + uint32_t idx; + IPluginContext *base = GetBaseContext(); + int err = base->FindPubvarByName("myinfo", &idx); + + memset(&m_info, 0, sizeof(m_info)); + + if (err == SP_ERROR_NONE) + { + struct sm_plugininfo_c_t + { + cell_t name; + cell_t description; + cell_t author; + cell_t version; + cell_t url; + }; + sm_plugininfo_c_t *cinfo; + cell_t local_addr; + + base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo); + base->LocalToString(cinfo->name, (char **)&m_info.name); + base->LocalToString(cinfo->description, (char **)&m_info.description); + base->LocalToString(cinfo->author, (char **)&m_info.author); + base->LocalToString(cinfo->url, (char **)&m_info.url); + base->LocalToString(cinfo->version, (char **)&m_info.version); + } + + m_info.author = m_info.author ? m_info.author : ""; + m_info.description = m_info.description ? m_info.description : ""; + m_info.name = m_info.name ? m_info.name : ""; + m_info.url = m_info.url ? m_info.url : ""; + m_info.version = m_info.version ? m_info.version : ""; + + if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE) + { + struct __version_info + { + cell_t version; + cell_t filevers; + cell_t date; + cell_t time; + }; + __version_info *info; + cell_t local_addr; + const char *pDate, *pTime, *pFileVers; + + pDate = ""; + pTime = ""; + + base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info); + m_FileVersion = info->version; + if (m_FileVersion >= 3) + { + base->LocalToString(info->date, (char **)&pDate); + base->LocalToString(info->time, (char **)&pTime); + UTIL_Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime); + } + if (m_FileVersion > 4) + { + base->LocalToString(info->filevers, (char **)&pFileVers); + SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers); + return false; + } + } + else + { + m_FileVersion = 0; + } + + if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE) + { + base->GetPubvarByIndex(idx, &m_MaxClientsVar); + } + + return true; +} + +void CPlugin::SyncMaxClients(int max_clients) +{ + if (m_MaxClientsVar == NULL) + { + return; + } + + *m_MaxClientsVar->offs = max_clients; +} + +void CPlugin::Call_OnPluginStart() +{ + if (m_status != Plugin_Loaded) + { + return; + } + + m_status = Plugin_Running; + + SyncMaxClients(g_Players.MaxClients()); + + cell_t result; + IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart"); + if (!pFunction) + { + return; + } + + int err; + if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) + { + SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)"); + } +} + +void CPlugin::Call_OnPluginEnd() +{ + if (m_status > Plugin_Paused) + { + return; + } + + cell_t result; + IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd"); + if (!pFunction) + { + return; + } + + pFunction->Execute(&result); +} + +void CPlugin::Call_OnAllPluginsLoaded() +{ + if (m_status > Plugin_Paused) + { + return; + } + + if (m_bGotAllLoaded) + { + return; + } + + m_bGotAllLoaded = true; + + cell_t result; + IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded"); + if (pFunction != NULL) + { + pFunction->Execute(&result); + } + + if (g_OnMapStarted) + { + if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL) + { + pFunction->Execute(NULL); + } + } + + if (SM_AreConfigsExecuted()) + { + SM_ExecuteForPlugin(GetBaseContext()); + } +} + +bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) +{ + if (m_status != Plugin_Created) + { + return false; + } + + m_status = Plugin_Loaded; + + int err; + cell_t result; + IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad"); + + if (!pFunction) + { + return true; + } + + pFunction->PushCell(m_handle); + pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0); + pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK); + pFunction->PushCell(maxlength); + if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) + { + return false; + } + + if (!result || m_status != Plugin_Loaded) + { + return false; + } + + return true; +} + +void *CPlugin::GetPluginStructure() +{ + return NULL; +} + +IPluginContext *CPlugin::GetBaseContext() +{ + if (m_pRuntime == NULL) + { + return NULL; + } + + return m_pRuntime->GetDefaultContext(); +} + +sp_context_t *CPlugin::GetContext() +{ + return NULL; +} + +const char *CPlugin::GetFilename() +{ + return m_filename; +} + +PluginType CPlugin::GetType() +{ + return m_type; +} + +const sm_plugininfo_t *CPlugin::GetPublicInfo() +{ + if (GetStatus() >= Plugin_Created) + { + return NULL; + } + return &m_info; +} + +unsigned int CPlugin::GetSerial() +{ + return m_serial; +} + +PluginStatus CPlugin::GetStatus() +{ + return m_status; +} + +bool CPlugin::IsDebugging() +{ + if (m_pRuntime == NULL) + { + return false; + } + + return true; +} + +void CPlugin::LibraryActions(bool dropping) +{ + List::iterator iter; + for (iter = m_Libraries.begin(); + iter != m_Libraries.end(); + iter++) + { + g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping); + } +} + +bool CPlugin::SetPauseState(bool paused) +{ + if (paused && GetStatus() != Plugin_Running) + { + return false; + } else if (!paused && GetStatus() != Plugin_Paused) { + return false; + } + + if (paused) + { + LibraryActions(true); + } + + IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange"); + if (pFunction) + { + cell_t result; + pFunction->PushCell(paused ? 1 : 0); + pFunction->Execute(&result); + } + + if (paused) + { + m_status = Plugin_Paused; + m_pRuntime->SetPauseState(true); + } else { + m_status = Plugin_Running; + m_pRuntime->SetPauseState(false); + } + + g_PluginSys._SetPauseState(this, paused); + + if (!paused) + { + LibraryActions(false); + } + + return true; +} + +IdentityToken_t *CPlugin::GetIdentity() +{ + return m_ident; +} + +bool CPlugin::IsRunnable() +{ + return (m_status <= Plugin_Paused) ? true : false; +} + +time_t CPlugin::GetFileTimeStamp() +{ + char path[PLATFORM_MAX_PATH]; + g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename); +#ifdef PLATFORM_WINDOWS + struct _stat s; + if (_stat(path, &s) != 0) +#elif defined PLATFORM_POSIX + struct stat s; + if (stat(path, &s) != 0) +#endif + { + return 0; + } + else + { + return s.st_mtime; + } +} + +time_t CPlugin::GetTimeStamp() +{ + return m_LastAccess; +} + +void CPlugin::SetTimeStamp(time_t t) +{ + m_LastAccess = t; +} + +IPhraseCollection *CPlugin::GetPhrases() +{ + return m_pPhrases; +} + +void CPlugin::DependencyDropped(CPlugin *pOwner) +{ + if (!m_pRuntime) + { + return; + } + + List::iterator reqlib_iter; + List::iterator lib_iter; + for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++) + { + for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++) + { + if ((*reqlib_iter) == (*lib_iter)) + { + m_LibraryMissing = true; + } + } + } + + List::iterator iter; + NativeEntry *pNative; + sp_native_t *native; + uint32_t idx; + unsigned int unbound = 0; + + for (iter = pOwner->m_Natives.begin(); + iter != pOwner->m_Natives.end(); + iter++) + { + pNative = (*iter); + /* Find this native! */ + if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE) + { + continue; + } + /* Unbind it */ + m_pRuntime->GetNativeByIndex(idx, &native); + native->pfn = NULL; + native->status = SP_NATIVE_UNBOUND; + unbound++; + } + + if (unbound) + { + m_FakeNativesMissing = true; + } + + /* :IDEA: in the future, add native trapping? */ + if (m_FakeNativesMissing || m_LibraryMissing) + { + SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename()); + } +} + +unsigned int CPlugin::GetConfigCount() +{ + return (unsigned int)m_configs.size(); +} + +AutoConfig *CPlugin::GetConfig(unsigned int i) +{ + if (i >= GetConfigCount()) + { + return NULL; + } + + return m_configs[i]; +} + +void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder) +{ + /* Do a check for duplicates to prevent double-execution */ + for (size_t i = 0; i < m_configs.size(); i++) + { + if (m_configs[i]->autocfg.compare(cfg) == 0 + && m_configs[i]->folder.compare(folder) == 0 + && m_configs[i]->create == autoCreate) + { + return; + } + } + + AutoConfig *c = new AutoConfig; + + c->autocfg = cfg; + c->folder = folder; + c->create = autoCreate; + + m_configs.push_back(c); +} + +void CPlugin::DropEverything() +{ + CPlugin *pOther; + List::iterator iter; + List::iterator wk_iter; + + /* Tell everyone that depends on us that we're about to drop */ + for (iter = m_Dependents.begin(); + iter != m_Dependents.end(); + iter++) + { + pOther = (*iter); + pOther->DependencyDropped(this); + } + + /* Note: we don't care about things we depend on. + * The reason is that extensions have their own cleanup + * code for plugins. Although the "right" design would be + * to centralize that here, i'm omitting it for now. Thus, + * the code below to walk the plugins list will suffice. + */ + + /* Other plugins could be holding weak references that were + * added by us. We need to clean all of those up now. + */ + for (iter = g_PluginSys.m_plugins.begin(); + iter != g_PluginSys.m_plugins.end(); + iter++) + { + (*iter)->DropRefsTo(this); + } + + /* Proceed with the rest of the necessities. */ + CNativeOwner::DropEverything(); +} + +bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func) +{ + NativeEntry *pEntry; + + if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL) + { + return false; + } + + m_Natives.push_back(pEntry); + + return true; +} + +/******************* + * PLUGIN ITERATOR * + *******************/ + +CPluginManager::CPluginIterator::CPluginIterator(List *_mylist) +{ + mylist = _mylist; + Reset(); +} + +IPlugin *CPluginManager::CPluginIterator::GetPlugin() +{ + return (*current); +} + +bool CPluginManager::CPluginIterator::MorePlugins() +{ + return (current != mylist->end()); +} + +void CPluginManager::CPluginIterator::NextPlugin() +{ + current++; +} + +void CPluginManager::CPluginIterator::Release() +{ + g_PluginSys.ReleaseIterator(this); +} + +CPluginManager::CPluginIterator::~CPluginIterator() +{ +} + +void CPluginManager::CPluginIterator::Reset() +{ + current = mylist->begin(); +} + +/****************** + * PLUGIN MANAGER * + ******************/ + +CPluginManager::CPluginManager() +{ + m_LoadLookup = sm_trie_create(); + m_AllPluginsLoaded = false; + m_MyIdent = NULL; + m_LoadingLocked = false; +} + +CPluginManager::~CPluginManager() +{ + /* :NOTICE: + * Ignore the fact that there might be plugins in the cache. + * This usually means that Core is not being unloaded properly, and everything + * will crash anyway. YAY + */ + sm_trie_destroy(m_LoadLookup); + + CStack::iterator iter; + for (iter=m_iters.begin(); iter!=m_iters.end(); iter++) + { + delete (*iter); + } + m_iters.popall(); +} + +void CPluginManager::Shutdown() +{ + List::iterator iter; + + while ((iter = m_plugins.begin()) != m_plugins.end()) + { + UnloadPlugin(*iter); + } +} + +void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir) +{ + /* First read in the database of plugin settings */ + SMCError err; + SMCStates states; + m_AllPluginsLoaded = false; + if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay) + { + g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config); + const char *err_msg = textparsers->GetSMCErrorString(err); + if (err_msg) + { + g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg); + } + } + + LoadPluginsFromDir(basedir, NULL); +} + +void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath) +{ + char base_path[PLATFORM_MAX_PATH]; + + /* Form the current path to start reading from */ + if (localpath == NULL) + { + g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir); + } else { + g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath); + } + + IDirectory *dir = g_LibSys.OpenDirectory(base_path); + + if (!dir) + { + char error[256]; + g_LibSys.GetPlatformError(error, sizeof(error)); + g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath); + g_Logger.LogError("[SM] Platform returned error: %s", error); + return; + } + + while (dir->MoreFiles()) + { + if (dir->IsEntryDirectory() + && (strcmp(dir->GetEntryName(), ".") != 0) + && (strcmp(dir->GetEntryName(), "..") != 0) + && (strcmp(dir->GetEntryName(), "disabled") != 0) + && (strcmp(dir->GetEntryName(), "optional") != 0)) + { + char new_local[PLATFORM_MAX_PATH]; + if (localpath == NULL) + { + /* If no path yet, don't add a former slash */ + snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName()); + } else { + g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName()); + } + LoadPluginsFromDir(basedir, new_local); + } else if (dir->IsEntryFile()) { + const char *name = dir->GetEntryName(); + size_t len = strlen(name); + if (len >= 4 + && strcmp(&name[len-4], ".smx") == 0) + { + /* If the filename matches, load the plugin */ + char plugin[PLATFORM_MAX_PATH]; + if (localpath == NULL) + { + snprintf(plugin, sizeof(plugin), "%s", name); + } else { + g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name); + } + LoadAutoPlugin(plugin); + } + } + dir->NextEntry(); + } + g_LibSys.CloseDirectory(dir); +} + +LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength) +{ + if (m_LoadingLocked) + { + return LoadRes_NeverLoad; + } + + int err; + bool no_load = false; + PluginSettings *pset; + unsigned int setcount = m_PluginInfo.GetSettingsNum(); + for (unsigned int i=0; iblockload_val) + { + no_load = true; + break; + } + } + + /** + * Does this plugin already exist? + */ + CPlugin *pPlugin; + if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin)) + { + /* Check to see if we should try reloading it */ + if (pPlugin->GetStatus() == Plugin_BadLoad + || pPlugin->GetStatus() == Plugin_Error + || pPlugin->GetStatus() == Plugin_Failed + || no_load) + { + UnloadPlugin(pPlugin); + } + else + { + if (_plugin) + { + *_plugin = pPlugin; + } + return LoadRes_AlreadyLoaded; + } + } + + if (no_load) + { + return LoadRes_NeverLoad; + } + + pPlugin = CPlugin::CreatePlugin(path, error, maxlength); + + assert(pPlugin != NULL); + + pPlugin->m_type = PluginType_MapUpdated; + + ICompilation *co = NULL; + + if (pPlugin->m_status == Plugin_Uncompiled) + { + co = g_pSourcePawn2->StartCompilation(); + } + + for (unsigned int i=0; im_type = pset->type_val; + if (co) + { + for (unsigned int j=0; jopts_num; j++) + { + const char *key, *val; + m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val); + if (!key || !val) + { + continue; + } + if ((err = co->SetOption(key, val)) == SP_ERROR_NONE) + { + if (error) + { + snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val); + } + co->Abort(); + co = NULL; + break; + } + } + } + } + + /* Do the actual compiling */ + if (co != NULL) + { + char fullpath[PLATFORM_MAX_PATH]; + g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename); + + pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err); + if (pPlugin->m_pRuntime == NULL) + { + snprintf(error, + maxlength, + "Unable to load plugin (error %d: %s)", + err, + g_pSourcePawn2->GetErrorString(err)); + pPlugin->m_status = Plugin_BadLoad; + } + else + { + if (pPlugin->UpdateInfo()) + { + pPlugin->m_status = Plugin_Created; + } + else + { + UTIL_Format(error, maxlength, "%s", pPlugin->m_errormsg); + } + } + } + + /* Get the status */ + if (pPlugin->GetStatus() == Plugin_Created) + { + /* First native pass - add anything from Core */ + g_ShareSys.BindNativesToPlugin(pPlugin, true); + pPlugin->InitIdentity(); + if (pPlugin->Call_AskPluginLoad(error, maxlength)) + { + /* Autoload any modules */ + LoadOrRequireExtensions(pPlugin, 1, error, maxlength); + } + else + { + pPlugin->SetErrorState(Plugin_Failed, "%s", error); + } + } + + /* Save the time stamp */ + time_t t = pPlugin->GetFileTimeStamp(); + pPlugin->SetTimeStamp(t); + + if (_plugin) + { + *_plugin = pPlugin; + } + + return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure; +} + +IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded) +{ + CPlugin *pl; + LoadRes res; + + *wasloaded = false; + if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure) + { + delete pl; + return NULL; + } + + if (res == LoadRes_AlreadyLoaded) + { + *wasloaded = true; + return pl; + } + + if (res == LoadRes_NeverLoad) + { + if (m_LoadingLocked) + { + UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect"); + } + else + { + UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)"); + } + return NULL; + } + + AddPlugin(pl); + + /* Run second pass if we need to */ + if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded) + { + if (!RunSecondPass(pl, error, maxlength)) + { + UnloadPlugin(pl); + return NULL; + } + pl->Call_OnAllPluginsLoaded(); + } + + return pl; +} + +void CPluginManager::LoadAutoPlugin(const char *plugin) +{ + CPlugin *pl = NULL; + LoadRes res; + char error[255] = "Unknown error"; + + if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure) + { + g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error); + pl->SetErrorState( + pl->GetStatus() == Plugin_BadLoad ? Plugin_BadLoad : Plugin_Failed, + "%s", + error); + } + + if (res == LoadRes_Successful || res == LoadRes_Failure) + { + AddPlugin(pl); + } +} + +void CPluginManager::AddPlugin(CPlugin *pPlugin) +{ + List::iterator iter; + IPluginsListener *pListener; + + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginCreated(pPlugin); + } + + m_plugins.push_back(pPlugin); + sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin); +} + +void CPluginManager::LoadAll_SecondPass() +{ + List::iterator iter; + CPlugin *pPlugin; + + char error[256]; + for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) + { + pPlugin = (*iter); + if (pPlugin->GetStatus() == Plugin_Loaded) + { + error[0] = '\0'; + if (!RunSecondPass(pPlugin, error, sizeof(error))) + { + g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error); + pPlugin->SetErrorState(Plugin_Failed, "%s", error); + } + } + } + + m_AllPluginsLoaded = true; +} + +bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength) +{ + struct _pl + { + cell_t name; + cell_t file; + cell_t required; + } *pl; + + IPluginContext *pBase = pPlugin->GetBaseContext(); + uint32_t num = pBase->GetPubVarsNum(); + sp_pubvar_t *pubvar; + char *name, *file; + char pathfile[PLATFORM_MAX_PATH]; + + for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) + { + continue; + } + if (strncmp(pubvar->name, "__pl_", 5) == 0) + { + pl = (_pl *)pubvar->offs; + if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE) + { + continue; + } + if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE) + { + continue; + } + g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename()); + if (strcmp(pathfile, file) == 0) + { + continue; + } + if (pl->required == false) + { + IPluginFunction *pFunc; + char buffer[64]; + UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]); + if ((pFunc=pBase->GetFunctionByName(buffer))) + { + cell_t res; + pFunc->Execute(&res); + if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE) + { + if (error) + { + snprintf(error, maxlength, "Fatal error during initializing plugin load"); + } + return false; + } + } + } + else + { + /* Check that we aren't registering the same library twice */ + if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end()) + { + pPlugin->m_RequiredLibs.push_back(name); + } + else + { + continue; + } + List::iterator iter; + CPlugin *pl; + bool found = false; + for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) + { + pl = (*iter); + if (pl->m_Libraries.find(name) != pl->m_Libraries.end()) + { + found = true; + break; + } + } + if (!found) + { + if (error) + { + snprintf(error, maxlength, "Could not find required plugin \"%s\"", name); + } + return false; + } + } + } + } + + return true; +} + +bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength) +{ + /* Find any extensions this plugin needs */ + struct _ext + { + cell_t name; + cell_t file; + cell_t autoload; + cell_t required; + } *ext; + + IPluginContext *pBase = pPlugin->GetBaseContext(); + uint32_t num = pBase->GetPubVarsNum(); + sp_pubvar_t *pubvar; + IExtension *pExt; + char path[PLATFORM_MAX_PATH]; + char *file, *name; + for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) + { + continue; + } + if (strncmp(pubvar->name, "__ext_", 6) == 0) + { + ext = (_ext *)pubvar->offs; + if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE) + { + continue; + } + if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE) + { + continue; + } + if (pass == 1) + { + /* Attempt to auto-load if necessary */ + if (ext->autoload) + { + g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); + g_Extensions.LoadAutoExtension(path); + } + } + else if (pass == 2) + { + /* Is this required? */ + if (ext->required) + { + g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); + if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL) + { + pExt = g_Extensions.FindExtensionByName(name); + } + /* :TODO: should we bind to unloaded extensions? + * Currently the extension manager will ignore this. + */ + if (!pExt || !pExt->IsRunning(NULL, 0)) + { + if (error) + { + snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file); + } + return false; + } + else + { + g_Extensions.BindChildPlugin(pExt, pPlugin); + } + } + else + { + IPluginFunction *pFunc; + char buffer[64]; + UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]); + + if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL) + { + cell_t res; + pFunc->Execute(&res); + if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE) + { + if (error) + { + UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)"); + } + return false; + } + } + } + } + } + } + + return true; +} + +bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength) +{ + /* Second pass for extension requirements */ + if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength)) + { + return false; + } + + if (!FindOrRequirePluginDeps(pPlugin, error, maxlength)) + { + return false; + } + + /* Run another binding pass */ + g_ShareSys.BindNativesToPlugin(pPlugin, false); + + /* Find any unbound natives. Right now, these are not allowed. */ + IPluginContext *pContext = pPlugin->GetBaseContext(); + uint32_t num = pContext->GetNativesNum(); + sp_native_t *native; + for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) + { + break; + } + if (native->status == SP_NATIVE_UNBOUND + && native->name[0] != '@' + && !(native->flags & SP_NTVFLAG_OPTIONAL)) + { + if (error) + { + snprintf(error, maxlength, "Native \"%s\" was not found", native->name); + } + return false; + } + } + + /* Finish by telling all listeners */ + List::iterator iter; + IPluginsListener *pListener; + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginLoaded(pPlugin); + } + + /* Tell this plugin to finish initializing itself */ + pPlugin->Call_OnPluginStart(); + + /* Now, if we have fake natives, go through all plugins that might need rebinding */ + if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size()) + { + List::iterator pl_iter; + CPlugin *pOther; + for (pl_iter = m_plugins.begin(); + pl_iter != m_plugins.end(); + pl_iter++) + { + pOther = (*pl_iter); + if ((pOther->GetStatus() == Plugin_Error + && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing)) + || pOther->m_FakeNativesMissing) + { + TryRefreshDependencies(pOther); + } + else if ((pOther->GetStatus() == Plugin_Running + || pOther->GetStatus() == Plugin_Paused) + && pOther != pPlugin) + { + List::iterator nv_iter; + for (nv_iter = pPlugin->m_Natives.begin(); + nv_iter != pPlugin->m_Natives.end(); + nv_iter++) + { + g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter)); + } + } + } + } + + /* Go through our libraries and tell other plugins they're added */ + List::iterator s_iter; + for (s_iter = pPlugin->m_Libraries.begin(); + s_iter != pPlugin->m_Libraries.end(); + s_iter++) + { + OnLibraryAction((*s_iter).c_str(), true, false); + } + + /* :TODO: optimize? does this even matter? */ + pPlugin->GetPhrases()->AddPhraseFile("core.phrases"); + + return true; +} + +void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin) +{ + assert(pPlugin->GetBaseContext() != NULL); + + g_ShareSys.BindNativesToPlugin(pPlugin, false); + + List::iterator lib_iter; + List::iterator req_iter; + List::iterator pl_iter; + CPlugin *pl; + for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++) + { + bool found = false; + for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++) + { + pl = (*pl_iter); + for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++) + { + if ((*req_iter) == (*lib_iter)) + { + found = true; + } + } + } + if (!found) + { + pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str()); + return; + } + } + + /* Find any unbound natives + * Right now, these are not allowed + */ + IPluginContext *pContext = pPlugin->GetBaseContext(); + uint32_t num = pContext->GetNativesNum(); + sp_native_t *native; + for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) + { + break; + } + if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL)) + { + pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name); + return; + } + } + + if (pPlugin->GetStatus() == Plugin_Error) + { + /* If we got here, all natives are okay again! */ + pPlugin->m_status = Plugin_Running; + if (pPlugin->m_pRuntime->IsPaused()) + { + pPlugin->m_pRuntime->SetPauseState(false); + _SetPauseState(pPlugin, false); + } + } +} + +bool CPluginManager::UnloadPlugin(IPlugin *plugin) +{ + CPlugin *pPlugin = (CPlugin *)plugin; + + /* This prevents removal during insertion or anything else weird */ + if (m_plugins.find(pPlugin) == m_plugins.end()) + { + return false; + } + + IPluginContext *pContext = plugin->GetBaseContext(); + if (pContext != NULL && pContext->IsInExec()) + { + char buffer[255]; + UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename()); + engine->ServerCommand(buffer); + return false; + } + + /* Remove us from the lookup table and linked list */ + m_plugins.remove(pPlugin); + sm_trie_delete(m_LoadLookup, pPlugin->m_filename); + + /* Go through our libraries and tell other plugins they're gone */ + List::iterator s_iter; + for (s_iter = pPlugin->m_Libraries.begin(); + s_iter != pPlugin->m_Libraries.end(); + s_iter++) + { + OnLibraryAction((*s_iter).c_str(), true, true); + } + + List::iterator iter; + IPluginsListener *pListener; + + if (pPlugin->GetStatus() <= Plugin_Error) + { + /* Notify listeners of unloading */ + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginUnloaded(pPlugin); + } + /* Notify plugin */ + pPlugin->Call_OnPluginEnd(); + } + + pPlugin->DropEverything(); + + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + /* Notify listeners of destruction */ + pListener = (*iter); + pListener->OnPluginDestroyed(pPlugin); + } + + /* Tell the plugin to delete itself */ + delete pPlugin; + + return true; +} + +IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx) +{ + IPlugin *pPlugin; + IPluginContext *pContext; + + pContext = reinterpret_cast(const_cast(ctx)); + + if (pContext->GetKey(2, (void **)&pPlugin)) + { + return pPlugin; + } + + return NULL; +} + +CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx) +{ + return (CPlugin *)FindPluginByContext(ctx); +} + +unsigned int CPluginManager::GetPluginCount() +{ + return m_plugins.size(); +} + +void CPluginManager::AddPluginsListener(IPluginsListener *listener) +{ + m_listeners.push_back(listener); +} + +void CPluginManager::RemovePluginsListener(IPluginsListener *listener) +{ + m_listeners.remove(listener); +} + +IPluginIterator *CPluginManager::GetPluginIterator() +{ + if (m_iters.empty()) + { + return new CPluginIterator(&m_plugins); + } else { + CPluginIterator *iter = m_iters.front(); + m_iters.pop(); + iter->Reset(); + return iter; + } +} + +void CPluginManager::ReleaseIterator(CPluginIterator *iter) +{ + m_iters.push(iter); +} + +bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath) +{ + /* As an optimization, we do not call strlen, but compute the length in the first pass */ + size_t alias_len = 0; + size_t local_len = 0; + + const char *ptr = alias; + unsigned int alias_explicit_paths = 0; + unsigned int alias_path_end = 0; + while (*ptr != '\0') + { + if (*ptr == '\\' || *ptr == '/') + { + alias_explicit_paths++; + alias_path_end = alias_len; + } + alias_len++; + ptr++; + } + + if (alias_explicit_paths && alias_path_end == alias_len - 1) + { + /* Trailing slash is totally invalid here */ + return false; + } + + ptr = localpath; + unsigned int local_explicit_paths = 0; + unsigned int local_path_end = 0; + while (*ptr != '\0') + { + if (*ptr == '\\' || *ptr == '/') + { + local_explicit_paths++; + local_path_end = local_len; + } + local_len++; + ptr++; + } + + /* If the alias has more explicit paths than the real path, + * no match will be possible. + */ + if (alias_explicit_paths > local_explicit_paths) + { + return false; + } + + if (alias_explicit_paths) + { + /* We need to find if the paths match now. For example, these should all match: + * csdm csdm + * csdm optional/csdm + * csdm/ban optional/crab/csdm/ban + */ + const char *aliasptr = alias; + const char *localptr = localpath; + bool match = true; + do + { + if (*aliasptr != *localptr) + { + /* We have to knock one path off */ + local_explicit_paths--; + if (alias_explicit_paths > local_explicit_paths) + { + /* Skip out if we're gonna have an impossible match */ + return false; + } + /* Eat up localptr tokens until we get a result */ + while (((localptr - localpath) < (int)local_path_end) + && *localptr != '/' + && *localptr != '\\') + { + localptr++; + } + /* Check if we hit the end of our searchable area. + * This probably isn't possible because of the path + * count check, but it's a good idea anyway. + */ + if ((localptr - localpath) >= (int)local_path_end) + { + return false; + } else { + /* Consume the slash token */ + localptr++; + } + /* Reset the alias pointer so we can continue consuming */ + aliasptr = alias; + match = false; + continue; + } + /* Note: + * This is safe because if localptr terminates early, aliasptr will too + */ + do + { + /* We should never reach the end of the string because of this check. */ + bool aliasend = (aliasptr - alias) > (int)alias_path_end; + bool localend = (localptr - localpath) > (int)local_path_end; + if (aliasend || localend) + { + if (aliasend && localend) + { + /* we matched, and we can break out now */ + match = true; + break; + } + /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */ + match = false; + break; + } + + /* If we got here, it's safe to compare the next two tokens */ + if (*localptr != *aliasptr) + { + match = false; + break; + } + localptr++; + aliasptr++; + } while (true); + } while (!match); + } + + /* If we got here, it's time to compare filenames */ + const char *aliasptr = alias; + const char *localptr = localpath; + + if (alias_explicit_paths) + { + aliasptr = &alias[alias_path_end + 1]; + } + + if (local_explicit_paths) + { + localptr = &localpath[local_path_end + 1]; + } + + while (true) + { + if (*aliasptr == '*') + { + /* First, see if this is the last character */ + if ((unsigned)(aliasptr - alias) == alias_len - 1) + { + /* If so, there's no need to match anything else */ + return true; + } + /* Otherwise, we need to search for an appropriate matching sequence in local. + * Note that we only need to search up to the next asterisk. + */ + aliasptr++; + bool match = true; + const char *local_orig = localptr; + do + { + match = true; + while (*aliasptr != '\0' && *aliasptr != '*') + { + /* Since aliasptr is never '\0', localptr hitting the end will fail */ + if (*aliasptr != *localptr) + { + match = false; + break; + } + aliasptr++; + localptr++; + } + if (!match) + { + /* If we didn't get a match, we need to advance the search stream. + * This will let us skip tokens while still searching for another match. + */ + localptr = ++local_orig; + /* Make sure we don't go out of bounds */ + if (*localptr == '\0') + { + break; + } + } + } while (!match); + + if (!match) + { + return false; + } else { + /* If we got a match, move on to the next token */ + continue; + } + } else if (*aliasptr == '\0') { + if (*localptr == '\0' + || + strcmp(localptr, ".smx") == 0) + { + return true; + } else { + return false; + } + } else if (*aliasptr != *localptr) { + return false; + } + aliasptr++; + localptr++; + } + + return true; +} + +bool CPluginManager::IsLateLoadTime() const +{ + return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading()); +} + +void CPluginManager::OnSourceModAllInitialized() +{ + m_MyIdent = g_ShareSys.CreateCoreIdentity(); + + HandleAccess sec; + g_HandleSys.InitAccessDefaults(NULL, &sec); + + sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY; + sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY; + + g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL); + g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN"); + + g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this); + + g_ShareSys.AddInterface(NULL, this); +} + +void CPluginManager::OnSourceModShutdown() +{ + g_RootMenu.RemoveRootConsoleCommand("plugins", this); + + UnloadAll(); + + g_HandleSys.RemoveType(g_PluginType, m_MyIdent); + g_ShareSys.DestroyIdentType(g_PluginIdent); + g_ShareSys.DestroyIdentity(m_MyIdent); +} + +void CPluginManager::OnHandleDestroy(HandleType_t type, void *object) +{ + /* We don't care about the internal object, actually */ +} + +bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize) +{ + *pSize = ((CPlugin *)object)->CalcMemUsage(); + return true; +} + +IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err) +{ + IPlugin *pPlugin; + HandleError _err; + + HandleSecurity sec; + + sec.pOwner = NULL; + sec.pIdentity = m_MyIdent; + + if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None) + { + pPlugin = NULL; + } + + if (err) + { + *err = _err; + } + + return pPlugin; +} + +CPlugin *CPluginManager::GetPluginByOrder(int num) +{ + if (num < 1 || num > (int)GetPluginCount()) + { + return NULL; + } + + CPlugin *pl; + int id = 1; + + SourceHook::List::iterator iter; + for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {} + + pl = *iter; + + return pl; +} + +const char *CPluginManager::GetStatusText(PluginStatus st) +{ + switch (st) + { + case Plugin_Running: + return "Running"; + case Plugin_Paused: + return "Paused"; + case Plugin_Error: + return "Error"; + case Plugin_Uncompiled: + return "Uncompiled"; + case Plugin_BadLoad: + return "Bad Load"; + case Plugin_Failed: + return "Failed"; + default: + assert(false); + return "-"; + } +} + +void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) +{ + int argcount = command.ArgC(); + if (argcount >= 3) + { + const char *cmd = command.Arg(2); + if (strcmp(cmd, "list") == 0) + { + char buffer[256]; + unsigned int id = 1; + int plnum = GetPluginCount(); + + if (!plnum) + { + g_RootMenu.ConsolePrint("[SM] No plugins loaded"); + return; + } + else + { + g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : ""); + } + + CPlugin *pl; + SourceHook::List::iterator iter; + SourceHook::List m_FailList; + + for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++) + { + pl = (*iter); + assert(pl->GetStatus() != Plugin_Created); + int len = 0; + const sm_plugininfo_t *info = pl->GetPublicInfo(); + if (pl->GetStatus() != Plugin_Running) + { + len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus())); + + if (pl->GetStatus() <= Plugin_Error) + { + /* Plugin has failed to load. */ + m_FailList.push_back(pl); + } + } + else + { + len += UTIL_Format(buffer, sizeof(buffer), " %02d", id); + } + if (pl->GetStatus() < Plugin_Created) + { + len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); + if (IS_STR_FILLED(info->version)) + { + len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version); + } + if (IS_STR_FILLED(info->author)) + { + UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author); + } + } + else + { + UTIL_Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename); + } + g_RootMenu.ConsolePrint("%s", buffer); + } + + if (!m_FailList.empty()) + { + g_RootMenu.ConsolePrint("Load Errors:"); + + SourceHook::List::iterator _iter; + + CPlugin *pl; + + for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++) + { + pl = (CPlugin *)*_iter; + g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg); + } + } + + return; + } + else if (strcmp(cmd, "load") == 0) + { + if (argcount < 4) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load "); + return; + } + + char error[128]; + bool wasloaded; + const char *filename = command.Arg(3); + + char pluginfile[256]; + const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx"; + UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext); + + IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded); + + if (wasloaded) + { + g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile); + return; + } + + if (pl) + { + g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile); + } + else + { + g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error); + } + + return; + } + else if (strcmp(cmd, "unload") == 0) + { + if (argcount < 4) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>"); + return; + } + + CPlugin *pl; + char *end; + const char *arg = command.Arg(3); + int id = strtol(arg, &end, 10); + + if (*end == '\0') + { + pl = GetPluginByOrder(id); + if (!pl) + { + g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); + return; + } + } + else + { + char pluginfile[256]; + const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; + UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); + + if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) + { + g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); + return; + } + } + + char name[PLATFORM_MAX_PATH]; + + if (pl->GetStatus() < Plugin_Created) + { + const sm_plugininfo_t *info = pl->GetPublicInfo(); + UTIL_Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); + } + else + { + UTIL_Format(name, sizeof(name), "%s", pl->GetFilename()); + } + + if (UnloadPlugin(pl)) + { + g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name); + } + else + { + g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name); + } + + return; + } + else if (strcmp(cmd, "unload_all") == 0) + { + g_PluginSys.UnloadAll(); + g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded."); + return; + } + else if (strcmp(cmd, "load_lock") == 0) + { + if (m_LoadingLocked) + { + g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect."); + } + else + { + m_LoadingLocked = true; + g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded."); + } + return; + } + else if (strcmp(cmd, "load_unlock") == 0) + { + if (m_LoadingLocked) + { + m_LoadingLocked = false; + g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect."); + } + else + { + g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect."); + } + return; + } + else if (strcmp(cmd, "info") == 0) + { + if (argcount < 4) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>"); + return; + } + + CPlugin *pl; + char *end; + const char *arg = command.Arg(3); + int id = strtol(arg, &end, 10); + + if (*end == '\0') + { + pl = GetPluginByOrder(id); + if (!pl) + { + g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); + return; + } + } + else + { + char pluginfile[256]; + const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; + UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); + + if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) + { + g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); + return; + } + } + + const sm_plugininfo_t *info = pl->GetPublicInfo(); + + g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename()); + if (pl->GetStatus() <= Plugin_Error) + { + if (IS_STR_FILLED(info->name)) + { + if (IS_STR_FILLED(info->description)) + { + g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description); + } else { + g_RootMenu.ConsolePrint(" Title: %s", info->name); + } + } + if (IS_STR_FILLED(info->author)) + { + g_RootMenu.ConsolePrint(" Author: %s", info->author); + } + if (IS_STR_FILLED(info->version)) + { + g_RootMenu.ConsolePrint(" Version: %s", info->version); + } + if (IS_STR_FILLED(info->url)) + { + g_RootMenu.ConsolePrint(" URL: %s", info->url); + } + if (pl->GetStatus() == Plugin_Error) + { + g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg); + } + else + { + if (pl->GetStatus() == Plugin_Running) + { + g_RootMenu.ConsolePrint(" Status: running"); + } + else + { + g_RootMenu.ConsolePrint(" Status: not running"); + } + + const char *typestr = ""; + switch (pl->GetType()) + { + case PluginType_MapUpdated: + typestr = "Map Change if Updated"; + break; + case PluginType_MapOnly: + typestr = "Map Change"; + break; + case PluginType_Private: + case PluginType_Global: + typestr = "Never"; + break; + } + + g_RootMenu.ConsolePrint(" Reloads: %s", typestr); + } + if (pl->m_FileVersion >= 3) + { + g_RootMenu.ConsolePrint(" Timestamp: %s", pl->m_DateTime); + } + } + else + { + g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg); + if (pl->GetStatus() < Plugin_Created) + { + g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")", + info->name ? info->name : "", + info->version ? info->version : ""); + if (IS_STR_FILLED(info->url)) + { + g_RootMenu.ConsolePrint(" File URL: %s", info->url); + } + } + } + + return; + } + else if (strcmp(cmd, "refresh") == 0) + { + g_SourceMod.DoGlobalPluginLoads(); + g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded."); + return; + } + else if (strcmp(cmd, "reload") == 0) + { + if (argcount < 4) + { + g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>"); + return; + } + + CPlugin *pl; + char *end; + const char *arg = command.Arg(3); + int id = strtol(arg, &end, 10); + + if (*end == '\0') + { + pl = GetPluginByOrder(id); + if (!pl) + { + g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); + return; + } + } + else + { + char pluginfile[256]; + const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; + UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); + + if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) + { + g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); + return; + } + } + + char name[PLATFORM_MAX_PATH]; + const sm_plugininfo_t *info = pl->GetPublicInfo(); + strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); + + if (ReloadPlugin(pl)) + { + g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name); + } + else + { + g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name); + } + + return; + } + } + + /* Draw the main menu */ + g_RootMenu.ConsolePrint("SourceMod Plugins Menu:"); + g_RootMenu.DrawGenericOption("info", "Information about a plugin"); + g_RootMenu.DrawGenericOption("list", "Show loaded plugins"); + g_RootMenu.DrawGenericOption("load", "Load a plugin"); + g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded"); + g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading"); + g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder"); + g_RootMenu.DrawGenericOption("reload", "Reloads a plugin"); + g_RootMenu.DrawGenericOption("unload", "Unload a plugin"); + g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins"); +} + +bool CPluginManager::ReloadPlugin(CPlugin *pl) +{ + List::iterator iter; + char filename[PLATFORM_MAX_PATH]; + bool wasloaded; + PluginType ptype; + IPlugin *newpl; + int id = 1; + + strcpy(filename, pl->m_filename); + ptype = pl->GetType(); + + for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++) + { + if ((*iter) == pl) + { + break; + } + } + + if (!UnloadPlugin(pl)) + { + return false; + } + if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded))) + { + return false; + } + + for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) + { + if ((*iter) == (CPlugin *)newpl) + { + m_plugins.erase(iter); + break; + } + } + + int i; + for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i::iterator iter; + List tmp_list = m_plugins; + CPlugin *pl; + time_t t; + + for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++) + { + pl = (*iter); + if (pl->GetType() == PluginType_MapOnly) + { + UnloadPlugin((IPlugin *)pl); + } + else if (pl->GetType() == PluginType_MapUpdated) + { + t = pl->GetFileTimeStamp(); + if (!t || t > pl->GetTimeStamp()) + { + pl->SetTimeStamp(t); + UnloadPlugin((IPlugin *)pl); + } + } + } +} + +void CPluginManager::_SetPauseState(CPlugin *pl, bool paused) +{ + List::iterator iter; + IPluginsListener *pListener; + for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) + { + pListener = (*iter); + pListener->OnPluginPauseChange(pl, paused); + } +} + +void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward) +{ + List::iterator iter; + CPlugin *pPlugin; + IPluginFunction *pFunction; + + for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++) + { + pPlugin = (*iter); + + if (pPlugin->GetStatus() <= Plugin_Paused) + { + pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name); + + if (pFunction) + { + pForward->AddFunction(pFunction); + } + } + } +} + +CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken) +{ + if (pToken->type != g_PluginIdent) + { + return NULL; + } + + return (CPlugin *)(pToken->ptr); +} + +void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop) +{ + List::iterator iter; + + struct _pl + { + cell_t name; + cell_t file; + cell_t required; + } *plc; + + struct _ext + { + cell_t name; + cell_t file; + cell_t autoload; + cell_t required; + } *ext; + + const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded"; + + for (iter=m_plugins.begin(); + iter!=m_plugins.end(); + iter++) + { + CPlugin *pl = (*iter); + if (pl->GetStatus() != Plugin_Running) + { + continue; + } + IPluginContext *pContext = pl->GetBaseContext(); + IPluginFunction *pf = pContext->GetFunctionByName(name); + if (!pf) + { + continue; + } + uint32_t num_vars = pContext->GetPubVarsNum(); + for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) + { + continue; + } + if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0) + { + plc = (_pl *)pubvar->offs; + if (plc->required) + { + continue; + } + char *str; + pContext->LocalToString(plc->name, &str); + if (strcmp(str, lib) != 0) + { + continue; + } + pf->PushString(lib); + pf->Execute(NULL); + } + else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0) + { + ext = (_ext *)pubvar->offs; + if (ext->required) + { + continue; + } + char *str; + pContext->LocalToString(ext->name, &str); + if (strcmp(str, lib) != 0) + { + continue; + } + pf->PushString(lib); + pf->Execute(NULL); + } + } + } +} + +bool CPluginManager::LibraryExists(const char *lib) +{ + List::iterator iter; + + for (iter=m_plugins.begin(); + iter!=m_plugins.end(); + iter++) + { + CPlugin *pl = (*iter); + if (pl->GetStatus() != Plugin_Running) + { + continue; + } + List::iterator s_iter; + for (s_iter = pl->m_Libraries.begin(); + s_iter != pl->m_Libraries.end(); + s_iter++) + { + if ((*s_iter).compare(lib) == 0) + { + return true; + } + } + } + + return false; +} + +void CPluginManager::AllPluginsLoaded() +{ + List::iterator iter; + CPlugin *pl; + + for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) + { + pl = (*iter); + pl->Call_OnAllPluginsLoaded(); + } +} + +void CPluginManager::UnloadAll() +{ + List::iterator iter; + while ( (iter = m_plugins.begin()) != m_plugins.end() ) + { + UnloadPlugin((*iter)); + } +} + +int CPluginManager::GetOrderOfPlugin(IPlugin *pl) +{ + int id = 1; + List::iterator iter; + + for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++) + { + if ((*iter) == pl) + { + return id; + } + } + + return -1; +} + +CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg) +{ + int id; + char *end; + CPlugin *pl; + + id = strtol(arg, &end, 10); + + if (*end == '\0') + { + pl = GetPluginByOrder(id); + if (!pl) + { + return NULL; + } + } + else + { + char pluginfile[256]; + const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; + UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); + + if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) + { + return NULL; + } + } + + return pl; +} + +void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue) +{ + SyncMaxClients(newvalue); +} + +void CPluginManager::SyncMaxClients(int max_clients) +{ + List::iterator iter; + + for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++) + { + (*iter)->SyncMaxClients(max_clients); + } +} diff -r 9981b8ecf935 -r 31f49899c0ae core/PluginSys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/PluginSys.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,458 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ +#define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sm_globals.h" +#include "PluginInfoDatabase.h" +#include "sm_trie.h" +#include "sourcemod.h" +#include +#if defined ORANGEBOX_BUILD +#include "convar_sm_ob.h" +#else +#include "convar_sm.h" +#endif +#include "ITranslator.h" +#include "NativeOwner.h" +#include "ShareSys.h" + +using namespace SourceHook; + +/** + * NOTES: + * + * UPDATE 2008-03-11: These comments are horribly out of date. They paint a good overall + * picture of how PluginSys works, but things like dependencies and fake natives have + * complicated things quite a bit. + * + * Currently this system needs a lot of work but it's good skeletally. Plugin creation + * is done without actually compiling anything. This is done by Load functions in the + * manager. This will need a rewrite when we add context switching. + * + * The plugin object itself has a few things to note. The most important is that it stores + * a table of function objects. The manager marshals allocation and freeing of these objects. + * The plugin object can be in erroneous states, they are: + * Plugin_Error --> Some error occurred any time during or after compilation. + * This error can be cleared since the plugin itself is valid. + * However, the state itself being set prevents any runtime action. + * Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it. + * + * If a plugin fails to load externally, it is never added to the internal tracker. However, + * plugins that failed to load from the internal loading mechanism are always tracked. This + * allows users to see which automatically loaded plugins failed, and makes the interface a bit + * more flexible. + * + * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid + * for execution. SourceMod is a two pass system, and even though the second pass is not implemented + * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When + * implemented, a Created plugin will be switched to Error in the second pass if it not loadable. + * + * The two pass loading mechanism is described below. Modules/natives are not implemented yet. + * PASS ONE: All loadable plugins are found and have the following steps performed: + * 1. Loading and compilation is attempted. + * 2. If successful, all natives from Core are added. + * 3. OnPluginLoad() is called. + * 4. If failed, any user natives are scrapped and the process halts here. + * 5. If successful, the plugin is ready for Pass 2. + * INTERMEDIATE: + * 1. All forced modules are loaded. + * PASS TWO: All loaded plugins are found and have these steps performed: + * 1. Any modules referenced in the plugin that are not already loaded, are loaded. + * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6. + * 3. If any natives are unresolved, check if they are found in the user-natives pool. + * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter. + * 5. If the filter fails, the plugin is marked as failed. + * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped. + * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran. + * PASS THREE (not a real pass): + * 7. Once all plugins are deemed to be loaded, OnPluginStart() is called + */ + +enum LoadRes +{ + LoadRes_Successful, + LoadRes_AlreadyLoaded, + LoadRes_Failure, + LoadRes_NeverLoad +}; + +struct AutoConfig +{ + String autocfg; + String folder; + bool create; +}; + +class CPlugin; + +class CPlugin : + public IPlugin, + public CNativeOwner +{ + friend class CPluginManager; + friend class CFunction; +public: + CPlugin(const char *file); + ~CPlugin(); +public: + PluginType GetType(); + SourcePawn::IPluginContext *GetBaseContext(); + sp_context_t *GetContext(); + void *GetPluginStructure(); + const char *GetFilename(); + bool IsDebugging(); + PluginStatus GetStatus(); + const sm_plugininfo_t *GetPublicInfo(); + bool SetPauseState(bool paused); + unsigned int GetSerial(); + IdentityToken_t *GetIdentity(); + unsigned int CalcMemUsage(); + bool SetProperty(const char *prop, void *ptr); + bool GetProperty(const char *prop, void **ptr, bool remove=false); + void DropEverything(); + SourcePawn::IPluginRuntime *GetRuntime(); +public: + /** + * Creates a plugin object with default values. + * If an error buffer is specified, and an error occurs, the error will be copied to the buffer + * and NULL will be returned. + * If an error buffer is not specified, the error will be copied to an internal buffer and + * a valid (but error-stated) CPlugin will be returned. + */ + static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength); +public: + + /** + * Sets an error state on the plugin + */ + void SetErrorState(PluginStatus status, const char *error_fmt, ...); + + /** + * Initializes the plugin's identity information + */ + void InitIdentity(); + + /** + * Calls the OnPluginLoad function, and sets any failed states if necessary. + * NOTE: Valid pre-states are: Plugin_Created + * NOTE: If validated, plugin state is changed to Plugin_Loaded + * + * If the error buffer is NULL, the error message is cached locally. + */ + bool Call_AskPluginLoad(char *error, size_t maxlength); + + /** + * Calls the OnPluginStart function. + * NOTE: Valid pre-states are: Plugin_Created + * NOTE: Post-state will be Plugin_Running + */ + void Call_OnPluginStart(); + + /** + * Calls the OnPluginEnd function. + */ + void Call_OnPluginEnd(); + + /** + * Calls the OnAllPluginsLoaded function. + */ + void Call_OnAllPluginsLoaded(); + + /** + * Returns true if a plugin is usable. + */ + bool IsRunnable(); + + /** + * Get languages info. + */ + IPhraseCollection *GetPhrases(); +public: + /** + * Returns the modification time during last plugin load. + */ + time_t GetTimeStamp(); + + /** + * Returns the current modification time of the plugin file. + */ + time_t GetFileTimeStamp(); + + /** + * Returns true if the plugin was running, but is now invalid. + */ + bool WasRunning(); + + Handle_t GetMyHandle(); + + bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func); + void AddConfig(bool autoCreate, const char *cfg, const char *folder); + unsigned int GetConfigCount(); + AutoConfig *GetConfig(unsigned int i); + inline void AddLibrary(const char *name) + { + m_Libraries.push_back(name); + } + void LibraryActions(bool dropping); + void SyncMaxClients(int max_clients); +protected: + bool UpdateInfo(); + void SetTimeStamp(time_t t); + void DependencyDropped(CPlugin *pOwner); +private: + PluginType m_type; + char m_filename[PLATFORM_MAX_PATH]; + PluginStatus m_status; + unsigned int m_serial; + sm_plugininfo_t m_info; + char m_errormsg[256]; + time_t m_LastAccess; + IdentityToken_t *m_ident; + Handle_t m_handle; + bool m_WasRunning; + IPhraseCollection *m_pPhrases; + List m_RequiredLibs; + List m_Libraries; + Trie *m_pProps; + bool m_FakeNativesMissing; + bool m_LibraryMissing; + CVector m_configs; + bool m_bGotAllLoaded; + int m_FileVersion; + char m_DateTime[256]; + IPluginRuntime *m_pRuntime; + IPluginContext *m_pContext; + sp_pubvar_t *m_MaxClientsVar; +}; + +class CPluginManager : + public IPluginManager, + public SMGlobalClass, + public IHandleTypeDispatch, + public IRootConsoleCommand +{ + friend class CPlugin; +public: + CPluginManager(); + ~CPluginManager(); +public: + /* Implements iterator class */ + class CPluginIterator : public IPluginIterator + { + public: + CPluginIterator(List *mylist); + virtual ~CPluginIterator(); + virtual bool MorePlugins(); + virtual IPlugin *GetPlugin(); + virtual void NextPlugin(); + void Release(); + public: + void Reset(); + private: + List *mylist; + List::iterator current; + }; + friend class CPluginManager::CPluginIterator; +public: //IPluginManager + IPlugin *LoadPlugin(const char *path, + bool debug, + PluginType type, + char error[], + size_t maxlength, + bool *wasloaded); + bool UnloadPlugin(IPlugin *plugin); + IPlugin *FindPluginByContext(const sp_context_t *ctx); + unsigned int GetPluginCount(); + IPluginIterator *GetPluginIterator(); + void AddPluginsListener(IPluginsListener *listener); + void RemovePluginsListener(IPluginsListener *listener); +public: //SMGlobalClass + void OnSourceModAllInitialized(); + void OnSourceModShutdown(); + void OnSourceModMaxPlayersChanged(int newvalue); +public: //IHandleTypeDispatch + void OnHandleDestroy(HandleType_t type, void *object); + bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); +public: //IRootConsoleCommand + void OnRootConsoleCommand(const char *cmdname, const CCommand &command); +public: + /** + * Loads all plugins not yet loaded + */ + void LoadAll_FirstPass(const char *config, const char *basedir); + + /** + * Runs the second loading pass for all plugins + */ + void LoadAll_SecondPass(); + + /** + * Tests a plugin file mask against a local folder. + * The alias is searched backwards from localdir - i.e., given this input: + * csdm/ban csdm/ban + * ban csdm/ban + * csdm/ban optional/csdm/ban + * All of these will return true for an alias match. + * Wildcards are allowed in the filename. + */ + bool TestAliasMatch(const char *alias, const char *localdir); + + /** + * Returns whether anything loaded will be a late load. + */ + bool IsLateLoadTime() const; + + /** + * Converts a Handle to an IPlugin if possible. + */ + IPlugin *PluginFromHandle(Handle_t handle, HandleError *err); + + /** + * Finds a plugin based on its index. (starts on index 1) + */ + CPlugin *GetPluginByOrder(int num); + + int GetOrderOfPlugin(IPlugin *pl); + + /** + * Internal version of FindPluginByContext() + */ + CPlugin *GetPluginByCtx(const sp_context_t *ctx); + + /** + * Gets status text for a status code + */ + const char *GetStatusText(PluginStatus status); + + /** + * Reload or update plugins on level shutdown. + */ + void ReloadOrUnloadPlugins(); + + /** + * Add public functions from all running or paused + * plugins to the specified forward if the names match. + */ + void AddFunctionsToForward(const char *name, IChangeableForward *pForward); + + /** + * Iterates through plugins to call OnAllPluginsLoaded. + */ + void AllPluginsLoaded(); + + CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken); + + void Shutdown(); + + void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop); + + bool LibraryExists(const char *lib); + + bool ReloadPlugin(CPlugin *pl); + + void UnloadAll(); + + CPlugin *FindPluginByConsoleArg(const char *arg); + + void SyncMaxClients(int max_clients); +private: + LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength); + + void LoadAutoPlugin(const char *plugin); + + /** + * Recursively loads all plugins in the given directory. + */ + void LoadPluginsFromDir(const char *basedir, const char *localdir); + + /** + * Adds a plugin object. This is wrapped by LoadPlugin functions. + */ + void AddPlugin(CPlugin *pPlugin); + + /** + * Runs the second loading pass on a plugin. + */ + bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength); + + /** + * Runs an extension pass on a plugin. + */ + bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength); + + /** + * Manages required natives. + */ + bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength); + + void _SetPauseState(CPlugin *pPlugin, bool pause); +protected: + /** + * Caching internal objects + */ + void ReleaseIterator(CPluginIterator *iter); +public: + inline IdentityToken_t *GetIdentity() + { + return m_MyIdent; + } +private: + void TryRefreshDependencies(CPlugin *pOther); +private: + List m_listeners; + List m_plugins; + CStack m_iters; + CPluginInfoDatabase m_PluginInfo; + Trie *m_LoadLookup; + bool m_AllPluginsLoaded; + IdentityToken_t *m_MyIdent; + + /* Dynamic native stuff */ + List m_Natives; + + bool m_LoadingLocked; +}; + +extern CPluginManager g_PluginSys; + +#endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/ShareSys.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ShareSys.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,529 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "ShareSys.h" +#include "HandleSys.h" +#include "ExtensionSys.h" +#include "LibrarySys.h" +#include "PluginSys.h" +#include "sm_stringutil.h" + +ShareSystem g_ShareSys; +static unsigned int g_mark_serial = 0; + +ShareSystem::ShareSystem() +{ + m_IdentRoot.ident = 0; + m_TypeRoot = 0; + m_IfaceType = 0; + m_CoreType = 0; +} + +IdentityToken_t *ShareSystem::CreateCoreIdentity() +{ + if (!m_CoreType) + { + m_CoreType = CreateIdentType("CORE"); + } + + return CreateIdentity(m_CoreType, this); +} + +void ShareSystem::Initialize() +{ + TypeAccess sec; + + g_HandleSys.InitAccessDefaults(&sec, NULL); + sec.ident = GetIdentRoot(); + + m_TypeRoot = g_HandleSys.CreateType("Identity", this, 0, &sec, NULL, NULL, NULL); + m_IfaceType = g_HandleSys.CreateType("Interface", this, 0, NULL, NULL, GetIdentRoot(), NULL); + + /* Initialize our static identity handle */ + m_IdentRoot.ident = g_HandleSys.CreateHandle(m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL); + + /* Add the Handle System and others... they are too innocent and pure to do it themselves */ + AddInterface(NULL, &g_HandleSys); + AddInterface(NULL, &g_LibSys); +} + +void ShareSystem::OnSourceModShutdown() +{ + if (m_CoreType) + { + g_HandleSys.RemoveType(m_CoreType, GetIdentRoot()); + } + + g_HandleSys.RemoveType(m_IfaceType, GetIdentRoot()); + g_HandleSys.RemoveType(m_TypeRoot, GetIdentRoot()); +} + +IdentityType_t ShareSystem::FindIdentType(const char *name) +{ + HandleType_t type; + + if (g_HandleSys.FindHandleType(name, &type)) + { + if (g_HandleSys.TypeCheck(type, m_TypeRoot)) + { + return type; + } + } + + return 0; +} + +IdentityType_t ShareSystem::CreateIdentType(const char *name) +{ + if (!m_TypeRoot) + { + return 0; + } + + return g_HandleSys.CreateType(name, this, m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL); +} + +void ShareSystem::OnHandleDestroy(HandleType_t type, void *object) +{ + /* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */ +} + +IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type, void *ptr) +{ + if (!m_TypeRoot) + { + return 0; + } + + /* :TODO: Cache? */ + IdentityToken_t *pToken = new IdentityToken_t; + + HandleSecurity sec; + sec.pOwner = sec.pIdentity = GetIdentRoot(); + + pToken->ident = g_HandleSys.CreateHandleInt(type, NULL, &sec, NULL, NULL, true); + pToken->ptr = ptr; + pToken->type = type; + + return pToken; +} + +bool ShareSystem::AddInterface(IExtension *myself, SMInterface *iface) +{ + if (!iface) + { + return false; + } + + IfaceInfo info; + + info.owner = myself; + info.iface = iface; + + m_Interfaces.push_back(info); + + return true; +} + +bool ShareSystem::RequestInterface(const char *iface_name, + unsigned int iface_vers, + IExtension *myself, + SMInterface **pIface) +{ + /* See if the interface exists */ + List::iterator iter; + SMInterface *iface; + IExtension *iface_owner; + bool found = false; + for (iter=m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++) + { + IfaceInfo &info = (*iter); + iface = info.iface; + if (strcmp(iface->GetInterfaceName(), iface_name) == 0) + { + if (iface->GetInterfaceVersion() == iface_vers + || iface->IsVersionCompatible(iface_vers)) + { + iface_owner = info.owner; + found = true; + break; + } + } + } + + if (!found) + { + return false; + } + + /* Add a dependency node */ + if (iface_owner) + { + IfaceInfo info; + info.iface = iface; + info.owner = iface_owner; + g_Extensions.BindDependency(myself, &info); + } + + if (pIface) + { + *pIface = iface; + } + + return true; +} + +void ShareSystem::AddNatives(IExtension *myself, const sp_nativeinfo_t *natives) +{ + CNativeOwner *pOwner; + + pOwner = g_Extensions.GetNativeOwner(myself); + + pOwner->AddNatives(natives); +} + +void ShareSystem::DestroyIdentity(IdentityToken_t *identity) +{ + HandleSecurity sec; + + sec.pOwner = GetIdentRoot(); + sec.pIdentity = GetIdentRoot(); + + g_HandleSys.FreeHandle(identity->ident, &sec); + delete identity; +} + +void ShareSystem::DestroyIdentType(IdentityType_t type) +{ + g_HandleSys.RemoveType(type, GetIdentRoot()); +} + +void ShareSystem::RemoveInterfaces(IExtension *pExtension) +{ + List::iterator iter = m_Interfaces.begin(); + + while (iter != m_Interfaces.end()) + { + if ((*iter).owner == pExtension) + { + iter = m_Interfaces.erase(iter); + } + else + { + iter++; + } + } +} + +void ShareSystem::AddDependency(IExtension *myself, const char *filename, bool require, bool autoload) +{ + g_Extensions.AddDependency(myself, filename, require, autoload); +} + +void ShareSystem::RegisterLibrary(IExtension *myself, const char *name) +{ + g_Extensions.AddLibrary(myself, name); +} + +void ShareSystem::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives) +{ + unsigned int i; + NativeEntry *pEntry; + CNativeOwner *pOwner; + + pOwner = g_Extensions.GetNativeOwner(myself); + + for (i = 0; natives[i].func != NULL && natives[i].name != NULL; i++) + { + if ((pEntry = FindNative(natives[i].name)) == NULL) + { + continue; + } + + if (pEntry->owner != g_pCoreNatives) + { + continue; + } + + if (pEntry->replacement.owner != NULL) + { + continue; + } + + /* Now it's safe to add the override */ + pEntry->replacement.func = natives[i].func; + pEntry->replacement.owner = pOwner; + pOwner->AddReplacedNative(pEntry); + } +} + +NativeEntry *ShareSystem::FindNative(const char *name) +{ + NativeEntry **ppEntry; + + if ((ppEntry = m_NtvCache.retrieve(name)) == NULL) + { + return NULL; + } + + return *ppEntry; +} + +void ShareSystem::BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly) +{ + NativeEntry *pEntry; + sp_native_t *native; + uint32_t i, native_count; + IPluginContext *pContext; + + pContext = pPlugin->GetBaseContext(); + + /* Generate a new serial ID, mark our dependencies with it. */ + g_mark_serial++; + pPlugin->PropogateMarkSerial(g_mark_serial); + + native_count = pContext->GetNativesNum(); + for (i = 0; i < native_count; i++) + { + if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE) + { + continue; + } + + /* If we're bound, check if there is a replacement available. + * If not, this native is totally finalized. + */ + if (native->status == SP_NATIVE_BOUND) + { + pEntry = (NativeEntry *)native->user; + assert(pEntry != NULL); + if (pEntry->replacement.owner == NULL + || (pEntry->replacement.owner != NULL + && pEntry->replacement.func == native->pfn)) + { + continue; + } + } + /* Otherwise, the native must be in our cache. */ + else if ((pEntry = FindNative(native->name)) == NULL) + { + continue; + } + + if (bCoreOnly && pEntry->owner != g_pCoreNatives) + { + continue; + } + + BindNativeToPlugin(pPlugin, native, i, pEntry); + } +} + +void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, NativeEntry *pEntry) +{ + uint32_t i; + sp_native_t *native; + IPluginContext *pContext; + + pContext = pPlugin->GetBaseContext(); + + if (pContext->FindNativeByName(pEntry->name, &i) != SP_ERROR_NONE) + { + return; + } + if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE) + { + return; + } + + if (native->status == SP_NATIVE_BOUND) + { + return; + } + + BindNativeToPlugin(pPlugin, native, i, pEntry); +} + +void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, + sp_native_t *native, + uint32_t index, + NativeEntry *pEntry) +{ + /* Mark as bound... we do the rest next. */ + native->status = SP_NATIVE_BOUND; + native->user = pEntry; + + /* See if a replacement is available. */ + if (pEntry->replacement.owner != NULL) + { + /* Perform a replacement bind. */ + native->pfn = pEntry->replacement.func; + pEntry->replacement.owner->AddWeakRef(WeakNative(pPlugin, index, pEntry)); + } + else + { + /* Perform a normal bind. */ + native->pfn = pEntry->func; + + /* We don't bother with dependency crap if the owner is Core. */ + if (pEntry->owner != g_pCoreNatives) + { + /* The native is optional, this is a special case */ + if ((native->flags & SP_NTVFLAG_OPTIONAL) == SP_NTVFLAG_OPTIONAL) + { + /* Only add if there is a valid owner. */ + if (pEntry->owner != NULL) + { + pEntry->owner->AddWeakRef(WeakNative(pPlugin, index)); + } + else + { + native->status = SP_NATIVE_UNBOUND; + } + } + /* Otherwise, we're a strong dependent and not a weak one */ + else + { + /* See if this has already been marked as a dependent. + * If it has, it means this relationship has already occurred, + * and there is no reason to do it again. + */ + if (pEntry->owner != pPlugin + && pEntry->owner->GetMarkSerial() != g_mark_serial) + { + /* This has not been marked as a dependency yet */ + //pPlugin->AddDependency(pEntry->owner); + pEntry->owner->AddDependent(pPlugin); + pEntry->owner->SetMarkSerial(g_mark_serial); + } + } + } + } +} + +NativeEntry *ShareSystem::AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv) +{ + NativeEntry *pEntry; + + if ((pEntry = FindNative(ntv->name)) == NULL) + { + pEntry = new NativeEntry; + + pEntry->owner = pOwner; + pEntry->name = ntv->name; + pEntry->func = ntv->func; + pEntry->replacement.func = NULL; + pEntry->replacement.owner = NULL; + pEntry->fake = NULL; + + m_NtvCache.insert(ntv->name, pEntry); + + return pEntry; + } + + if (pEntry->owner != NULL) + { + return NULL; + } + + pEntry->owner = pOwner; + pEntry->func = ntv->func; + pEntry->name = ntv->name; + + return pEntry; +} + +void ShareSystem::ClearNativeFromCache(CNativeOwner *pOwner, const char *name) +{ + NativeEntry *pEntry; + + if ((pEntry = FindNative(name)) == NULL) + { + return; + } + + if (pEntry->owner != pOwner) + { + return; + } + + if (pEntry->fake != NULL) + { + g_pSourcePawn2->DestroyFakeNative(pEntry->func); + delete pEntry->fake; + pEntry->fake = NULL; + } + + pEntry->func = NULL; + pEntry->name = NULL; + pEntry->owner = NULL; + pEntry->replacement.func = NULL; + pEntry->replacement.owner = NULL; +} + +NativeEntry *ShareSystem::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func) +{ + FakeNative *pFake; + NativeEntry *pEntry; + SPVM_NATIVE_FUNC gate; + + if ((pEntry = FindNative(name)) != NULL && pEntry->owner != NULL) + { + return NULL; + } + + pFake = new FakeNative; + + if ((gate = g_pSourcePawn2->CreateFakeNative(func, pFake)) == NULL) + { + delete pFake; + return NULL; + } + + if (pEntry == NULL) + { + pEntry = new NativeEntry; + m_NtvCache.insert(name, pEntry); + } + + pFake->call = pFunc; + pFake->ctx = pFunc->GetParentContext(); + strncopy(pFake->name, name, sizeof(pFake->name)); + + pEntry->fake = pFake; + pEntry->func = gate; + pEntry->name = pFake->name; + pEntry->owner = g_PluginSys.GetPluginByCtx(pFake->ctx->GetContext()); + pEntry->replacement.func = NULL; + pEntry->replacement.owner = NULL; + + return pEntry; +} diff -r 9981b8ecf935 -r 31f49899c0ae core/ShareSys.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/ShareSys.h Mon Nov 24 08:39:46 2008 -0600 @@ -0,0 +1,150 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_SHARESYSTEM_H_ +#define _INCLUDE_SOURCEMOD_SHARESYSTEM_H_ + +#include +#include +#include +#include +#include "sm_globals.h" +#include "sourcemod.h" + +using namespace SourceHook; + +namespace SourceMod +{ + struct IdentityToken_t + { + Handle_t ident; + void *ptr; + IdentityType_t type; + }; +}; + +struct IfaceInfo +{ + bool operator ==(const IfaceInfo &info) + { + return (info.iface == iface && info.owner == owner); + } + SMInterface *iface; + IExtension *owner; +}; + +class CNativeOwner; +class CPlugin; +struct NativeEntry; + +struct ReplaceNative +{ + CNativeOwner *owner; + SPVM_NATIVE_FUNC func; +}; + +struct FakeNative +{ + char name[64]; + IPluginContext *ctx; + IPluginFunction *call; +}; + +struct NativeEntry +{ + CNativeOwner *owner; + SPVM_NATIVE_FUNC func; + const char *name; + ReplaceNative replacement; + FakeNative *fake; +}; + +class ShareSystem : + public IShareSys, + public SMGlobalClass, + public IHandleTypeDispatch +{ + friend class CNativeOwner; +public: + ShareSystem(); +public: //IShareSys + bool AddInterface(IExtension *myself, SMInterface *pIface); + bool RequestInterface(const char *iface_name, + unsigned int iface_vers, + IExtension *myself, + SMInterface **pIface); + void AddNatives(IExtension *myself, const sp_nativeinfo_t *natives); + IdentityType_t CreateIdentType(const char *name); + IdentityType_t FindIdentType(const char *name); + IdentityToken_t *CreateIdentity(IdentityType_t type, void *ptr); + void DestroyIdentType(IdentityType_t type); + void DestroyIdentity(IdentityToken_t *identity); + void AddDependency(IExtension *myself, const char *filename, bool require, bool autoload); + void RegisterLibrary(IExtension *myself, const char *name); + void OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives); +public: //SMGlobalClass + /* Pre-empt in case anything tries to register idents early */ + void Initialize(); + void OnSourceModShutdown(); +public: //IHandleTypeDispatch + void OnHandleDestroy(HandleType_t type, void *object); +public: + IdentityToken_t *CreateCoreIdentity(); + void RemoveInterfaces(IExtension *pExtension); +public: + inline IdentityToken_t *GetIdentRoot() + { + return &m_IdentRoot; + } +public: + void BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly); + void BindNativeToPlugin(CPlugin *pPlugin, NativeEntry *pEntry); + NativeEntry *AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func); +private: + NativeEntry *AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv); + void ClearNativeFromCache(CNativeOwner *pOwner, const char *name); + NativeEntry *FindNative(const char *name); + void BindNativeToPlugin(CPlugin *pPlugin, + sp_native_t *ntv, + uint32_t index, + NativeEntry *pEntry); +private: + List m_Interfaces; + HandleType_t m_TypeRoot; + IdentityToken_t m_IdentRoot; + HandleType_t m_IfaceType; + IdentityType_t m_CoreType; + KTrie m_NtvCache; +}; + +extern ShareSystem g_ShareSys; + +#endif //_INCLUDE_SOURCEMOD_SHARESYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/msvc8/sourcemod_mm.vcproj --- a/core/msvc8/sourcemod_mm.vcproj Mon Nov 24 08:25:36 2008 -0600 +++ b/core/msvc8/sourcemod_mm.vcproj Mon Nov 24 08:39:46 2008 -0600 @@ -1291,31 +1291,31 @@ UniqueIdentifier="{7D3AB33D-C1AD-46ee-B505-FEA8B6EF18E4}" > @@ -1325,31 +1325,31 @@ UniqueIdentifier="{D1864A10-FF5F-4672-A70E-57C9F7486F92}" > diff -r 9981b8ecf935 -r 31f49899c0ae core/msvc9/sourcemod_mm.vcproj --- a/core/msvc9/sourcemod_mm.vcproj Mon Nov 24 08:25:36 2008 -0600 +++ b/core/msvc9/sourcemod_mm.vcproj Mon Nov 24 08:39:46 2008 -0600 @@ -854,6 +854,14 @@ > + + + + @@ -870,6 +878,14 @@ > + + + + @@ -918,10 +934,22 @@ > + + + + + + @@ -1036,6 +1064,14 @@ > + + + + @@ -1052,6 +1088,14 @@ > + + + + @@ -1100,10 +1144,22 @@ > + + + + + + @@ -1274,79 +1330,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff -r 9981b8ecf935 -r 31f49899c0ae core/sourcemod.cpp --- a/core/sourcemod.cpp Mon Nov 24 08:25:36 2008 -0600 +++ b/core/sourcemod.cpp Mon Nov 24 08:39:46 2008 -0600 @@ -32,7 +32,7 @@ #include #include "sourcemod.h" #include "sourcemm_api.h" -#include "systems/LibrarySys.h" +#include "LibrarySys.h" #include #include "PluginSys.h" #include "ShareSys.h" diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/ExtensionSys.cpp --- a/core/systems/ExtensionSys.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1208 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include "ExtensionSys.h" -#include "LibrarySys.h" -#include "ShareSys.h" -#include "Logger.h" -#include "sourcemm_api.h" -#include "sm_srvcmds.h" -#include "sm_stringutil.h" - -CExtensionManager g_Extensions; -IdentityType_t g_ExtType; - -void CExtension::Initialize(const char *filename, const char *path) -{ - m_pAPI = NULL; - m_pIdentToken = NULL; - unload_code = 0; - m_bFullyLoaded = false; - m_File.assign(filename); - m_Path.assign(path); -} - -CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path) -{ - Initialize(filename, path); - m_pAPI = pAPI; -} - -CLocalExtension::CLocalExtension(const char *filename) -{ - m_PlId = 0; - m_pLib = NULL; - - char path[PLATFORM_MAX_PATH]; - - /* First see if there is an engine specific build! */ - g_SourceMod.BuildPath(Path_SM, - path, - PLATFORM_MAX_PATH, -#if defined METAMOD_PLAPI_VERSION -#if defined ORANGEBOX_BUILD - "extensions/auto.2.ep2/%s", -#else - "extensions/auto.2.ep1/%s", -#endif //ORANGEBOX_BUILD -#else //METAMOD_PLAPI_VERSION - "extensions/auto.1.ep1/%s", -#endif //METAMOD_PLAPI_VERSION - filename); - - /* Try the "normal" version */ - if (!g_LibSys.IsPathFile(path)) - { - g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename); - } - - Initialize(filename, path); -} - -bool CRemoteExtension::Load(char *error, size_t maxlength) -{ - if (!PerformAPICheck(error, maxlength)) - { - m_pAPI = NULL; - return false; - } - - if (!CExtension::Load(error, maxlength)) - { - m_pAPI = NULL; - return false; - } - - return true; -} - -bool CLocalExtension::Load(char *error, size_t maxlength) -{ - m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength); - - if (m_pLib == NULL) - { - return false; - } - - typedef IExtensionInterface *(*GETAPI)(); - GETAPI pfnGetAPI = NULL; - - if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL) - { - m_pLib->CloseLibrary(); - m_pLib = NULL; - snprintf(error, maxlength, "Unable to find extension entry point"); - return false; - } - - m_pAPI = pfnGetAPI(); - - /* Check pointer and version */ - if (!PerformAPICheck(error, maxlength)) - { - m_pLib->CloseLibrary(); - m_pLib = NULL; - m_pAPI = NULL; - return false; - } - - /* Load as MM:S */ - if (m_pAPI->IsMetamodExtension()) - { - bool already; - m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength); - } - - if (!CExtension::Load(error, maxlength)) - { - if (m_pAPI->IsMetamodExtension()) - { - if (m_PlId) - { - char dummy[255]; - g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy)); - m_PlId = 0; - } - } - m_pLib->CloseLibrary(); - m_pLib = NULL; - m_pAPI = NULL; - return false; - } - - return true; -} - -void CRemoteExtension::Unload() -{ -} - -void CLocalExtension::Unload() -{ - if (m_pAPI != NULL && m_PlId) - { - char temp_buffer[255]; - g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer)); - m_PlId = 0; - } - - if (m_pLib != NULL) - { - m_pLib->CloseLibrary(); - m_pLib = NULL; - } -} - -bool CRemoteExtension::IsExternal() -{ - return true; -} - -bool CLocalExtension::IsExternal() -{ - return false; -} - -CExtension::~CExtension() -{ - DestroyIdentity(); -} - -bool CExtension::PerformAPICheck(char *error, size_t maxlength) -{ - if (!m_pAPI) - { - snprintf(error, maxlength, "No IExtensionInterface instance provided"); - return false; - } - - if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) - { - snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION); - return false; - } - - return true; -} - -bool CExtension::Load(char *error, size_t maxlength) -{ - CreateIdentity(); - if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading())) - { - DestroyIdentity(); - return false; - } - else - { - /* Check if we're past load time */ - if (!g_SourceMod.IsMapLoading()) - { - m_pAPI->OnExtensionsAllLoaded(); - } - } - - return true; -} - -void CExtension::CreateIdentity() -{ - if (m_pIdentToken != NULL) - { - return; - } - - m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this); -} - -void CExtension::DestroyIdentity() -{ - if (m_pIdentToken == NULL) - { - return; - } - - g_ShareSys.DestroyIdentity(m_pIdentToken); - m_pIdentToken = NULL; -} - -void CExtension::MarkAllLoaded() -{ - assert(m_bFullyLoaded == false); - if (!m_bFullyLoaded) - { - m_bFullyLoaded = true; - m_pAPI->OnExtensionsAllLoaded(); - } -} - -void CExtension::AddPlugin(CPlugin *pPlugin) -{ - /* Unfortunately we have to do this :( */ - if (m_Dependents.find(pPlugin) != m_Dependents.end()) - { - m_Dependents.push_back(pPlugin); - } -} - -void CExtension::SetError(const char *error) -{ - m_Error.assign(error); -} - -IExtensionInterface *CExtension::GetAPI() -{ - return m_pAPI; -} - -const char *CExtension::GetFilename() -{ - return m_File.c_str(); -} - -IdentityToken_t *CExtension::GetIdentity() -{ - return m_pIdentToken; -} - -bool CRemoteExtension::IsLoaded() -{ - return (m_pAPI != NULL); -} - -bool CLocalExtension::IsLoaded() -{ - return (m_pLib != NULL); -} - -void CExtension::AddDependency(const IfaceInfo *pInfo) -{ - if (m_Deps.find(*pInfo) == m_Deps.end()) - { - m_Deps.push_back(*pInfo); - } -} - -bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2) -{ - return (i1.iface == i2.iface) && (i1.owner == i2.owner); -} - -void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface) -{ - IfaceInfo info; - info.iface = iface; - info.owner = pOther; - - List::iterator iter; - for (iter = m_ChildDeps.begin(); - iter != m_ChildDeps.end(); - iter++) - { - IfaceInfo &other = (*iter); - if (other == info) - { - return; - } - } - - m_ChildDeps.push_back(info); -} - -ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) -{ - List::iterator iter = m_Deps.begin(); - - if (iter == m_Deps.end()) - { - return NULL; - } - - if (pOwner) - { - *pOwner = (*iter).owner; - } - if (pInterface) - { - *pInterface = (*iter).iface; - } - - List::iterator *pIter = new List::iterator(iter); - - return (ITERATOR *)pIter; -} - -bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) -{ - List::iterator *pIter = (List::iterator *)iter; - List::iterator _iter; - - if (_iter == m_Deps.end()) - { - return false; - } - - _iter++; - - if (pOwner) - { - *pOwner = (*_iter).owner; - } - if (pInterface) - { - *pInterface = (*_iter).iface; - } - - *pIter = _iter; - - if (_iter == m_Deps.end()) - { - return false; - } - - return true; -} - -void CExtension::FreeDependencyIterator(ITERATOR *iter) -{ - List::iterator *pIter = (List::iterator *)iter; - - delete pIter; -} - -void CExtension::AddInterface(SMInterface *pInterface) -{ - m_Interfaces.push_back(pInterface); -} - -bool CExtension::IsRunning(char *error, size_t maxlength) -{ - if (!IsLoaded()) - { - if (error) - { - snprintf(error, maxlength, "%s", m_Error.c_str()); - } - return false; - } - - return m_pAPI->QueryRunning(error, maxlength); -} - -void CExtension::AddLibrary(const char *library) -{ - m_Libraries.push_back(library); -} - -/********************* - * EXTENSION MANAGER * - *********************/ - -void CExtensionManager::OnSourceModAllInitialized() -{ - g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); - g_PluginSys.AddPluginsListener(this); - g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this); - g_ShareSys.AddInterface(NULL, this); -} - -void CExtensionManager::OnSourceModShutdown() -{ - g_RootMenu.RemoveRootConsoleCommand("exts", this); - g_ShareSys.DestroyIdentType(g_ExtType); -} - -void CExtensionManager::Shutdown() -{ - List::iterator iter; - - while ((iter = m_Libs.begin()) != m_Libs.end()) - { - UnloadExtension((*iter)); - } -} - -void CExtensionManager::TryAutoload() -{ - char path[PLATFORM_MAX_PATH]; - - g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions"); - - IDirectory *pDir = g_LibSys.OpenDirectory(path); - if (!pDir) - { - return; - } - - const char *lfile; - size_t len; - while (pDir->MoreFiles()) - { - if (pDir->IsEntryDirectory()) - { - pDir->NextEntry(); - continue; - } - - lfile = pDir->GetEntryName(); - len = strlen(lfile); - if (len <= 9) /* size of ".autoload" */ - { - pDir->NextEntry(); - continue; - } - - if (strcmp(&lfile[len - 9], ".autoload") != 0) - { - pDir->NextEntry(); - continue; - } - - char file[PLATFORM_MAX_PATH]; - len = UTIL_Format(file, sizeof(file), "%s", lfile); - strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT); - - LoadAutoExtension(file); - - pDir->NextEntry(); - } -} - -IExtension *CExtensionManager::LoadAutoExtension(const char *path) -{ - if (!strstr(path, "." PLATFORM_LIB_EXT)) - { - char newpath[PLATFORM_MAX_PATH]; - g_LibSys.PathFormat(newpath, sizeof(newpath), "%s.%s", path, PLATFORM_LIB_EXT); - return LoadAutoExtension(newpath); - } - - IExtension *pAlready; - if ((pAlready=FindExtensionByFile(path)) != NULL) - { - return pAlready; - } - - char error[256]; - CExtension *p = new CLocalExtension(path); - - /* We put us in the list beforehand so extensions that check for each other - * won't recursively load each other. - */ - m_Libs.push_back(p); - - if (!p->Load(error, sizeof(error)) || !p->IsLoaded()) - { - g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error); - p->SetError(error); - } - - return p; -} - -IExtension *CExtensionManager::FindExtensionByFile(const char *file) -{ - List::iterator iter; - CExtension *pExt; - - /* Chomp off the path */ - char lookup[PLATFORM_MAX_PATH]; - g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file); - - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) - { - pExt = (*iter); - char short_file[PLATFORM_MAX_PATH]; - g_LibSys.GetFileFromPath(short_file, sizeof(short_file), pExt->GetFilename()); - if (strcmp(lookup, short_file) == 0) - { - return pExt; - } - } - - /* If we got no results, test if there was a platform extension. - * If not, add one. - */ - if (!strstr(file, "." PLATFORM_LIB_EXT)) - { - char path[PLATFORM_MAX_PATH]; - UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT); - return FindExtensionByFile(path); - } - - return NULL; -} - -IExtension *CExtensionManager::FindExtensionByName(const char *ext) -{ - List::iterator iter; - CExtension *pExt; - IExtensionInterface *pAPI; - const char *name; - - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) - { - pExt = (*iter); - if (!pExt->IsLoaded()) - { - continue; - } - if ((pAPI = pExt->GetAPI()) == NULL) - { - continue; - } - name = pAPI->GetExtensionName(); - if (!name) - { - continue; - } - if (strcmp(name, ext) == 0) - { - return pExt; - } - } - - return NULL; -} - -IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength) -{ - IExtension *pAlready; - if ((pAlready=FindExtensionByFile(file)) != NULL) - { - return pAlready; - } - - CExtension *pExt = new CLocalExtension(file); - - if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) - { - pExt->Unload(); - delete pExt; - return NULL; - } - - /* :TODO: do QueryRunning check if the map is loaded */ - - m_Libs.push_back(pExt); - - return pExt; -} - -void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo) -{ - CExtension *pExt = (CExtension *)pRequester; - CExtension *pOwner = (CExtension *)pInfo->owner; - - pExt->AddDependency(pInfo); - - IExtensionInterface *pAPI = pExt->GetAPI(); - if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface)) - { - pOwner->AddChildDependent(pExt, pInfo->iface); - } -} - -void CExtensionManager::AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface) -{ - CExtension *pExt = (CExtension *)ext; - CExtension *pOwner = GetExtensionFromIdent(other); - - IfaceInfo info; - - info.iface = (SMInterface *)iface; - info.owner = pOwner; - - pExt->AddDependency(&info); - pOwner->AddChildDependent(pExt, (SMInterface *)iface); -} - -void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface) -{ - CExtension *pExt = (CExtension *)pOwner; - - pExt->AddInterface(pInterface); -} - -void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin ) -{ - CExtension *pExt = (CExtension *)pParent; - - pExt->AddPlugin(pPlugin); -} - -void CExtensionManager::OnPluginDestroyed(IPlugin *plugin) -{ - List::iterator iter; - - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) - { - (*iter)->DropRefsTo((CPlugin *)plugin); - } -} - -CExtension *CExtensionManager::FindByOrder(unsigned int num) -{ - if (num < 1 || num > m_Libs.size()) - { - return NULL; - } - - List::iterator iter = m_Libs.begin(); - - while (iter != m_Libs.end()) - { - if (--num == 0) - { - return (*iter); - } - iter++; - } - - return NULL; -} - -bool CExtensionManager::UnloadExtension(IExtension *_pExt) -{ - if (!_pExt) - { - return false; - } - - CExtension *pExt = (CExtension *)_pExt; - - if (m_Libs.find(pExt) == m_Libs.end()) - { - return false; - } - - /* First remove us from internal lists */ - g_ShareSys.RemoveInterfaces(_pExt); - m_Libs.remove(pExt); - - List UnloadQueue; - - /* Handle dependencies */ - if (pExt->IsLoaded()) - { - /* Unload any dependent plugins */ - List::iterator p_iter = pExt->m_Dependents.begin(); - while (p_iter != pExt->m_Dependents.end()) - { - /* We have to manually unlink ourselves here, since we're no longer being managed */ - g_PluginSys.UnloadPlugin((*p_iter)); - p_iter = pExt->m_Dependents.erase(p_iter); - } - - List::iterator s_iter; - for (s_iter = pExt->m_Libraries.begin(); - s_iter != pExt->m_Libraries.end(); - s_iter++) - { - g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true); - } - - /* Notify and/or unload all dependencies */ - List::iterator c_iter; - CExtension *pDep; - IExtensionInterface *pAPI; - for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++) - { - pDep = (*c_iter); - if ((pAPI=pDep->GetAPI()) == NULL) - { - continue; - } - if (pDep == pExt) - { - continue; - } - /* Now, get its dependency list */ - bool dropped = false; - List::iterator i_iter = pDep->m_Deps.begin(); - while (i_iter != pDep->m_Deps.end()) - { - if ((*i_iter).owner == _pExt) - { - if (!pAPI->QueryInterfaceDrop((*i_iter).iface)) - { - if (!dropped) - { - dropped = true; - UnloadQueue.push_back(pDep); - } - } - pAPI->NotifyInterfaceDrop((*i_iter).iface); - i_iter = pDep->m_Deps.erase(i_iter); - } - else - { - i_iter++; - } - } - /* Flush out any back references to this plugin */ - i_iter = pDep->m_ChildDeps.begin(); - while (i_iter != pDep->m_ChildDeps.end()) - { - if ((*i_iter).owner == pExt) - { - i_iter = pDep->m_ChildDeps.erase(i_iter); - } - else - { - i_iter++; - } - } - } - - /* Unbind our natives from Core */ - pExt->DropEverything(); - } - - IdentityToken_t *pIdentity; - if ((pIdentity = pExt->GetIdentity()) != NULL) - { - SMGlobalClass *glob = SMGlobalClass::head; - while (glob) - { - glob->OnSourceModIdentityDropped(pIdentity); - glob = glob->m_pGlobalClassNext; - } - } - - /* Tell it to unload */ - if (pExt->IsLoaded()) - { - IExtensionInterface *pAPI = pExt->GetAPI(); - pAPI->OnExtensionUnload(); - } - - pExt->Unload(); - delete pExt; - - List::iterator iter; - for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++) - { - /* NOTE: This is safe because the unload function backs out of anything not present */ - UnloadExtension((*iter)); - } - - return true; -} - -void CExtensionManager::MarkAllLoaded() -{ - List::iterator iter; - CExtension *pExt; - - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) - { - pExt = (*iter); - if (!pExt->IsLoaded()) - { - continue; - } - if (pExt->m_bFullyLoaded) - { - continue; - } - pExt->MarkAllLoaded(); - } -} - -void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload) -{ - /* This function doesn't really need to do anything now. We make sure the - * other extension is loaded, but handling of dependencies is really done - * by the interface fetcher. - */ - if (required || autoload) - { - LoadAutoExtension(file); - } -} - -void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) -{ - int argcount = command.ArgC(); - if (argcount >= 3) - { - const char *cmd = command.Arg(2); - if (strcmp(cmd, "list") == 0) - { - List::iterator iter; - CExtension *pExt; - unsigned int num = 1; - switch (m_Libs.size()) - { - case 1: - { - g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:"); - break; - } - case 0: - { - g_RootMenu.ConsolePrint("[SM] No extensions are loaded."); - break; - } - default: - { - g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size()); - break; - } - } - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++) - { - pExt = (*iter); - if (pExt->IsLoaded()) - { - char error[255]; - if (!pExt->IsRunning(error, sizeof(error))) - { - g_RootMenu.ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), error); - } - else - { - IExtensionInterface *pAPI = pExt->GetAPI(); - const char *name = pAPI->GetExtensionName(); - const char *version = pAPI->GetExtensionVerString(); - const char *descr = pAPI->GetExtensionDescription(); - g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr); - } - } else { - g_RootMenu.ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str()); - } - } - return; - } - else if (strcmp(cmd, "info") == 0) - { - if (argcount < 4) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>"); - return; - } - - const char *sId = command.Arg(3); - unsigned int id = atoi(sId); - if (id <= 0) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>"); - return; - } - - if (m_Libs.size() == 0) - { - g_RootMenu.ConsolePrint("[SM] No extensions are loaded."); - return; - } - - if (id > m_Libs.size()) - { - g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id); - return; - } - - List::iterator iter = m_Libs.begin(); - CExtension *pExt = NULL; - while (iter != m_Libs.end()) - { - if (--id == 0) - { - pExt = (*iter); - break; - } - iter++; - } - /* This should never happen */ - if (!pExt) - { - g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id); - return; - } - - if (!pExt->IsLoaded()) - { - g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); - g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str()); - } - else - { - char error[255]; - if (!pExt->IsRunning(error, sizeof(error))) - { - g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); - g_RootMenu.ConsolePrint(" Loaded: Yes"); - g_RootMenu.ConsolePrint(" Running: No (%s)", error); - } - else - { - IExtensionInterface *pAPI = pExt->GetAPI(); - g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); - g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString()); - g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription()); - g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL()); - g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString()); - - if (pExt->IsExternal()) - { - g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod"); - } - else if (pAPI->IsMetamodExtension()) - { - g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source"); - } - else - { - g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod"); - } - } - } - return; - } - else if (strcmp(cmd, "unload") == 0) - { - if (argcount < 4) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]"); - return; - } - - const char *arg = command.Arg(3); - unsigned int num = atoi(arg); - CExtension *pExt = FindByOrder(num); - - if (!pExt) - { - g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num); - return; - } - - if (argcount > 4 && pExt->unload_code) - { - const char *unload = command.Arg(4); - if (pExt->unload_code == (unsigned)atoi(unload)) - { - char filename[PLATFORM_MAX_PATH]; - snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); - UnloadExtension(pExt); - g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename); - } - else - { - g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code); - } - return; - } - - if (!pExt->IsLoaded() - || (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size())) - { - char filename[PLATFORM_MAX_PATH]; - snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); - UnloadExtension(pExt); - g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename); - return; - } - else - { - List plugins; - if (pExt->m_ChildDeps.size()) - { - g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename()); - List::iterator iter; - CExtension *pOther; - /* Get list of all extensions */ - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) - { - List::iterator i_iter; - pOther = (*iter); - if (!pOther->IsLoaded() || pOther == pExt) - { - continue; - } - /* Get their dependencies */ - for (i_iter=pOther->m_Deps.begin(); - i_iter!=pOther->m_Deps.end(); - i_iter++) - { - /* Is this dependency to us? */ - if ((*i_iter).owner != pExt) - { - continue; - } - /* Will our dependent care? */ - if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface)) - { - g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename()); - /* Add to plugin unload list */ - List::iterator p_iter; - for (p_iter=pOther->m_Dependents.begin(); - p_iter!=pOther->m_Dependents.end(); - p_iter++) - { - if (plugins.find((*p_iter)) == plugins.end()) - { - plugins.push_back((*p_iter)); - } - } - } - } - } - } - if (pExt->m_Dependents.size()) - { - g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename()); - List::iterator iter; - CPlugin *pPlugin; - for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++) - { - pPlugin = (*iter); - if (plugins.find(pPlugin) == plugins.end()) - { - plugins.push_back(pPlugin); - } - } - for (iter = plugins.begin(); iter != plugins.end(); iter++) - { - pPlugin = (*iter); - g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename()); - } - } - srand(static_cast(time(NULL))); - pExt->unload_code = (rand() % 877) + 123; //123 to 999 - g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename()); - g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code); - - return; - } - } - } - - g_RootMenu.ConsolePrint("SourceMod Extensions Menu:"); - g_RootMenu.DrawGenericOption("info", "Extra extension information"); - g_RootMenu.DrawGenericOption("list", "List extensions"); - g_RootMenu.DrawGenericOption("unload", "Unload an extension"); -} - -CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr) -{ - if (ptr->type == g_ExtType) - { - return (CExtension *)(ptr->ptr); - } - - return NULL; -} - -CExtensionManager::CExtensionManager() -{ -} - -CExtensionManager::~CExtensionManager() -{ -} - -void CExtensionManager::AddLibrary(IExtension *pSource, const char *library) -{ - CExtension *pExt = (CExtension *)pSource; - pExt->AddLibrary(library); - g_PluginSys.OnLibraryAction(library, false, false); -} - -bool CExtensionManager::LibraryExists(const char *library) -{ - CExtension *pExt; - - for (List::iterator iter = m_Libs.begin(); - iter != m_Libs.end(); - iter++) - { - pExt = (*iter); - for (List::iterator s_iter = pExt->m_Libraries.begin(); - s_iter != pExt->m_Libraries.end(); - s_iter++) - { - if ((*s_iter).compare(library) == 0) - { - return true; - } - } - } - - return false; -} - -IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface, - const char *filepath, - const char *filename, - char *error, - size_t maxlength) -{ - IExtension *pAlready; - if ((pAlready=FindExtensionByFile(filename)) != NULL) - { - return pAlready; - } - - CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath); - - if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) - { - pExt->Unload(); - delete pExt; - return NULL; - } - - m_Libs.push_back(pExt); - - return pExt; -} - -void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) -{ - IExtensionInterface *pAPI; - List::iterator iter; - - for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) - { - if ((pAPI = (*iter)->GetAPI()) == NULL) - { - continue; - } - if (pAPI->GetExtensionVersion() > 3) - { - pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax); - } - } -} diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/ExtensionSys.h --- a/core/systems/ExtensionSys.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,186 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_EXTENSION_SYSTEM_H_ -#define _INCLUDE_SOURCEMOD_EXTENSION_SYSTEM_H_ - -#include -#include -#include -#include -#include -#include "sm_globals.h" -#include "ShareSys.h" -#include -#include -#include -#include "PluginSys.h" -#include "NativeOwner.h" - -using namespace SourceMod; -using namespace SourceHook; - -class CExtension; - -struct sm_extnative_t -{ - CExtension *owner; - const sp_nativeinfo_t *info; -}; - -class CExtension : - public IExtension, - public CNativeOwner -{ - friend class CExtensionManager; -public: - virtual ~CExtension(); -public: //IExtension - IExtensionInterface *GetAPI(); - const char *GetFilename(); - IdentityToken_t *GetIdentity(); - ITERATOR *FindFirstDependency(IExtension **pOwner, SMInterface **pInterface); - bool FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface); - void FreeDependencyIterator(ITERATOR *iter); - bool IsRunning(char *error, size_t maxlength); -public: - void SetError(const char *error); - void AddDependency(const IfaceInfo *pInfo); - void AddChildDependent(CExtension *pOther, SMInterface *iface); - void AddInterface(SMInterface *pInterface); - void AddPlugin(CPlugin *pPlugin); - void MarkAllLoaded(); - void AddLibrary(const char *library); -public: - virtual bool Load(char *error, size_t maxlength); - virtual bool IsLoaded() =0; - virtual void Unload() =0; -protected: - void Initialize(const char *filename, const char *path); - bool PerformAPICheck(char *error, size_t maxlength); - void CreateIdentity(); - void DestroyIdentity(); -protected: - IdentityToken_t *m_pIdentToken; - IExtensionInterface *m_pAPI; - String m_File; - String m_Path; - String m_Error; - List m_Deps; /** Dependencies */ - List m_ChildDeps; /** Children who might depend on us */ - List m_Interfaces; - List m_Libraries; - unsigned int unload_code; - bool m_bFullyLoaded; -}; - -class CLocalExtension : public CExtension -{ -public: - CLocalExtension(const char *filename); -public: - bool Load(char *error, size_t maxlength); - bool IsLoaded(); - void Unload(); - bool IsExternal(); -private: - PluginId m_PlId; - ILibrary *m_pLib; -}; - -class CRemoteExtension : public CExtension -{ -public: - CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path); -public: - bool Load(char *error, size_t maxlength); - bool IsLoaded(); - void Unload(); - bool IsExternal(); -}; - -class CExtensionManager : - public IExtensionManager, - public SMGlobalClass, - public IPluginsListener, - public IRootConsoleCommand -{ -public: - CExtensionManager(); - ~CExtensionManager(); -public: //SMGlobalClass - void OnSourceModAllInitialized(); - void OnSourceModShutdown(); -public: //IExtensionManager - IExtension *LoadExtension(const char *path, - char *error, - size_t maxlength); - bool UnloadExtension(IExtension *pExt); - IExtension *FindExtensionByFile(const char *file); - IExtension *FindExtensionByName(const char *ext); - IExtension *LoadExternal(IExtensionInterface *pInterface, - const char *filepath, - const char *filename, - char *error, - size_t maxlength); -public: //IPluginsListener - void OnPluginDestroyed(IPlugin *plugin); -public: //IRootConsoleCommand - void OnRootConsoleCommand(const char *cmdname, const CCommand &command); -public: - IExtension *LoadAutoExtension(const char *path); - void BindDependency(IExtension *pOwner, IfaceInfo *pInfo); - void AddInterface(IExtension *pOwner, SMInterface *pInterface); - void BindChildPlugin(IExtension *pParent, CPlugin *pPlugin); - void MarkAllLoaded(); - void AddDependency(IExtension *pSource, const char *file, bool required, bool autoload); - void TryAutoload(); - void AddLibrary(IExtension *pSource, const char *library); - bool LibraryExists(const char *library); - void CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax); - void AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface); -public: - CExtension *GetExtensionFromIdent(IdentityToken_t *ptr); - void Shutdown(); - CNativeOwner *GetNativeOwner(IExtension *pExt) - { - CExtension *p = (CExtension *)pExt; - return p; - } -private: - CExtension *FindByOrder(unsigned int num); -private: - List m_Libs; -}; - -extern CExtensionManager g_Extensions; - -#endif //_INCLUDE_SOURCEMOD_EXTENSION_SYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/ForwardSys.cpp --- a/core/systems/ForwardSys.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,751 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include -#include -#include "ForwardSys.h" -#include "PluginSys.h" -#include "ShareSys.h" -#include "DebugReporter.h" - -CForwardManager g_Forwards; - -/** - * Genesis turns to its source, reduction occurs stepwise although the essence is all one. - * End of line. FTL system check. - * - * :TODO: WHAT NEEDS TO BE TESTED IN THIS BEAST - * VARARG FUNCTIONS: - * - Pushing no varargs - */ - -// :TODO: IMPORTANT!!! The result pointer arg in the execute function maybe invalid if the forward fails -// so later evaluation of this result may cause problems on higher levels of abstraction. DOCUMENT OR FIX ALL FORWARDS! - -CForwardManager::~CForwardManager() -{ - CStack::iterator iter; - for (iter=m_FreeForwards.begin(); iter!=m_FreeForwards.end(); iter++) - { - delete (*iter); - } - m_FreeForwards.popall(); -} - -void CForwardManager::OnSourceModAllInitialized() -{ - g_PluginSys.AddPluginsListener(this); - g_ShareSys.AddInterface(NULL, this); -} - -IForward *CForwardManager::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, ...) -{ - CForward *fwd; - va_list ap; - va_start(ap, types); - - fwd = CForward::CreateForward(name, et, num_params, types, ap); - - va_end(ap); - - if (fwd) - { - g_PluginSys.AddFunctionsToForward(name, fwd); - - m_managed.push_back(fwd); - } - - return fwd; -} - -IChangeableForward *CForwardManager::CreateForwardEx(const char *name, ExecType et, int num_params, const ParamType *types, ...) -{ - CForward *fwd; - va_list ap; - va_start(ap, types); - - fwd = CForward::CreateForward(name, et, num_params, types, ap); - - va_end(ap); - - if (fwd) - { - m_unmanaged.push_back(fwd); - } - - return fwd; -} - -void CForwardManager::OnPluginLoaded(IPlugin *plugin) -{ - /* Attach any globally managed forwards */ - List::iterator iter; - CForward *fwd; - - for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) - { - fwd = (*iter); - IPluginFunction *pFunc = plugin->GetBaseContext()->GetFunctionByName(fwd->GetForwardName()); - if (pFunc) - { - fwd->AddFunction(pFunc); - } - } -} - -void CForwardManager::OnPluginUnloaded(IPlugin *plugin) -{ - List::iterator iter; - CForward *fwd; - - for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) - { - fwd = (*iter); - fwd->RemoveFunctionsOfPlugin(plugin); - } - - for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++) - { - fwd = (*iter); - fwd->RemoveFunctionsOfPlugin(plugin); - } -} - -IForward *CForwardManager::FindForward(const char *name, IChangeableForward **ifchng) -{ - List::iterator iter; - CForward *fwd; - - for (iter=m_managed.begin(); iter!=m_managed.end(); iter++) - { - fwd = (*iter); - if (strcmp(fwd->GetForwardName(), name) == 0) - { - if (ifchng) - { - *ifchng = NULL; - } - return fwd; - } - } - - for (iter=m_unmanaged.begin(); iter!=m_unmanaged.end(); iter++) - { - fwd = (*iter); - if (strcmp(fwd->GetForwardName(), name) == 0) - { - if (ifchng) - { - *ifchng = fwd; - } - return fwd; - } - } - - if (ifchng) - { - *ifchng = NULL; - } - - return NULL; -} - -void CForwardManager::ReleaseForward(IForward *forward) -{ - ForwardFree(static_cast(forward)); -} - -void CForwardManager::ForwardFree(CForward *fwd) -{ - if (fwd == NULL) - { - return; - } - - m_FreeForwards.push(fwd); - m_managed.remove(fwd); -} - -CForward *CForwardManager::ForwardMake() -{ - CForward *fwd; - if (m_FreeForwards.empty()) - { - fwd = new CForward; - } else { - fwd = m_FreeForwards.front(); - m_FreeForwards.pop(); - } - return fwd; -} - -void CForwardManager::OnPluginPauseChange(IPlugin *plugin, bool paused) -{ - /* No longer used */ -} - -/************************************* - * ACTUAL FORWARD API IMPLEMENTATION * - *************************************/ - -CForward *CForward::CreateForward(const char *name, ExecType et, unsigned int num_params, const ParamType *types, va_list ap) -{ - ParamType _types[SP_MAX_EXEC_PARAMS]; - - if (num_params > SP_MAX_EXEC_PARAMS) - { - return NULL; - } - - if (types == NULL && num_params) - { - for (unsigned int i=0; im_curparam = 0; - pForward->m_ExecType = et; - snprintf(pForward->m_name, FORWARDS_NAME_MAX, "%s", name ? name : ""); - - for (unsigned int i=0; im_types[i] = _types[i]; - } - - if (num_params && _types[num_params-1] == Param_VarArgs) - { - pForward->m_varargs = num_params--; - } else { - pForward->m_varargs = false; - } - - pForward->m_numparams = num_params; - pForward->m_errstate = SP_ERROR_NONE; - - pForward->m_functions.clear(); - - return pForward; -} - -int CForward::Execute(cell_t *result, IForwardFilter *filter) -{ - if (m_errstate) - { - int err = m_errstate; - Cancel(); - return err; - } - - FuncIter iter = m_functions.begin(); - IPluginFunction *func; - cell_t cur_result = 0; - cell_t high_result = 0; - cell_t low_result = 0; - int err; - unsigned int failed=0, success=0; - unsigned int num_params = m_curparam; - FwdParamInfo temp_info[SP_MAX_EXEC_PARAMS]; - FwdParamInfo *param; - ParamType type; - - /* Save local, reset */ - memcpy(temp_info, m_params, sizeof(m_params)); - m_curparam = 0; - - for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) - { - func = (*iter); - - for (unsigned int i=0; i= m_numparams || m_types[i] == Param_Any) - { - type = param->pushedas; - } - else - { - type = m_types[i]; - } - - if ((i >= m_numparams) || (type & SP_PARAMFLAG_BYREF)) - { - /* If we're byref or we're vararg, we always push everything by ref. - * Even if they're byval, we must push them byref. - */ - if (type == Param_String) - { - err = func->PushStringEx((char *)param->byref.orig_addr, param->byref.cells, param->byref.sz_flags, param->byref.flags); - } - else if (type == Param_Float || type == Param_Cell) - { - err = func->PushCellByRef(¶m->val); - } - else - { - err = func->PushArray(param->byref.orig_addr, param->byref.cells, param->byref.flags); - assert(type == Param_Array || type == Param_FloatByRef || type == Param_CellByRef); - } - } - else - { - /* If we're not byref or not vararg, our job is a bit easier. */ - assert(type == Param_Cell || type == Param_Float); - err = func->PushCell(param->val); - } - - if (err != SP_ERROR_NONE) - { - g_DbgReporter.GenerateError(func->GetParentContext(), - func->GetFunctionID(), - err, - "Failed to push parameter while executing forward"); - continue; - } - } - - /* Call the function and deal with the return value. */ - if ((err=func->Execute(&cur_result)) != SP_ERROR_NONE) - { - failed++; - } - else - { - success++; - switch (m_ExecType) - { - case ET_Event: - { - if (cur_result > high_result) - { - high_result = cur_result; - } - break; - } - case ET_Hook: - { - if (cur_result > high_result) - { - high_result = cur_result; - if ((ResultType)high_result == Pl_Stop) - { - goto done; - } - } - break; - } - case ET_LowEvent: - { - /* Check if the current result is the lowest so far (or if it's the first result) */ - if (cur_result < low_result || success == 1) - { - low_result = cur_result; - } - break; - } - default: - { - break; - } - } - } - } - -done: - - if (success) - { - switch (m_ExecType) - { - case ET_Ignore: - { - cur_result = 0; - break; - } - case ET_Event: - case ET_Hook: - { - cur_result = high_result; - break; - } - case ET_LowEvent: - { - cur_result = low_result; - break; - } - default: - { - break; - } - } - - if (result) - { - *result = cur_result; - } - } - - return SP_ERROR_NONE; -} - -int CForward::PushCell(cell_t cell) -{ - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_Cell; - } else if (m_types[m_curparam] != Param_Cell) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_Cell; - } - - m_params[m_curparam++].val = cell; - - return SP_ERROR_NONE; -} - -int CForward::PushFloat(float number) -{ - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_Float; - } else if (m_types[m_curparam] != Param_Float) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_Float; - } - - m_params[m_curparam++].val = *(cell_t *)&number; - - return SP_ERROR_NONE; -} - -int CForward::PushCellByRef(cell_t *cell, int flags) -{ - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_CellByRef; - } else if (m_types[m_curparam] != Param_CellByRef) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_CellByRef; - } - - _Int_PushArray(cell, 1, flags); - m_curparam++; - - return SP_ERROR_NONE; -} - -int CForward::PushFloatByRef(float *num, int flags) -{ - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_FloatByRef; - } else if (m_types[m_curparam] != Param_FloatByRef) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_numparams > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_FloatByRef; - } - - _Int_PushArray((cell_t *)num, 1, flags); - m_curparam++; - - return SP_ERROR_NONE; -} - -void CForward::_Int_PushArray(cell_t *inarray, unsigned int cells, int flags) -{ - m_params[m_curparam].byref.cells = cells; - m_params[m_curparam].byref.flags = flags; - m_params[m_curparam].byref.orig_addr = inarray; -} - -int CForward::PushArray(cell_t *inarray, unsigned int cells, int flags) -{ - /* We don't allow this here */ - if (!inarray) - { - return SetError(SP_ERROR_PARAM); - } - - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_Array; - } else if (m_types[m_curparam] != Param_Array) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_Array; - } - - _Int_PushArray(inarray, cells, flags); - - m_curparam++; - - return SP_ERROR_NONE; -} - -void CForward::_Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags) -{ - m_params[m_curparam].byref.cells = cells; /* Notice this contains the char count not cell count */ - m_params[m_curparam].byref.flags = cp_flags; - m_params[m_curparam].byref.orig_addr = inarray; - m_params[m_curparam].byref.sz_flags = sz_flags; -} - -int CForward::PushString(const char *string) -{ - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_String; - } else if (m_types[m_curparam] != Param_String) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_String; - } - - _Int_PushString((cell_t *)string, strlen(string)+1, SM_PARAM_STRING_COPY, 0); - m_curparam++; - - return SP_ERROR_NONE; -} - -int CForward::PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags) -{ - if (m_curparam < m_numparams) - { - if (m_types[m_curparam] == Param_Any) - { - m_params[m_curparam].pushedas = Param_String; - } else if (m_types[m_curparam] != Param_String) { - return SetError(SP_ERROR_PARAM); - } - } else { - if (!m_varargs || m_curparam > SP_MAX_EXEC_PARAMS) - { - return SetError(SP_ERROR_PARAMS_MAX); - } - m_params[m_curparam].pushedas = Param_String; - } - - _Int_PushString((cell_t *)buffer, length, sz_flags, cp_flags); - m_curparam++; - - return SP_ERROR_NONE; -} - -void CForward::Cancel() -{ - if (!m_curparam) - { - return; - } - - m_curparam = 0; - m_errstate = SP_ERROR_NONE; -} - -bool CForward::AddFunction(IPluginContext *pContext, funcid_t index) -{ - IPluginFunction *pFunc = pContext->GetFunctionById(index); - - if (!pFunc) - { - return false; - } - - return AddFunction(pFunc); -} - -bool CForward::RemoveFunction(IPluginContext *pContext, funcid_t index) -{ - IPluginFunction *pFunc = pContext->GetFunctionById(index); - - if (!pFunc) - { - return false; - } - - return RemoveFunction(pFunc); -} - -bool CForward::RemoveFunction(IPluginFunction *func) -{ - bool found = false; - FuncIter iter; - List *lst; - - if (func->IsRunnable()) - { - lst = &m_functions; - } else { - lst = &m_paused; - } - - for (iter=m_functions.begin(); iter!=m_functions.end(); iter++) - { - if ((*iter) == func) - { - found = true; - lst->erase(iter); - break; - } - } - - /* Cancel a call, if any */ - if (found || m_curparam) - { - func->Cancel(); - } - - return found; -} - -unsigned int CForward::RemoveFunctionsOfPlugin(IPlugin *plugin) -{ - FuncIter iter; - IPluginFunction *func; - unsigned int removed = 0; - IPluginContext *pContext = plugin->GetBaseContext(); - for (iter=m_functions.begin(); iter!=m_functions.end();) - { - func = (*iter); - if (func->GetParentContext() == pContext) - { - iter = m_functions.erase(iter); - removed++; - } else { - iter++; - } - } - - return removed; -} - -bool CForward::AddFunction(IPluginFunction *func) -{ - if (m_curparam) - { - return false; - } - - if (func->IsRunnable()) - { - m_functions.push_back(func); - } else { - m_paused.push_back(func); - } - - return true; -} - -const char *CForward::GetForwardName() -{ - return m_name; -} - -unsigned int CForward::GetFunctionCount() -{ - return m_functions.size(); -} - -ExecType CForward::GetExecType() -{ - return m_ExecType; -} diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/ForwardSys.h --- a/core/systems/ForwardSys.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,158 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ -#define _INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ - -#include -#include -#include "sm_globals.h" -#include -#include -#include "sourcemod.h" - -using namespace SourceHook; - -typedef List::iterator FuncIter; - -/* :TODO: a global name max define for sourcepawn, should mirror compiler's sNAMEMAX */ -#define FORWARDS_NAME_MAX 64 - -struct ByrefInfo -{ - unsigned int cells; - cell_t *orig_addr; - int flags; - int sz_flags; -}; - -struct FwdParamInfo -{ - cell_t val; - ByrefInfo byref; - ParamType pushedas; -}; - -class CForward : public IChangeableForward -{ -public: //ICallable - virtual int PushCell(cell_t cell); - virtual int PushCellByRef(cell_t *cell, int flags); - virtual int PushFloat(float number); - virtual int PushFloatByRef(float *number, int flags); - virtual int PushArray(cell_t *inarray, unsigned int cells, int flags); - virtual int PushString(const char *string); - virtual int PushStringEx(char *buffer, size_t length, int sz_flags, int cp_flags); - virtual void Cancel(); -public: //IForward - virtual const char *GetForwardName(); - virtual unsigned int GetFunctionCount(); - virtual ExecType GetExecType(); - virtual int Execute(cell_t *result, IForwardFilter *filter); -public: //IChangeableForward - virtual bool RemoveFunction(IPluginFunction *func); - virtual unsigned int RemoveFunctionsOfPlugin(IPlugin *plugin); - virtual bool AddFunction(IPluginFunction *func); - virtual bool AddFunction(IPluginContext *ctx, funcid_t index); - virtual bool RemoveFunction(IPluginContext *ctx, funcid_t index); -public: - static CForward *CreateForward(const char *name, - ExecType et, - unsigned int num_params, - const ParamType *types, - va_list ap); -private: - void _Int_PushArray(cell_t *inarray, unsigned int cells, int flags); - void _Int_PushString(cell_t *inarray, unsigned int cells, int sz_flags, int cp_flags); - inline int SetError(int err) - { - m_errstate = err; - return err; - } -protected: - /* :TODO: I want a caching list type here. - * Destroying these things and using new/delete for their members feels bad. - */ - mutable List m_functions; - mutable List m_paused; - - /* Type and name information */ - FwdParamInfo m_params[SP_MAX_EXEC_PARAMS]; - ParamType m_types[SP_MAX_EXEC_PARAMS]; - char m_name[FORWARDS_NAME_MAX+1]; - unsigned int m_numparams; - unsigned int m_varargs; - ExecType m_ExecType; - - /* State information */ - unsigned int m_curparam; - int m_errstate; -}; - -class CForwardManager : - public IForwardManager, - public IPluginsListener, - public SMGlobalClass -{ - friend class CForward; -public: - ~CForwardManager(); -public: //IForwardManager - IForward *CreateForward(const char *name, - ExecType et, - unsigned int num_params, - const ParamType *types, - ...); - IChangeableForward *CreateForwardEx(const char *name, - ExecType et, - int num_params, - const ParamType *types, - ...); - IForward *FindForward(const char *name, IChangeableForward **ifchng); - void ReleaseForward(IForward *forward); -public: //IPluginsListener - void OnPluginLoaded(IPlugin *plugin); - void OnPluginUnloaded(IPlugin *plugin); - void OnPluginPauseChange(IPlugin *plugin, bool paused); -public: //SMGlobalClass - void OnSourceModAllInitialized(); -protected: - CForward *ForwardMake(); - void ForwardFree(CForward *fwd); -private: - CStack m_FreeForwards; - List m_managed; - List m_unmanaged; -}; - -extern CForwardManager g_Forwards; - -#endif //_INCLUDE_SOURCEMOD_FORWARDSYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/HandleSys.cpp --- a/core/systems/HandleSys.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1076 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include "HandleSys.h" -#include "ShareSys.h" -#include "PluginSys.h" -#include "ExtensionSys.h" -#include "Logger.h" -#include -#include -#include "sm_stringutil.h" - -HandleSystem g_HandleSys; - -QHandle *ignore_handle; - -inline HandleType_t TypeParent(HandleType_t type) -{ - return (type & ~HANDLESYS_SUBTYPE_MASK); -} - -inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index) -{ - return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index); -} - -HandleSystem::HandleSystem() -{ - m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1]; - memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1)); - - m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE]; - memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE); - - m_TypeLookup = sm_trie_create(); - m_strtab = new BaseStringTable(512); - - m_TypeTail = 0; -} - -HandleSystem::~HandleSystem() -{ - delete [] m_Handles; - delete [] m_Types; - sm_trie_destroy(m_TypeLookup); - delete m_strtab; -} - - -HandleType_t HandleSystem::CreateType(const char *name, - IHandleTypeDispatch *dispatch, - HandleType_t parent, - const TypeAccess *typeAccess, - const HandleAccess *hndlAccess, - IdentityToken_t *ident, - HandleError *err) -{ - if (!dispatch) - { - if (err) - { - *err = HandleError_Parameter; - } - return 0; - } - - if (typeAccess && typeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) - { - if (err) - { - *err = HandleError_Version; - } - return 0; - } - - if (hndlAccess && hndlAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) - { - if (err) - { - *err = HandleError_Version; - } - return 0; - } - - bool isChild = false; - - if (parent != 0) - { - isChild = true; - if (parent & HANDLESYS_SUBTYPE_MASK) - { - if (err) - { - *err = HandleError_NoInherit; - } - return 0; - } - if (parent >= HANDLESYS_TYPEARRAY_SIZE - || m_Types[parent].dispatch == NULL) - { - if (err) - { - *err = HandleError_Parameter; - } - return 0; - } - if (m_Types[parent].typeSec.access[HTypeAccess_Inherit] == false - && (m_Types[parent].typeSec.ident != ident)) - { - if (err) - { - *err = HandleError_Access; - } - return 0; - } - } - - if (name && name[0] != '\0') - { - if (sm_trie_retrieve(m_TypeLookup, name, NULL)) - { - if (err) - { - *err = HandleError_Parameter; - } - return 0; - } - } - - unsigned int index; - - if (isChild) - { - QHandleType *pParent = &m_Types[parent]; - if (pParent->children >= HANDLESYS_MAX_SUBTYPES) - { - if (err) - { - *err = HandleError_Limit; - } - return 0; - } - index = 0; - for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) - { - if (m_Types[parent + i].dispatch == NULL) - { - index = parent + i; - break; - } - } - if (!index) - { - if (err) - { - *err = HandleError_Limit; - } - return 0; - } - pParent->children++; - } else { - if (m_FreeTypes == 0) - { - /* Reserve another index */ - if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE) - { - if (err) - { - *err = HandleError_Limit; - } - return 0; - } else { - m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1); - index = m_TypeTail; - } - } else { - /* The "free array" is compacted into the normal array for easiness */ - index = m_Types[m_FreeTypes--].freeID; - } - } - - QHandleType *pType = &m_Types[index]; - - pType->dispatch = dispatch; - if (name && name[0] != '\0') - { - pType->nameIdx = m_strtab->AddString(name); - sm_trie_insert(m_TypeLookup, name, (void *)pType); - } else { - pType->nameIdx = -1; - } - - pType->opened = 0; - - if (typeAccess) - { - pType->typeSec = *typeAccess; - } else { - InitAccessDefaults(&pType->typeSec, NULL); - pType->typeSec.ident = ident; - } - - if (hndlAccess) - { - pType->hndlSec = *hndlAccess; - } else { - InitAccessDefaults(NULL, &pType->hndlSec); - } - - if (!isChild) - { - pType->children = 0; - } - - return index; -} - -bool HandleSystem::FindHandleType(const char *name, HandleType_t *type) -{ - QHandleType *_type; - - if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type)) - { - return false; - } - - unsigned int offset = _type - m_Types; - - if (type) - { - *type = offset; - } - - return true; -} - -HandleError HandleSystem::TryAllocHandle(unsigned int *handle) -{ - if (m_FreeHandles == 0) - { - if (m_HandleTail >= HANDLESYS_MAX_HANDLES) - { - return HandleError_Limit;; - } - *handle = ++m_HandleTail; - } - else - { - *handle = m_Handles[m_FreeHandles--].freeID; - } - - return HandleError_None; -} - -HandleError HandleSystem::MakePrimHandle(HandleType_t type, - QHandle **in_pHandle, - unsigned int *in_index, - Handle_t *in_handle, - IdentityToken_t *owner, - bool identity) -{ - HandleError err; - unsigned int owner_index = 0; - - if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None)) - { - return HandleError_Identity; - } - - unsigned int handle; - if ((err = TryAllocHandle(&handle)) != HandleError_None) - { - if (!TryAndFreeSomeHandles() - || (err = TryAllocHandle(&handle)) != HandleError_None) - { - return err; - } - } - - QHandle *pHandle = &m_Handles[handle]; - - assert(pHandle->set == false); - - if (++m_HSerial >= HANDLESYS_MAX_SERIALS) - { - m_HSerial = 1; - } - - /* Set essential information */ - pHandle->set = identity ? HandleSet_Identity : HandleSet_Used; - pHandle->refcount = 1; - pHandle->type = type; - pHandle->serial = m_HSerial; - pHandle->owner = owner; - pHandle->ch_next = 0; - pHandle->access_special = false; - pHandle->is_destroying = false; - - /* Create the hash value */ - Handle_t hash = pHandle->serial; - hash <<= 16; - hash |= handle; - - /* Add a reference count to the type */ - m_Types[type].opened++; - - /* Output */ - *in_pHandle = pHandle; - *in_index = handle; - *in_handle = hash; - - /* Decode the identity token - * For now, we don't allow nested ownership - */ - if (owner && !identity) - { - QHandle *pIdentity = &m_Handles[owner_index]; - if (pIdentity->ch_prev == 0) - { - pIdentity->ch_prev = handle; - pIdentity->ch_next = handle; - pHandle->ch_prev = 0; - } - else - { - /* Link previous node to us (forward) */ - m_Handles[pIdentity->ch_next].ch_next = handle; - /* Link us to previous node (backwards) */ - pHandle->ch_prev = pIdentity->ch_next; - /* Set new tail */ - pIdentity->ch_next = handle; - } - pIdentity->refcount++; - } - else - { - pHandle->ch_prev = 0; - } - - return HandleError_None; -} - -void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken) -{ - if (!type - || type >= HANDLESYS_TYPEARRAY_SIZE - || m_Types[type].dispatch == NULL) - { - return; - } - - m_Types[type].typeSec.ident = pToken; -} - -Handle_t HandleSystem::CreateHandleInt(HandleType_t type, - void *object, - const HandleSecurity *pSec, - HandleError *err, - const HandleAccess *pAccess, - bool identity) -{ - IdentityToken_t *ident; - IdentityToken_t *owner; - - if (pSec) - { - ident = pSec->pIdentity; - owner = pSec->pOwner; - } else { - ident = NULL; - owner = NULL; - } - - if (!type - || type >= HANDLESYS_TYPEARRAY_SIZE - || m_Types[type].dispatch == NULL) - { - if (err) - { - *err = HandleError_Parameter; - } - return 0; - } - - /* Check to see if we're allowed to create this handle type */ - QHandleType *pType = &m_Types[type]; - if (!pType->typeSec.access[HTypeAccess_Create] - && (!pType->typeSec.ident - || pType->typeSec.ident != ident)) - { - if (err) - { - *err = HandleError_Access; - } - return 0; - } - - unsigned int index; - Handle_t handle; - QHandle *pHandle; - HandleError _err; - - if ((_err=MakePrimHandle(type, &pHandle, &index, &handle, owner, identity)) != HandleError_None) - { - if (err) - { - *err = _err; - } - return 0; - } - - if (pAccess) - { - pHandle->access_special = true; - pHandle->sec = *pAccess; - } - - pHandle->object = object; - pHandle->clone = 0; - - return handle; -} - -Handle_t HandleSystem::CreateHandleEx(HandleType_t type, void *object, const HandleSecurity *pSec, const HandleAccess *pAccess, HandleError *err) -{ - return CreateHandleInt(type, object, pSec, err, pAccess, false); -} - -Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *owner, IdentityToken_t *ident, HandleError *err) -{ - HandleSecurity sec; - - sec.pIdentity = ident; - sec.pOwner = owner; - - return CreateHandleEx(type, object, &sec, NULL, err); -} - -bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype) -{ - /* Check the type inheritance */ - if (intype & HANDLESYS_SUBTYPE_MASK) - { - if (intype != outtype - && (TypeParent(intype) != TypeParent(outtype))) - { - return false; - } - } else { - if (intype != outtype) - { - return false; - } - } - - return true; -} - -HandleError HandleSystem::GetHandle(Handle_t handle, - IdentityToken_t *ident, - QHandle **in_pHandle, - unsigned int *in_index, - bool ignoreFree) -{ - unsigned int serial = (handle >> 16); - unsigned int index = (handle & HANDLESYS_HANDLE_MASK); - - if (index == 0 || index > m_HandleTail || index > HANDLESYS_MAX_HANDLES) - { - return HandleError_Index; - } - - QHandle *pHandle = &m_Handles[index]; - - if (!pHandle->set - || (pHandle->set == HandleSet_Freed && !ignoreFree)) - { - return HandleError_Freed; - } else if (pHandle->set == HandleSet_Identity - && ident != g_ShareSys.GetIdentRoot()) - { - /* Only IdentityHandle() can read this! */ - return HandleError_Identity; - } - if (pHandle->serial != serial) - { - return HandleError_Changed; - } - - *in_pHandle = pHandle; - *in_index = index; - - return HandleError_None; -} - -bool HandleSystem::CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity) -{ - QHandleType *pType = &m_Types[pHandle->type]; - unsigned int access; - - if (pHandle->access_special) - { - access = pHandle->sec.access[right]; - } else { - access = pType->hndlSec.access[right]; - } - - /* Check if the type's identity matches */ - if (access & HANDLE_RESTRICT_IDENTITY) - { - IdentityToken_t *owner = pType->typeSec.ident; - if (!owner - || (!pSecurity || pSecurity->pIdentity != owner)) - { - return false; - } - } - - /* Check if the owner is allowed */ - if (access & HANDLE_RESTRICT_OWNER) - { - IdentityToken_t *owner = pHandle->owner; - if (owner - && (!pSecurity || pSecurity->pOwner != owner)) - { - return false; - } - } - - return true; -} - -HandleError HandleSystem::CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner) -{ - /* Get a new Handle ID */ - unsigned int new_index; - QHandle *pNewHandle; - Handle_t new_handle; - HandleError err; - - if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, newOwner)) != HandleError_None) - { - return err; - } - - /* Assign permissions from parent */ - if (pHandle->access_special) - { - pNewHandle->access_special = true; - pNewHandle->sec = pHandle->sec; - } - - pNewHandle->clone = index; - pHandle->refcount++; - - *newhandle = new_handle; - - return HandleError_None; -} - -HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *newhandle, IdentityToken_t *newOwner, const HandleSecurity *pSecurity) -{ - HandleError err; - QHandle *pHandle; - unsigned int index; - IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; - - if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) - { - return err; - } - - /* Identities cannot be cloned */ - if (pHandle->set == HandleSet_Identity) - { - return HandleError_Identity; - } - - /* Check if the handle can be cloned */ - if (!CheckAccess(pHandle, HandleAccess_Clone, pSecurity)) - { - return HandleError_Access; - } - - /* Make sure we're not cloning a clone */ - if (pHandle->clone) - { - QHandle *pParent = &m_Handles[pHandle->clone]; - return CloneHandle(pParent, pHandle->clone, newhandle, newOwner); - } - - return CloneHandle(pHandle, index, newhandle, newOwner); -} - -HandleError HandleSystem::FreeHandle(QHandle *pHandle, unsigned int index) -{ - QHandleType *pType = &m_Types[pHandle->type]; - - if (pHandle->clone) - { - /* If we're a clone, decrease the parent reference count */ - QHandle *pMaster; - unsigned int master; - - /* Note that if we ever have per-handle security, we would need to re-check - * the access on this Handle. */ - master = pHandle->clone; - pMaster = &m_Handles[master]; - - /* Release the clone now */ - ReleasePrimHandle(index); - - /* Decrement the master's reference count */ - if (--pMaster->refcount == 0) - { - /* Type should be the same but do this anyway... */ - pType = &m_Types[pMaster->type]; - pMaster->is_destroying = true; - pType->dispatch->OnHandleDestroy(pMaster->type, pMaster->object); - ReleasePrimHandle(master); - } - } else if (pHandle->set == HandleSet_Identity) { - /* If we're an identity, skip all this stuff! - * NOTE: SHARESYS DOES NOT CARE ABOUT THE DESTRUCTOR - */ - ReleasePrimHandle(index); - } else { - /* Decrement, free if necessary */ - if (--pHandle->refcount == 0) - { - pHandle->is_destroying = true; - pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object); - ReleasePrimHandle(index); - } else { - /* We must be cloned, so mark ourselves as freed */ - pHandle->set = HandleSet_Freed; - /* Now, unlink us, so we're not being tracked by the owner */ - if (pHandle->owner) - { - UnlinkHandleFromOwner(pHandle, index); - } - } - } - - return HandleError_None; -} - -HandleError HandleSystem::FreeHandle(Handle_t handle, const HandleSecurity *pSecurity) -{ - unsigned int index; - QHandle *pHandle; - HandleError err; - IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; - - if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) - { - return err; - } - - if (!CheckAccess(pHandle, HandleAccess_Delete, pSecurity)) - { - return HandleError_Access; - } - - if (pHandle->is_destroying) - { - /* Someone tried to free this recursively. - * We'll just ignore this safely. - */ - return HandleError_None; - } - - return FreeHandle(pHandle, index); -} - -HandleError HandleSystem::ReadHandle(Handle_t handle, HandleType_t type, const HandleSecurity *pSecurity, void **object) -{ - unsigned int index; - QHandle *pHandle; - HandleError err; - IdentityToken_t *ident = pSecurity ? pSecurity->pIdentity : NULL; - - if ((err=GetHandle(handle, ident, &pHandle, &index)) != HandleError_None) - { - return err; - } - - if (!CheckAccess(pHandle, HandleAccess_Read, pSecurity)) - { - return HandleError_Access; - } - - /* Check the type inheritance */ - if (pHandle->type & HANDLESYS_SUBTYPE_MASK) - { - if (pHandle->type != type - && (TypeParent(pHandle->type) != TypeParent(type))) - { - return HandleError_Type; - } - } else if (type) { - if (pHandle->type != type) - { - return HandleError_Type; - } - } - - if (object) - { - /* if we're a clone, the rules change - object is ONLY in our reference */ - if (pHandle->clone) - { - pHandle = &m_Handles[pHandle->clone]; - } - *object = pHandle->object; - } - - return HandleError_None; -} - -void HandleSystem::UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index) -{ - /* Unlink us if necessary */ - unsigned int ident_index; - if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None) - { - /* Uh-oh! */ - assert(pHandle->owner == 0); - return; - } - /* Note that since 0 is an invalid handle, if any of these links are 0, - * the data can still be set. - */ - QHandle *pIdentity = &m_Handles[ident_index]; - - /* Unlink case: We're the head AND tail node */ - if (index == pIdentity->ch_prev && index == pIdentity->ch_next) - { - pIdentity->ch_prev = 0; - pIdentity->ch_next = 0; - } - /* Unlink case: We're the head node */ - else if (index == pIdentity->ch_prev) { - /* Link us to the next in the chain */ - pIdentity->ch_prev = pHandle->ch_next; - /* Patch up the previous link */ - m_Handles[pHandle->ch_next].ch_prev = 0; - } - /* Unlink case: We're the tail node */ - else if (index == pIdentity->ch_next) { - /* Link us to the previous in the chain */ - pIdentity->ch_next = pHandle->ch_prev; - /* Patch up the next link */ - m_Handles[pHandle->ch_prev].ch_next = 0; - } - /* Unlink case: We're in the middle! */ - else { - /* Patch the forward reference */ - m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev; - /* Patch the backward reference */ - m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next; - } - - /* Lastly, decrease the reference count */ - pIdentity->refcount--; -} - -void HandleSystem::ReleasePrimHandle(unsigned int index) -{ - QHandle *pHandle = &m_Handles[index]; - HandleSet set = pHandle->set; - - if (pHandle->owner && (set != HandleSet_Identity)) - { - UnlinkHandleFromOwner(pHandle, index); - } - - /* Were we an identity ourself? */ - QHandle *pLocal; - if (set == HandleSet_Identity) - { - /* Extra work to do. We need to find everything connected to this identity and release it. */ - unsigned int ch_index; -#if defined _DEBUG - unsigned int old_index = 0; -#endif - while ((ch_index = pHandle->ch_next) != 0) - { - pLocal = &m_Handles[ch_index]; -#if defined _DEBUG - assert(old_index != ch_index); - assert(pLocal->set == HandleSet_Used); - old_index = ch_index; -#endif - FreeHandle(pLocal, ch_index); - } - } - - pHandle->set = HandleSet_None; - m_Types[pHandle->type].opened--; - m_Handles[++m_FreeHandles].freeID = index; -} - -bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident) -{ - if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE) - { - return false; - } - - QHandleType *pType = &m_Types[type]; - - if (pType->typeSec.ident - && pType->typeSec.ident != ident) - { - return false; - } - - if (pType->dispatch == NULL) - { - return false; - } - - /* Remove children if we have to */ - if (!(type & HANDLESYS_SUBTYPE_MASK)) - { - QHandleType *childType; - for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++) - { - childType = &m_Types[type + i]; - if (childType->dispatch) - { - RemoveType(type + i, childType->typeSec.ident); - } - } - /* Link us into the free chain */ - m_Types[++m_FreeTypes].freeID = type; - } - - /* Invalidate the type now */ - IHandleTypeDispatch *dispatch = pType->dispatch; - pType->dispatch = NULL; - - /* Make sure nothing is using this type. */ - if (pType->opened) - { - QHandle *pHandle; - for (unsigned int i=1; i<=m_HandleTail; i++) - { - pHandle = &m_Handles[i]; - if (!pHandle->set || pHandle->type != type) - { - continue; - } - if (pHandle->clone) - { - /* Get parent */ - QHandle *pOther = &m_Handles[pHandle->clone]; - if (--pOther->refcount == 0) - { - /* Free! */ - dispatch->OnHandleDestroy(type, pOther->object); - ReleasePrimHandle(pHandle->clone); - } - /* Unlink ourselves since we don't have a reference count */ - ReleasePrimHandle(i); - } else { - /* If it's not a clone, we still have to check the reference count. - * Either way, we'll be destroyed eventually because the handle types do not change. - */ - if (--pHandle->refcount == 0) - { - /* Free! */ - dispatch->OnHandleDestroy(type, pHandle->object); - ReleasePrimHandle(i); - } - } - if (pType->opened == 0) - { - break; - } - } - } - - /* Remove it from the type cache. */ - if (pType->nameIdx != -1) - { - const char *typeName; - - typeName = m_strtab->GetString(pType->nameIdx); - sm_trie_delete(m_TypeLookup, typeName); - } - - return true; -} - -bool HandleSystem::InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess) -{ - if (pTypeAccess) - { - if (pTypeAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) - { - return false; - } - pTypeAccess->access[HTypeAccess_Create] = false; - pTypeAccess->access[HTypeAccess_Inherit] = false; - pTypeAccess->ident = NULL; - } - - if (pHandleAccess) - { - if (pHandleAccess->hsVersion > SMINTERFACE_HANDLESYSTEM_VERSION) - { - return false; - } - - pHandleAccess->access[HandleAccess_Clone] = 0; - pHandleAccess->access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER; - pHandleAccess->access[HandleAccess_Read] = HANDLE_RESTRICT_IDENTITY; - } - - return true; -} - -bool HandleSystem::TryAndFreeSomeHandles() -{ - IPluginIterator *pl_iter = g_PluginSys.GetPluginIterator(); - IPlugin *highest_owner = NULL; - unsigned int highest_handle_count = 0; - - /* Search all plugins */ - while (pl_iter->MorePlugins()) - { - IPlugin *plugin = pl_iter->GetPlugin(); - IdentityToken_t *identity = plugin->GetIdentity(); - unsigned int handle_count = 0; - - if (identity == NULL) - { - continue; - } - - /* Search all handles */ - for (unsigned int i = 1; i <= m_HandleTail; i++) - { - if (m_Handles[i].set != HandleSet_Used) - { - continue; - } - if (m_Handles[i].owner == identity) - { - handle_count++; - } - } - - if (handle_count > highest_handle_count) - { - highest_owner = plugin; - highest_handle_count = handle_count; - } - - pl_iter->NextPlugin(); - } - - if (highest_owner == NULL || highest_handle_count == 0) - { - return false; - } - - g_Logger.LogFatal("[SM] MEMORY LEAK DETECTED IN PLUGIN (file \"%s\")", highest_owner->GetFilename()); - g_Logger.LogFatal("[SM] Reloading plugin to free %d handles.", highest_handle_count); - g_Logger.LogFatal("[SM] Contact the author(s) of this plugin to correct this error.", highest_handle_count); - - highest_owner->GetBaseContext()->ThrowNativeErrorEx(SP_ERROR_MEMACCESS, "Memory leak"); - - return g_PluginSys.UnloadPlugin(highest_owner); -} - -void HandleSystem::Dump(HANDLE_REPORTER rep) -{ - unsigned int total_size = 0; - rep("%-10.10s\t%-20.20s\t%-20.20s\t%-10.10s", "Handle", "Owner", "Type", "Memory"); - rep("--------------------------------------------------------------------------"); - for (unsigned int i = 1; i <= m_HandleTail; i++) - { - if (m_Handles[i].set != HandleSet_Used) - { - continue; - } - /* Get the index */ - unsigned int index = (m_Handles[i].serial << 16) | i; - /* Determine the owner */ - const char *owner = "UNKNOWN"; - if (m_Handles[i].owner) - { - IdentityToken_t *pOwner = m_Handles[i].owner; - if (pOwner == g_pCoreIdent) - { - owner = "CORE"; - } - else if (pOwner == g_PluginSys.GetIdentity()) - { - owner = "PLUGINSYS"; - } - else - { - CExtension *ext = g_Extensions.GetExtensionFromIdent(pOwner); - if (ext) - { - owner = ext->GetFilename(); - } - else - { - CPlugin *pPlugin = g_PluginSys.GetPluginFromIdentity(pOwner); - if (pPlugin) - { - owner = pPlugin->GetFilename(); - } - } - } - } - else - { - owner = "NONE"; - } - const char *type = "ANON"; - QHandleType *pType = &m_Types[m_Handles[i].type]; - unsigned int size = 0; - if (pType->nameIdx != -1) - { - type = m_strtab->GetString(pType->nameIdx); - } - if (pType->dispatch->GetDispatchVersion() < HANDLESYS_MEMUSAGE_MIN_VERSION - || !pType->dispatch->GetHandleApproxSize(m_Handles[i].type, m_Handles[i].object, &size)) - { - rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, "-1"); - } - else - { - char buffer[32]; - UTIL_Format(buffer, sizeof(buffer), "%d", size); - rep("0x%08x\t%-20.20s\t%-20.20s\t%-10.10s", index, owner, type, buffer); - total_size += size; - } - } - rep("-- Approximately %d bytes of memory are in use by Handles.\n", total_size); -} - diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/HandleSys.h --- a/core/systems/HandleSys.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ -#define _INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ - -#include -#include -#include "sm_globals.h" -#include "sm_trie.h" -#include "sourcemod.h" -#include "sm_memtable.h" - -#define HANDLESYS_MAX_HANDLES (1<<14) -#define HANDLESYS_MAX_TYPES (1<<9) -#define HANDLESYS_MAX_SUBTYPES 0xF -#define HANDLESYS_SUBTYPE_MASK 0xF -#define HANDLESYS_TYPEARRAY_SIZE (HANDLESYS_MAX_TYPES * (HANDLESYS_MAX_SUBTYPES + 1)) -#define HANDLESYS_MAX_SERIALS 0xFFFF -#define HANDLESYS_SERIAL_MASK 0xFFFF0000 -#define HANDLESYS_HANDLE_MASK 0x0000FFFF - -#define HANDLESYS_MEMUSAGE_MIN_VERSION 3 - -/** - * The QHandle is a nasty structure that compacts the handle system into a big vector. - * The members of the vector each encapsulate one Handle, however, they also act as nodes - * in an inlined linked list and an inlined vector. - * - * The first of these lists is the 'freeID' list. Each node from 1 to N (where N - * is the number of free nodes) has a 'freeID' that specifies a free Handle ID. This - * is a quick hack to get around allocating a second base vector. - * - * The second vector is the identity linked list. An identity has its own handle, so - * these handles are used as sentinel nodes for index linking. They point to the first and last - * index into the handle array. Each subsequent Handle who is owned by that identity is mapped into - * that list. This lets owning identities be unloaded in O(n) time. - * - * Eventually, there may be a third list for type chains. - */ - -enum HandleSet -{ - HandleSet_None = 0, - HandleSet_Used, /* The Handle is in use */ - HandleSet_Freed, /* The "master" Handle of a clone chain is freed */ - HandleSet_Identity, /* The Handle is a special identity */ -}; - -struct QHandle -{ - HandleType_t type; /* Handle type */ - void *object; /* Unmaintained object pointer */ - IdentityToken_t *owner; /* Identity of object which owns this */ - unsigned int serial; /* Serial no. for sanity checking */ - unsigned int refcount; /* Reference count for safe destruction */ - unsigned int clone; /* If non-zero, this is our cloned parent index */ - HandleSet set; /* Information about the handle's state */ - bool access_special; /* Whether or not access rules are special or type-derived */ - bool is_destroying; /* Whether or not the handle is being destroyed */ - HandleAccess sec; /* Security rules */ - /* The following variables are unrelated to the Handle array, and used - * as an inlined chain of information */ - unsigned int freeID; /* ID of a free handle in the free handle chain */ - /* Indexes into the handle array for owner membership. - * For identity roots, these are treated as the head/tail. */ - unsigned int ch_prev; /* chained previous handle */ - unsigned int ch_next; /* chained next handle */ -}; - -struct QHandleType -{ - IHandleTypeDispatch *dispatch; - unsigned int freeID; - unsigned int children; - TypeAccess typeSec; - HandleAccess hndlSec; - unsigned int opened; - int nameIdx; -}; - -typedef void (HANDLE_REPORTER)(const char *str, ...); - -class HandleSystem : - public IHandleSys -{ - friend HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index); - friend class ShareSystem; -public: - HandleSystem(); - ~HandleSystem(); -public: //IHandleSystem - - HandleType_t CreateType(const char *name, - IHandleTypeDispatch *dispatch, - HandleType_t parent, - const TypeAccess *typeAccess, - const HandleAccess *hndlAccess, - IdentityToken_t *ident, - HandleError *err); - - bool RemoveType(HandleType_t type, IdentityToken_t *ident); - - bool FindHandleType(const char *name, HandleType_t *type); - - Handle_t CreateHandle(HandleType_t type, - void *object, - IdentityToken_t *owner, - IdentityToken_t *ident, - HandleError *err); - - HandleError FreeHandle(Handle_t handle, const HandleSecurity *pSecurity); - - HandleError CloneHandle(Handle_t handle, - Handle_t *newhandle, - IdentityToken_t *newOwner, - const HandleSecurity *pSecurity); - - HandleError ReadHandle(Handle_t handle, - HandleType_t type, - const HandleSecurity *pSecurity, - void **object); - - bool InitAccessDefaults(TypeAccess *pTypeAccess, HandleAccess *pHandleAccess); - - bool TypeCheck(HandleType_t intype, HandleType_t outtype); - - virtual Handle_t CreateHandleEx(HandleType_t type, - void *object, - const HandleSecurity *pSec, - const HandleAccess *pAccess, - HandleError *err); - - void Dump(HANDLE_REPORTER rep); -protected: - /** - * Decodes a handle with sanity and security checking. - */ - HandleError GetHandle(Handle_t handle, - IdentityToken_t *ident, - QHandle **pHandle, - unsigned int *index, - bool ignoreFree=false); - - /** - * Creates a basic handle and sets its reference count to 1. - * Does not do any type or security checking. - */ - HandleError MakePrimHandle(HandleType_t type, - QHandle **pHandle, - unsigned int *index, - HandleType_t *handle, - IdentityToken_t *owner, - bool identity=false); - - /** - * Frees a primitive handle. Does no object freeing, only reference count, bookkeeping, - * and linked list maintenance. - * If used on an Identity handle, destroys all Handles under that identity. - */ - void ReleasePrimHandle(unsigned int index); - - /** - * Sets the security owner of a type. - */ - void SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken); - - /** - * Helper function to check access rights. - */ - bool CheckAccess(QHandle *pHandle, HandleAccessRight right, const HandleSecurity *pSecurity); - - /** - * Some wrappers for internal functions, so we can pass indexes instead of encoded handles. - */ - HandleError FreeHandle(QHandle *pHandle, unsigned int index); - void UnlinkHandleFromOwner(QHandle *pHandle, unsigned int index); - HandleError CloneHandle(QHandle *pHandle, unsigned int index, Handle_t *newhandle, IdentityToken_t *newOwner); - Handle_t CreateHandleInt(HandleType_t type, void *object, const HandleSecurity *pSec, HandleError *err, const HandleAccess *pAccess, bool identity); - - bool TryAndFreeSomeHandles(); - HandleError TryAllocHandle(unsigned int *handle); -private: - QHandle *m_Handles; - QHandleType *m_Types; - Trie *m_TypeLookup; - unsigned int m_TypeTail; - unsigned int m_FreeTypes; - unsigned int m_HandleTail; - unsigned int m_FreeHandles; - unsigned int m_HSerial; - BaseStringTable *m_strtab; -}; - -extern HandleSystem g_HandleSys; - -#endif //_INCLUDE_SOURCEMOD_HANDLESYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/LibrarySys.cpp --- a/core/systems/LibrarySys.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,441 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include -#include -#include -#include -#include "sm_stringutil.h" -#include "LibrarySys.h" - -LibrarySystem g_LibSys; - -CLibrary::~CLibrary() -{ - if (m_lib) - { -#if defined PLATFORM_WINDOWS - FreeLibrary(m_lib); -#elif defined PLATFORM_POSIX - dlclose(m_lib); -#endif - m_lib = NULL; - } -} - -CLibrary::CLibrary(LibraryHandle me) -{ - m_lib = me; -} - -void CLibrary::CloseLibrary() -{ - delete this; -} - -void *CLibrary::GetSymbolAddress(const char *symname) -{ -#if defined PLATFORM_WINDOWS - return GetProcAddress(m_lib, symname); -#elif defined PLATFORM_POSIX - return dlsym(m_lib, symname); -#endif -} - - -/******************** - ** Directory Code ** - ********************/ - - -CDirectory::CDirectory(const char *path) -{ -#if defined PLATFORM_WINDOWS - char newpath[PLATFORM_MAX_PATH]; - snprintf(newpath, sizeof(newpath), "%s\\*.*", path); - m_dir = FindFirstFileA(newpath, &m_fd); - if (!IsValid()) - { - m_fd.cFileName[0] = '\0'; - } -#elif defined PLATFORM_POSIX - m_dir = opendir(path); - if (IsValid()) - { - /* :TODO: we need to read past "." and ".."! */ - ep = readdir(m_dir); - snprintf(m_origpath, PLATFORM_MAX_PATH, "%s", path); - } else { - ep = NULL; - } -#endif -} - -CDirectory::~CDirectory() -{ - if (IsValid()) - { -#if defined PLATFORM_WINDOWS - FindClose(m_dir); -#elif defined PLATFORM_POSIX - closedir(m_dir); -#endif - } -} - -void CDirectory::NextEntry() -{ -#if defined PLATFORM_WINDOWS - if (FindNextFile(m_dir, &m_fd) == 0) - { - FindClose(m_dir); - m_dir = INVALID_HANDLE_VALUE; - } -#elif defined PLATFORM_POSIX - if ((ep=readdir(m_dir)) == NULL) - { - closedir(m_dir); - m_dir = NULL; - } -#endif -} - -bool CDirectory::IsEntryValid() -{ - return IsValid(); -} - -bool CDirectory::IsEntryDirectory() -{ -#if defined PLATFORM_WINDOWS - return ((m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY); -#elif defined PLATFORM_LINUX - char temppath[PLATFORM_MAX_PATH]; - snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName()); - return g_LibSys.IsPathDirectory(temppath); -#endif -} - -bool CDirectory::IsEntryFile() -{ -#if defined PLATFORM_WINDOWS - return !(m_fd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)); -#elif defined PLATFORM_POSIX - char temppath[PLATFORM_MAX_PATH]; - snprintf(temppath, sizeof(temppath), "%s/%s", m_origpath, GetEntryName()); - return g_LibSys.IsPathFile(temppath); -#endif -} - -const char *CDirectory::GetEntryName() -{ -#if defined PLATFORM_WINDOWS - return m_fd.cFileName; -#elif defined PLATFORM_LINUX - return ep ? ep->d_name : ""; -#endif -} - -bool CDirectory::MoreFiles() -{ - return IsValid(); -} - -bool CDirectory::IsValid() -{ -#if defined PLATFORM_WINDOWS - return (m_dir != INVALID_HANDLE_VALUE); -#elif defined PLATFORM_LINUX - return (m_dir != NULL); -#endif -} - - -/************************* - ** Library System Code ** - *************************/ - - -bool LibrarySystem::PathExists(const char *path) -{ -#if defined PLATFORM_WINDOWS - DWORD attr = GetFileAttributesA(path); - - return (attr != INVALID_FILE_ATTRIBUTES); -#elif defined PLATFORM_POSIX - struct stat s; - - return (stat(path, &s) == 0); -#endif -} - -bool LibrarySystem::IsPathFile(const char *path) -{ -#if defined PLATFORM_WINDOWS - DWORD attr = GetFileAttributes(path); - - if (attr == INVALID_FILE_ATTRIBUTES) - { - return false; - } - - if (attr & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) - { - return false; - } - - return true; -#elif defined PLATFORM_LINUX - struct stat s; - - if (stat(path, &s) != 0) - { - return false; - } - - return S_ISREG(s.st_mode) ? true : false; -#endif -} - -bool LibrarySystem::IsPathDirectory(const char *path) -{ -#if defined PLATFORM_WINDOWS - DWORD attr = GetFileAttributes(path); - - if (attr == INVALID_FILE_ATTRIBUTES) - { - return false; - } - - if (attr & FILE_ATTRIBUTE_DIRECTORY) - { - return true; - } - -#elif defined PLATFORM_LINUX - struct stat s; - - if (stat(path, &s) != 0) - { - return false; - } - - if (S_ISDIR(s.st_mode)) - { - return true; - } -#endif - - return false; -} - -IDirectory *LibrarySystem::OpenDirectory(const char *path) -{ - CDirectory *dir = new CDirectory(path); - - if (!dir->IsValid()) - { - delete dir; - return NULL; - } - - return dir; -} - -void LibrarySystem::GetPlatformError(char *error, size_t maxlength) -{ - if (error && maxlength) - { -#if defined PLATFORM_WINDOWS - DWORD dw = GetLastError(); - FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - dw, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)error, - maxlength, - NULL); -#elif defined PLATFORM_POSIX - UTIL_Format(error, maxlength, "%s", strerror(errno)); -#endif - } -} - -void LibrarySystem::GetLoaderError(char *buffer, size_t maxlength) -{ -#if defined PLATFORM_WINDOWS - GetPlatformError(buffer, maxlength); -#elif defined PLATFORM_POSIX - if (buffer != NULL && maxlength) - { - strncopy(buffer, dlerror(), maxlength); - } -#endif -} - -void LibrarySystem::CloseDirectory(IDirectory *dir) -{ - delete dir; -} - -ILibrary *LibrarySystem::OpenLibrary(const char *path, char *error, size_t maxlength) -{ - LibraryHandle lib; -#if defined PLATFORM_WINDOWS - lib = LoadLibraryA(path); -#elif defined PLATFORM_POSIX - lib = dlopen(path, RTLD_NOW); -#endif - - if (lib == NULL) - { - GetLoaderError(error, maxlength); - return NULL; - } - - return new CLibrary(lib); -} - -size_t LibrarySystem::PathFormat(char *buffer, size_t len, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - size_t mylen = vsnprintf(buffer, len, fmt, ap); - va_end(ap); - - if (mylen >= len) - { - mylen = len - 1; - buffer[mylen] = '\0'; - } - - for (size_t i=0; i= 0 && i <= length - 1; - i--) - { - if (path[i] == '/' -#if defined PLATFORM_WINDOWS - || path[i] == '\\' -#endif - ) - { - return UTIL_Format(buffer, maxlength, "%s", &path[i+1]); - } - } - - /* We scanned and found no path separator */ - return UTIL_Format(buffer, maxlength, "%s", path); -} - -bool LibrarySystem::FileTime(const char *path, FileTimeType type, time_t *pTime) -{ -#ifdef PLATFORM_WINDOWS - struct _stat s; - if (_stat(path, &s) != 0) -#elif defined PLATFORM_POSIX - struct stat s; - if (stat(path, &s) != 0) -#endif - { - return false; - } - - if (type == FileTime_LastAccess) - { - *pTime = s.st_atime; - } - else if (type == FileTime_Created) - { - *pTime = s.st_ctime; - } - else if (type == FileTime_LastChange) - { - *pTime = s.st_mtime; - } - - return true; -} diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/LibrarySys.h --- a/core/systems/LibrarySys.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ -#define _INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ - -#include -#include "sm_platform.h" - -using namespace SourceMod; - -#if defined PLATFORM_WINDOWS -typedef HMODULE LibraryHandle; -#elif defined PLATFORM_POSIX -typedef void * LibraryHandle; -#endif - -class CDirectory : public IDirectory -{ -public: - CDirectory(const char *path); - ~CDirectory(); -public: - bool MoreFiles(); - void NextEntry(); - const char *GetEntryName(); - bool IsEntryDirectory(); - bool IsEntryFile(); - bool IsEntryValid(); -public: - bool IsValid(); -private: -#if defined PLATFORM_WINDOWS - HANDLE m_dir; - WIN32_FIND_DATAA m_fd; -#elif defined PLATFORM_POSIX - DIR *m_dir; - struct dirent *ep; - char m_origpath[PLATFORM_MAX_PATH]; -#endif -}; - -class CLibrary : public ILibrary -{ -public: - CLibrary(LibraryHandle me); - ~CLibrary(); -public: - void CloseLibrary(); - void *GetSymbolAddress(const char *symname); -private: - LibraryHandle m_lib; -}; - -class LibrarySystem : public ILibrarySys -{ -public: - ILibrary *OpenLibrary(const char *path, char *error, size_t maxlength); - IDirectory *OpenDirectory(const char *path); - void CloseDirectory(IDirectory *dir); - bool PathExists(const char *path); - bool IsPathFile(const char *path); - bool IsPathDirectory(const char *path); - void GetPlatformError(char *error, size_t maxlength); - size_t PathFormat(char *buffer, size_t len, const char *fmt, ...); - const char *GetFileExtension(const char *filename); - bool CreateFolder(const char *path); - size_t GetFileFromPath(char *buffer, size_t maxlength, const char *path); - bool FileTime(const char *path, FileTimeType type, time_t *pTime); - void GetLoaderError(char *buffer, size_t maxlength); -}; - -extern LibrarySystem g_LibSys; - -#endif //_INCLUDE_SOURCEMOD_SYSTEM_LIBRARY_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/PluginInfoDatabase.cpp --- a/core/systems/PluginInfoDatabase.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,349 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include -#include "PluginInfoDatabase.h" -#include "PluginSys.h" - -void PluginSettings::Init() -{ - name = -1; - pause_val = false; - type_val = PluginType_MapUpdated; - optarray = -1; - opts_num = 0; - opts_size = 0; - blockload_val = false; -} - -/** - * :TODO: write the logger, make these errors log instead of being static - * NOTE: once we do that, we will have to change some code to ignore sections - */ - -CPluginInfoDatabase::CPluginInfoDatabase() -{ - m_strtab = NULL; - m_infodb_count = 0; - m_infodb_size = 0; - m_infodb = -1; -} - -CPluginInfoDatabase::~CPluginInfoDatabase() -{ - delete m_strtab; -} - -void CPluginInfoDatabase::ReadSMC_ParseStart() -{ - /* Create or reset our string table */ - if (m_strtab) - { - m_strtab->Reset(); - } else { - m_strtab = new BaseStringTable(1024); - } - - /* Set our internal states to the beginning */ - in_plugins = false; - cur_plugin = -1; - in_options = false; - m_infodb_size = 0; - m_infodb_count = 0; - m_infodb = -1; -} - -SMCResult CPluginInfoDatabase::MakeError(const char *fmt, ...) -{ - char buffer[512]; - va_list ap; - - va_start(ap, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, ap); - va_end(ap); - - m_errmsg = m_strtab->AddString(buffer); - - return SMCResult_HaltFail; -} - -unsigned int CPluginInfoDatabase::GetSettingsNum() -{ - return m_infodb_count; -} - -PluginSettings *CPluginInfoDatabase::GetSettingsIfMatch(unsigned int index, const char *filename) -{ - BaseMemTable *memtab = m_strtab->GetMemTable(); - int *table = (int *)memtab->GetAddress(m_infodb); - - if (!table || index >= m_infodb_count) - { - return NULL; - } - - PluginSettings *plugin = (PluginSettings *)memtab->GetAddress(table[index]); - - const char *name = m_strtab->GetString(plugin->name); - - if (!name) - { - return NULL; - } - - if (!g_PluginSys.TestAliasMatch(name, filename)) - { - return NULL; - } - - return plugin; -} - -void CPluginInfoDatabase::GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val) -{ - PluginOpts *table = (PluginOpts *)m_strtab->GetMemTable()->GetAddress(settings->optarray); - if (!table) - { - *key = NULL; - *val = NULL; - return; - } - - if (opt_num >= settings->opts_num) - { - *key = NULL; - *val = NULL; - return; - } - - *key = m_strtab->GetString(table[opt_num].key); - *val = m_strtab->GetString(table[opt_num].val); -} - -SMCResult CPluginInfoDatabase::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) -{ - if (cur_plugin != -1) - { - PluginSettings *plugin = (PluginSettings *)m_strtab->GetMemTable()->GetAddress(cur_plugin); - if (!in_options) - { - if (strcmp(key, "pause") == 0) - { - if (strcasecmp(value, "yes") == 0) - { - plugin->pause_val = true; - } - else - { - plugin->pause_val = false; - } - } - else if (strcmp(key, "lifetime") == 0) - { - if (strcasecmp(value, "private") == 0) - { - plugin->type_val = PluginType_Private; - } - else if (strcasecmp(value, "mapsync") == 0) - { - plugin->type_val = PluginType_MapUpdated; - } - else if (strcasecmp(value, "maponly") == 0) - { - plugin->type_val = PluginType_MapOnly; - } - else if (strcasecmp(value, "global") == 0) - { - plugin->type_val = PluginType_Global; - } - else - { - return MakeError("Unknown value for key \"lifetime\": \"%s\"", value); - } - } - else if (strcmp(key, "blockload") == 0) - { - plugin->blockload_val = true; - } - else - { - return MakeError("Unknown property key: \"%s\"", key); - } - } - else - { - /* Cache every option, valid or not */ - int keyidx = m_strtab->AddString(key); - int validx = m_strtab->AddString(value); - PluginOpts *table; - BaseMemTable *memtab = m_strtab->GetMemTable(); - plugin = (PluginSettings *)memtab->GetAddress(cur_plugin); - if (plugin->opts_num + 1 > plugin->opts_size) - { - unsigned int oldsize = plugin->opts_size; - if (oldsize == 0) - { - //right now we don't have many - plugin->opts_size = 2; - } - else - { - plugin->opts_size *= 2; - } - int newidx = memtab->CreateMem(plugin->opts_size * sizeof(PluginOpts), (void **)&table); - /* in case it resized */ - plugin = (PluginSettings *)memtab->GetAddress(cur_plugin); - if (plugin->optarray != -1) - { - void *oldtable = memtab->GetAddress(plugin->optarray); - memcpy(table, oldtable, oldsize * sizeof(PluginOpts)); - } - plugin->optarray = newidx; - } - else - { - table = (PluginOpts *)memtab->GetAddress(plugin->optarray); - } - PluginOpts *opt = &table[plugin->opts_num++]; - opt->key = keyidx; - opt->val = validx; - } - } - else if (in_plugins) - { - return MakeError("Unknown property key: \"%s\"", key); - } - else - { - /* Ignore anything we don't know about! */ - } - - return SMCResult_Continue; -} - -SMCResult CPluginInfoDatabase::ReadSMC_LeavingSection(const SMCStates *states) -{ - if (in_plugins) - { - if (cur_plugin != -1) - { - if (in_options) - { - in_options = false; - } - else - { - /* If the plugin is ending, add it to the table */ - BaseMemTable *memtab = m_strtab->GetMemTable(); - int *table; - if (m_infodb_count + 1 > m_infodb_size) - { - unsigned int oldsize = m_infodb_size; - if (!m_infodb_size) - { - m_infodb_size = 8; - } - else - { - m_infodb_size *= 2; - } - int newidx = memtab->CreateMem(m_infodb_size, (void **)&table); - if (m_infodb != -1) - { - void *oldtable = (int *)memtab->GetAddress(m_infodb); - memcpy(table, oldtable, oldsize * sizeof(int)); - } - m_infodb = newidx; - } - else - { - table = (int *)memtab->GetAddress(m_infodb); - } - /* Assign to table and scrap the current plugin */ - table[m_infodb_count++] = cur_plugin; - cur_plugin = -1; - } - } - else - { - in_plugins = false; - } - } - - return SMCResult_Continue; -} - -SMCResult CPluginInfoDatabase::ReadSMC_NewSection(const SMCStates *states, const char *name) -{ - if (!in_plugins) - { - /* If we're not in the main Plugins section, and we don't get it for the name, error out */ - if (strcmp(name, "Plugins") != 0) - { - return MakeError("Unknown root section: \"%s\"", name); - } - else - { - /* Otherwise set our states */ - in_plugins = true; - cur_plugin = -1; - in_options = false; - } - } - else - { - if (cur_plugin == -1) - { - /* If we get a plugin node and we don't have a current plugin, create a new one */ - PluginSettings *plugin; - int i_name = m_strtab->AddString(name); - cur_plugin = m_strtab->GetMemTable()->CreateMem(sizeof(PluginSettings), (void **)&plugin); - plugin->Init(); - plugin->name = i_name; - in_options = false; - } - else - { - if (!in_options && strcmp(name, "Options") == 0) - { - in_options = true; - } - else - { - return MakeError("Unknown plugin sub-section: \"%s\"", name); - } - } - } - - return SMCResult_Continue; -} - diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/PluginInfoDatabase.h --- a/core/systems/PluginInfoDatabase.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_ -#define _INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_ - -/** - * This file parses plugin_settings.cfg and stores the information in cached memory. - * It provides simplistic abstraction to retrieving the info. - * :TODO: currently untested - */ - -#include "sm_memtable.h" -#include "ITextParsers.h" -#include "IPluginSys.h" -#include "sm_globals.h" - -struct PluginOpts -{ - int key; - int val; -}; - -struct PluginSettings -{ - void Init(); - int name; - bool pause_val; - PluginType type_val; - int optarray; - size_t opts_num; - size_t opts_size; - bool blockload_val; -}; - -class CPluginInfoDatabase : public ITextListener_SMC -{ -public: - CPluginInfoDatabase(); - ~CPluginInfoDatabase(); -public: //ITextListener_SMC - void ReadSMC_ParseStart(); - SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name); - SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value); - SMCResult ReadSMC_LeavingSection(const SMCStates *states); -public: - /** - * Returns the number of plugin settings available. - */ - unsigned int GetSettingsNum(); - - /** - * Given an index, returns the plugin settings block if the filename matches. - * Otherwise, returns NULL. - */ - PluginSettings *GetSettingsIfMatch(unsigned int index, const char *filename); - - /** - * Given a plugin settings struct and an index, - * returns the given JIT key/value option pair at that index. - * If the input is invalid, key and val will be set to NULL. - */ - void GetOptionsForPlugin(PluginSettings *settings, unsigned int opt_num, const char **key, const char **val); -private: - SMCResult MakeError(const char *fmt, ...); -private: - BaseStringTable *m_strtab; - int m_errmsg; - bool in_plugins; - bool in_options; - int m_infodb; - size_t m_infodb_count; - size_t m_infodb_size; - int cur_plugin; -}; - - -#endif //_INCLUDE_SOURCEMOD_CORE_SYSTEM_PLUGININFODATABASE_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/PluginSys.cpp --- a/core/systems/PluginSys.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2626 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include -#include "PluginSys.h" -#include "ShareSys.h" -#include "LibrarySys.h" -#include "HandleSys.h" -#include "ForwardSys.h" -#include "sourcemm_api.h" -#include "sourcemod.h" -#include "TextParsers.h" -#include "Logger.h" -#include "ExtensionSys.h" -#include "sm_srvcmds.h" -#include "sm_stringutil.h" -#include "ConCmdManager.h" -#include "PlayerManager.h" -#include "CoreConfig.h" -#include "Translator.h" - -CPluginManager g_PluginSys; -HandleType_t g_PluginType = 0; -IdentityType_t g_PluginIdent = 0; - -CPlugin::CPlugin(const char *file) -{ - static int MySerial = 0; - - m_type = PluginType_Private; - m_status = Plugin_Uncompiled; - m_serial = ++MySerial; - m_pRuntime = NULL; - m_errormsg[256] = '\0'; - snprintf(m_filename, sizeof(m_filename), "%s", file); - m_handle = 0; - m_ident = NULL; - m_pProps = sm_trie_create(); - m_FakeNativesMissing = false; - m_LibraryMissing = false; - m_bGotAllLoaded = false; - m_pPhrases = g_Translator.CreatePhraseCollection(); - m_MaxClientsVar = NULL; -} - -CPlugin::~CPlugin() -{ - if (m_handle) - { - HandleSecurity sec; - sec.pOwner = g_PluginSys.GetIdentity(); - sec.pIdentity = sec.pOwner; - - g_HandleSys.FreeHandle(m_handle, &sec); - g_ShareSys.DestroyIdentity(m_ident); - } - - if (m_pRuntime != NULL) - { - delete m_pRuntime; - m_pRuntime = NULL; - } - - if (m_pProps) - { - sm_trie_destroy(m_pProps); - } - for (size_t i=0; iDestroy(); - m_pPhrases = NULL; - } -} - -void CPlugin::InitIdentity() -{ - if (!m_handle) - { - m_ident = g_ShareSys.CreateIdentity(g_PluginIdent, this); - m_handle = g_HandleSys.CreateHandle(g_PluginType, this, g_PluginSys.GetIdentity(), g_PluginSys.GetIdentity(), NULL); - m_pRuntime->GetDefaultContext()->SetKey(1, m_ident); - m_pRuntime->GetDefaultContext()->SetKey(2, (IPlugin *)this); - } -} - -unsigned int CPlugin::CalcMemUsage() -{ - unsigned int base_size = - sizeof(CPlugin) - + sizeof(IdentityToken_t) - + (m_configs.size() * (sizeof(AutoConfig *) + sizeof(AutoConfig))) - + sm_trie_mem_usage(m_pProps); - - for (unsigned int i = 0; i < m_configs.size(); i++) - { - base_size += m_configs[i]->autocfg.size(); - base_size += m_configs[i]->folder.size(); - } - - for (List::iterator i = m_Libraries.begin(); - i != m_Libraries.end(); - i++) - { - base_size += (*i).size(); - } - - for (List::iterator i = m_RequiredLibs.begin(); - i != m_RequiredLibs.end(); - i++) - { - base_size += (*i).size(); - } - - return base_size; -} - -Handle_t CPlugin::GetMyHandle() -{ - return m_handle; -} - -CPlugin *CPlugin::CreatePlugin(const char *file, char *error, size_t maxlength) -{ - char fullpath[PLATFORM_MAX_PATH]; - g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", file); - FILE *fp = fopen(fullpath, "rb"); - - CPlugin *pPlugin = new CPlugin(file); - - if (!fp) - { - if (error) - { - snprintf(error, maxlength, "Unable to open file"); - } - pPlugin->m_status = Plugin_BadLoad; - return pPlugin; - } - - fclose(fp); - - return pPlugin; -} - -bool CPlugin::GetProperty(const char *prop, void **ptr, bool remove/* =false */) -{ - bool exists = sm_trie_retrieve(m_pProps, prop, ptr); - - if (exists && remove) - { - sm_trie_delete(m_pProps, prop); - } - - return exists; -} - -bool CPlugin::SetProperty(const char *prop, void *ptr) -{ - return sm_trie_insert(m_pProps, prop, ptr); -} - -IPluginRuntime *CPlugin::GetRuntime() -{ - return m_pRuntime; -} - -void CPlugin::SetErrorState(PluginStatus status, const char *error_fmt, ...) -{ - PluginStatus old_status = m_status; - m_status = status; - - if (old_status == Plugin_Running) - { - /* Tell everyone we're now paused */ - g_PluginSys._SetPauseState(this, true); - } - - va_list ap; - va_start(ap, error_fmt); - vsnprintf(m_errormsg, sizeof(m_errormsg), error_fmt, ap); - va_end(ap); - - if (m_pRuntime != NULL) - { - m_pRuntime->SetPauseState(true); - } -} - -bool CPlugin::UpdateInfo() -{ - /* Now grab the info */ - uint32_t idx; - IPluginContext *base = GetBaseContext(); - int err = base->FindPubvarByName("myinfo", &idx); - - memset(&m_info, 0, sizeof(m_info)); - - if (err == SP_ERROR_NONE) - { - struct sm_plugininfo_c_t - { - cell_t name; - cell_t description; - cell_t author; - cell_t version; - cell_t url; - }; - sm_plugininfo_c_t *cinfo; - cell_t local_addr; - - base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&cinfo); - base->LocalToString(cinfo->name, (char **)&m_info.name); - base->LocalToString(cinfo->description, (char **)&m_info.description); - base->LocalToString(cinfo->author, (char **)&m_info.author); - base->LocalToString(cinfo->url, (char **)&m_info.url); - base->LocalToString(cinfo->version, (char **)&m_info.version); - } - - m_info.author = m_info.author ? m_info.author : ""; - m_info.description = m_info.description ? m_info.description : ""; - m_info.name = m_info.name ? m_info.name : ""; - m_info.url = m_info.url ? m_info.url : ""; - m_info.version = m_info.version ? m_info.version : ""; - - if ((err = base->FindPubvarByName("__version", &idx)) == SP_ERROR_NONE) - { - struct __version_info - { - cell_t version; - cell_t filevers; - cell_t date; - cell_t time; - }; - __version_info *info; - cell_t local_addr; - const char *pDate, *pTime, *pFileVers; - - pDate = ""; - pTime = ""; - - base->GetPubvarAddrs(idx, &local_addr, (cell_t **)&info); - m_FileVersion = info->version; - if (m_FileVersion >= 3) - { - base->LocalToString(info->date, (char **)&pDate); - base->LocalToString(info->time, (char **)&pTime); - UTIL_Format(m_DateTime, sizeof(m_DateTime), "%s %s", pDate, pTime); - } - if (m_FileVersion > 4) - { - base->LocalToString(info->filevers, (char **)&pFileVers); - SetErrorState(Plugin_Failed, "Newer SourceMod required (%s or higher)", pFileVers); - return false; - } - } - else - { - m_FileVersion = 0; - } - - if ((err = base->FindPubvarByName("MaxClients", &idx)) == SP_ERROR_NONE) - { - base->GetPubvarByIndex(idx, &m_MaxClientsVar); - } - - return true; -} - -void CPlugin::SyncMaxClients(int max_clients) -{ - if (m_MaxClientsVar == NULL) - { - return; - } - - *m_MaxClientsVar->offs = max_clients; -} - -void CPlugin::Call_OnPluginStart() -{ - if (m_status != Plugin_Loaded) - { - return; - } - - m_status = Plugin_Running; - - SyncMaxClients(g_Players.MaxClients()); - - cell_t result; - IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginStart"); - if (!pFunction) - { - return; - } - - int err; - if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) - { - SetErrorState(Plugin_Error, "Error detected in plugin startup (see error logs)"); - } -} - -void CPlugin::Call_OnPluginEnd() -{ - if (m_status > Plugin_Paused) - { - return; - } - - cell_t result; - IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginEnd"); - if (!pFunction) - { - return; - } - - pFunction->Execute(&result); -} - -void CPlugin::Call_OnAllPluginsLoaded() -{ - if (m_status > Plugin_Paused) - { - return; - } - - if (m_bGotAllLoaded) - { - return; - } - - m_bGotAllLoaded = true; - - cell_t result; - IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnAllPluginsLoaded"); - if (pFunction != NULL) - { - pFunction->Execute(&result); - } - - if (g_OnMapStarted) - { - if ((pFunction = m_pRuntime->GetFunctionByName("OnMapStart")) != NULL) - { - pFunction->Execute(NULL); - } - } - - if (SM_AreConfigsExecuted()) - { - SM_ExecuteForPlugin(GetBaseContext()); - } -} - -bool CPlugin::Call_AskPluginLoad(char *error, size_t maxlength) -{ - if (m_status != Plugin_Created) - { - return false; - } - - m_status = Plugin_Loaded; - - int err; - cell_t result; - IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("AskPluginLoad"); - - if (!pFunction) - { - return true; - } - - pFunction->PushCell(m_handle); - pFunction->PushCell(g_PluginSys.IsLateLoadTime() ? 1 : 0); - pFunction->PushStringEx(error, maxlength, 0, SM_PARAM_COPYBACK); - pFunction->PushCell(maxlength); - if ((err=pFunction->Execute(&result)) != SP_ERROR_NONE) - { - return false; - } - - if (!result || m_status != Plugin_Loaded) - { - return false; - } - - return true; -} - -void *CPlugin::GetPluginStructure() -{ - return NULL; -} - -IPluginContext *CPlugin::GetBaseContext() -{ - if (m_pRuntime == NULL) - { - return NULL; - } - - return m_pRuntime->GetDefaultContext(); -} - -sp_context_t *CPlugin::GetContext() -{ - return NULL; -} - -const char *CPlugin::GetFilename() -{ - return m_filename; -} - -PluginType CPlugin::GetType() -{ - return m_type; -} - -const sm_plugininfo_t *CPlugin::GetPublicInfo() -{ - if (GetStatus() >= Plugin_Created) - { - return NULL; - } - return &m_info; -} - -unsigned int CPlugin::GetSerial() -{ - return m_serial; -} - -PluginStatus CPlugin::GetStatus() -{ - return m_status; -} - -bool CPlugin::IsDebugging() -{ - if (m_pRuntime == NULL) - { - return false; - } - - return true; -} - -void CPlugin::LibraryActions(bool dropping) -{ - List::iterator iter; - for (iter = m_Libraries.begin(); - iter != m_Libraries.end(); - iter++) - { - g_PluginSys.OnLibraryAction((*iter).c_str(), true, dropping); - } -} - -bool CPlugin::SetPauseState(bool paused) -{ - if (paused && GetStatus() != Plugin_Running) - { - return false; - } else if (!paused && GetStatus() != Plugin_Paused) { - return false; - } - - if (paused) - { - LibraryActions(true); - } - - IPluginFunction *pFunction = m_pRuntime->GetFunctionByName("OnPluginPauseChange"); - if (pFunction) - { - cell_t result; - pFunction->PushCell(paused ? 1 : 0); - pFunction->Execute(&result); - } - - if (paused) - { - m_status = Plugin_Paused; - m_pRuntime->SetPauseState(true); - } else { - m_status = Plugin_Running; - m_pRuntime->SetPauseState(false); - } - - g_PluginSys._SetPauseState(this, paused); - - if (!paused) - { - LibraryActions(false); - } - - return true; -} - -IdentityToken_t *CPlugin::GetIdentity() -{ - return m_ident; -} - -bool CPlugin::IsRunnable() -{ - return (m_status <= Plugin_Paused) ? true : false; -} - -time_t CPlugin::GetFileTimeStamp() -{ - char path[PLATFORM_MAX_PATH]; - g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "plugins/%s", m_filename); -#ifdef PLATFORM_WINDOWS - struct _stat s; - if (_stat(path, &s) != 0) -#elif defined PLATFORM_POSIX - struct stat s; - if (stat(path, &s) != 0) -#endif - { - return 0; - } - else - { - return s.st_mtime; - } -} - -time_t CPlugin::GetTimeStamp() -{ - return m_LastAccess; -} - -void CPlugin::SetTimeStamp(time_t t) -{ - m_LastAccess = t; -} - -IPhraseCollection *CPlugin::GetPhrases() -{ - return m_pPhrases; -} - -void CPlugin::DependencyDropped(CPlugin *pOwner) -{ - if (!m_pRuntime) - { - return; - } - - List::iterator reqlib_iter; - List::iterator lib_iter; - for (lib_iter=pOwner->m_Libraries.begin(); lib_iter!=pOwner->m_Libraries.end(); lib_iter++) - { - for (reqlib_iter=m_RequiredLibs.begin(); reqlib_iter!=m_RequiredLibs.end(); reqlib_iter++) - { - if ((*reqlib_iter) == (*lib_iter)) - { - m_LibraryMissing = true; - } - } - } - - List::iterator iter; - NativeEntry *pNative; - sp_native_t *native; - uint32_t idx; - unsigned int unbound = 0; - - for (iter = pOwner->m_Natives.begin(); - iter != pOwner->m_Natives.end(); - iter++) - { - pNative = (*iter); - /* Find this native! */ - if (m_pRuntime->FindNativeByName(pNative->name, &idx) != SP_ERROR_NONE) - { - continue; - } - /* Unbind it */ - m_pRuntime->GetNativeByIndex(idx, &native); - native->pfn = NULL; - native->status = SP_NATIVE_UNBOUND; - unbound++; - } - - if (unbound) - { - m_FakeNativesMissing = true; - } - - /* :IDEA: in the future, add native trapping? */ - if (m_FakeNativesMissing || m_LibraryMissing) - { - SetErrorState(Plugin_Error, "Depends on plugin: %s", pOwner->GetFilename()); - } -} - -unsigned int CPlugin::GetConfigCount() -{ - return (unsigned int)m_configs.size(); -} - -AutoConfig *CPlugin::GetConfig(unsigned int i) -{ - if (i >= GetConfigCount()) - { - return NULL; - } - - return m_configs[i]; -} - -void CPlugin::AddConfig(bool autoCreate, const char *cfg, const char *folder) -{ - /* Do a check for duplicates to prevent double-execution */ - for (size_t i = 0; i < m_configs.size(); i++) - { - if (m_configs[i]->autocfg.compare(cfg) == 0 - && m_configs[i]->folder.compare(folder) == 0 - && m_configs[i]->create == autoCreate) - { - return; - } - } - - AutoConfig *c = new AutoConfig; - - c->autocfg = cfg; - c->folder = folder; - c->create = autoCreate; - - m_configs.push_back(c); -} - -void CPlugin::DropEverything() -{ - CPlugin *pOther; - List::iterator iter; - List::iterator wk_iter; - - /* Tell everyone that depends on us that we're about to drop */ - for (iter = m_Dependents.begin(); - iter != m_Dependents.end(); - iter++) - { - pOther = (*iter); - pOther->DependencyDropped(this); - } - - /* Note: we don't care about things we depend on. - * The reason is that extensions have their own cleanup - * code for plugins. Although the "right" design would be - * to centralize that here, i'm omitting it for now. Thus, - * the code below to walk the plugins list will suffice. - */ - - /* Other plugins could be holding weak references that were - * added by us. We need to clean all of those up now. - */ - for (iter = g_PluginSys.m_plugins.begin(); - iter != g_PluginSys.m_plugins.end(); - iter++) - { - (*iter)->DropRefsTo(this); - } - - /* Proceed with the rest of the necessities. */ - CNativeOwner::DropEverything(); -} - -bool CPlugin::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func) -{ - NativeEntry *pEntry; - - if ((pEntry = g_ShareSys.AddFakeNative(pFunc, name, func)) == NULL) - { - return false; - } - - m_Natives.push_back(pEntry); - - return true; -} - -/******************* - * PLUGIN ITERATOR * - *******************/ - -CPluginManager::CPluginIterator::CPluginIterator(List *_mylist) -{ - mylist = _mylist; - Reset(); -} - -IPlugin *CPluginManager::CPluginIterator::GetPlugin() -{ - return (*current); -} - -bool CPluginManager::CPluginIterator::MorePlugins() -{ - return (current != mylist->end()); -} - -void CPluginManager::CPluginIterator::NextPlugin() -{ - current++; -} - -void CPluginManager::CPluginIterator::Release() -{ - g_PluginSys.ReleaseIterator(this); -} - -CPluginManager::CPluginIterator::~CPluginIterator() -{ -} - -void CPluginManager::CPluginIterator::Reset() -{ - current = mylist->begin(); -} - -/****************** - * PLUGIN MANAGER * - ******************/ - -CPluginManager::CPluginManager() -{ - m_LoadLookup = sm_trie_create(); - m_AllPluginsLoaded = false; - m_MyIdent = NULL; - m_LoadingLocked = false; -} - -CPluginManager::~CPluginManager() -{ - /* :NOTICE: - * Ignore the fact that there might be plugins in the cache. - * This usually means that Core is not being unloaded properly, and everything - * will crash anyway. YAY - */ - sm_trie_destroy(m_LoadLookup); - - CStack::iterator iter; - for (iter=m_iters.begin(); iter!=m_iters.end(); iter++) - { - delete (*iter); - } - m_iters.popall(); -} - -void CPluginManager::Shutdown() -{ - List::iterator iter; - - while ((iter = m_plugins.begin()) != m_plugins.end()) - { - UnloadPlugin(*iter); - } -} - -void CPluginManager::LoadAll_FirstPass(const char *config, const char *basedir) -{ - /* First read in the database of plugin settings */ - SMCError err; - SMCStates states; - m_AllPluginsLoaded = false; - if ((err=textparsers->ParseFile_SMC(config, &m_PluginInfo, &states)) != SMCError_Okay) - { - g_Logger.LogError("[SM] Encountered fatal error parsing file \"%s\"", config); - const char *err_msg = textparsers->GetSMCErrorString(err); - if (err_msg) - { - g_Logger.LogError("[SM] Parse error encountered: \"%s\"", err_msg); - } - } - - LoadPluginsFromDir(basedir, NULL); -} - -void CPluginManager::LoadPluginsFromDir(const char *basedir, const char *localpath) -{ - char base_path[PLATFORM_MAX_PATH]; - - /* Form the current path to start reading from */ - if (localpath == NULL) - { - g_LibSys.PathFormat(base_path, sizeof(base_path), "%s", basedir); - } else { - g_LibSys.PathFormat(base_path, sizeof(base_path), "%s/%s", basedir, localpath); - } - - IDirectory *dir = g_LibSys.OpenDirectory(base_path); - - if (!dir) - { - char error[256]; - g_LibSys.GetPlatformError(error, sizeof(error)); - g_Logger.LogError("[SM] Failure reading from plugins path: %s", localpath); - g_Logger.LogError("[SM] Platform returned error: %s", error); - return; - } - - while (dir->MoreFiles()) - { - if (dir->IsEntryDirectory() - && (strcmp(dir->GetEntryName(), ".") != 0) - && (strcmp(dir->GetEntryName(), "..") != 0) - && (strcmp(dir->GetEntryName(), "disabled") != 0) - && (strcmp(dir->GetEntryName(), "optional") != 0)) - { - char new_local[PLATFORM_MAX_PATH]; - if (localpath == NULL) - { - /* If no path yet, don't add a former slash */ - snprintf(new_local, sizeof(new_local), "%s", dir->GetEntryName()); - } else { - g_LibSys.PathFormat(new_local, sizeof(new_local), "%s/%s", localpath, dir->GetEntryName()); - } - LoadPluginsFromDir(basedir, new_local); - } else if (dir->IsEntryFile()) { - const char *name = dir->GetEntryName(); - size_t len = strlen(name); - if (len >= 4 - && strcmp(&name[len-4], ".smx") == 0) - { - /* If the filename matches, load the plugin */ - char plugin[PLATFORM_MAX_PATH]; - if (localpath == NULL) - { - snprintf(plugin, sizeof(plugin), "%s", name); - } else { - g_LibSys.PathFormat(plugin, sizeof(plugin), "%s/%s", localpath, name); - } - LoadAutoPlugin(plugin); - } - } - dir->NextEntry(); - } - g_LibSys.CloseDirectory(dir); -} - -LoadRes CPluginManager::_LoadPlugin(CPlugin **_plugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength) -{ - if (m_LoadingLocked) - { - return LoadRes_NeverLoad; - } - - int err; - bool no_load = false; - PluginSettings *pset; - unsigned int setcount = m_PluginInfo.GetSettingsNum(); - for (unsigned int i=0; iblockload_val) - { - no_load = true; - break; - } - } - - /** - * Does this plugin already exist? - */ - CPlugin *pPlugin; - if (sm_trie_retrieve(m_LoadLookup, path, (void **)&pPlugin)) - { - /* Check to see if we should try reloading it */ - if (pPlugin->GetStatus() == Plugin_BadLoad - || pPlugin->GetStatus() == Plugin_Error - || pPlugin->GetStatus() == Plugin_Failed - || no_load) - { - UnloadPlugin(pPlugin); - } - else - { - if (_plugin) - { - *_plugin = pPlugin; - } - return LoadRes_AlreadyLoaded; - } - } - - if (no_load) - { - return LoadRes_NeverLoad; - } - - pPlugin = CPlugin::CreatePlugin(path, error, maxlength); - - assert(pPlugin != NULL); - - pPlugin->m_type = PluginType_MapUpdated; - - ICompilation *co = NULL; - - if (pPlugin->m_status == Plugin_Uncompiled) - { - co = g_pSourcePawn2->StartCompilation(); - } - - for (unsigned int i=0; im_type = pset->type_val; - if (co) - { - for (unsigned int j=0; jopts_num; j++) - { - const char *key, *val; - m_PluginInfo.GetOptionsForPlugin(pset, j, &key, &val); - if (!key || !val) - { - continue; - } - if ((err = co->SetOption(key, val)) == SP_ERROR_NONE) - { - if (error) - { - snprintf(error, maxlength, "Unable to set JIT option (key \"%s\") (value \"%s\")", key, val); - } - co->Abort(); - co = NULL; - break; - } - } - } - } - - /* Do the actual compiling */ - if (co != NULL) - { - char fullpath[PLATFORM_MAX_PATH]; - g_SourceMod.BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename); - - pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(co, fullpath, &err); - if (pPlugin->m_pRuntime == NULL) - { - snprintf(error, - maxlength, - "Unable to load plugin (error %d: %s)", - err, - g_pSourcePawn2->GetErrorString(err)); - pPlugin->m_status = Plugin_BadLoad; - } - else - { - if (pPlugin->UpdateInfo()) - { - pPlugin->m_status = Plugin_Created; - } - else - { - UTIL_Format(error, maxlength, "%s", pPlugin->m_errormsg); - } - } - } - - /* Get the status */ - if (pPlugin->GetStatus() == Plugin_Created) - { - /* First native pass - add anything from Core */ - g_ShareSys.BindNativesToPlugin(pPlugin, true); - pPlugin->InitIdentity(); - if (pPlugin->Call_AskPluginLoad(error, maxlength)) - { - /* Autoload any modules */ - LoadOrRequireExtensions(pPlugin, 1, error, maxlength); - } - else - { - pPlugin->SetErrorState(Plugin_Failed, "%s", error); - } - } - - /* Save the time stamp */ - time_t t = pPlugin->GetFileTimeStamp(); - pPlugin->SetTimeStamp(t); - - if (_plugin) - { - *_plugin = pPlugin; - } - - return (pPlugin->GetStatus() == Plugin_Loaded) ? LoadRes_Successful : LoadRes_Failure; -} - -IPlugin *CPluginManager::LoadPlugin(const char *path, bool debug, PluginType type, char error[], size_t maxlength, bool *wasloaded) -{ - CPlugin *pl; - LoadRes res; - - *wasloaded = false; - if ((res=_LoadPlugin(&pl, path, true, type, error, maxlength)) == LoadRes_Failure) - { - delete pl; - return NULL; - } - - if (res == LoadRes_AlreadyLoaded) - { - *wasloaded = true; - return pl; - } - - if (res == LoadRes_NeverLoad) - { - if (m_LoadingLocked) - { - UTIL_Format(error, maxlength, "There is a global plugin loading lock in effect"); - } - else - { - UTIL_Format(error, maxlength, "This plugin is blocked from loading (see plugin_settings.cfg)"); - } - return NULL; - } - - AddPlugin(pl); - - /* Run second pass if we need to */ - if (IsLateLoadTime() && pl->GetStatus() == Plugin_Loaded) - { - if (!RunSecondPass(pl, error, maxlength)) - { - UnloadPlugin(pl); - return NULL; - } - pl->Call_OnAllPluginsLoaded(); - } - - return pl; -} - -void CPluginManager::LoadAutoPlugin(const char *plugin) -{ - CPlugin *pl = NULL; - LoadRes res; - char error[255] = "Unknown error"; - - if ((res=_LoadPlugin(&pl, plugin, false, PluginType_MapUpdated, error, sizeof(error))) == LoadRes_Failure) - { - g_Logger.LogError("[SM] Failed to load plugin \"%s\": %s", plugin, error); - pl->SetErrorState( - pl->GetStatus() == Plugin_BadLoad ? Plugin_BadLoad : Plugin_Failed, - "%s", - error); - } - - if (res == LoadRes_Successful || res == LoadRes_Failure) - { - AddPlugin(pl); - } -} - -void CPluginManager::AddPlugin(CPlugin *pPlugin) -{ - List::iterator iter; - IPluginsListener *pListener; - - for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) - { - pListener = (*iter); - pListener->OnPluginCreated(pPlugin); - } - - m_plugins.push_back(pPlugin); - sm_trie_insert(m_LoadLookup, pPlugin->m_filename, pPlugin); -} - -void CPluginManager::LoadAll_SecondPass() -{ - List::iterator iter; - CPlugin *pPlugin; - - char error[256]; - for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) - { - pPlugin = (*iter); - if (pPlugin->GetStatus() == Plugin_Loaded) - { - error[0] = '\0'; - if (!RunSecondPass(pPlugin, error, sizeof(error))) - { - g_Logger.LogError("[SM] Unable to load plugin \"%s\": %s", pPlugin->GetFilename(), error); - pPlugin->SetErrorState(Plugin_Failed, "%s", error); - } - } - } - - m_AllPluginsLoaded = true; -} - -bool CPluginManager::FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength) -{ - struct _pl - { - cell_t name; - cell_t file; - cell_t required; - } *pl; - - IPluginContext *pBase = pPlugin->GetBaseContext(); - uint32_t num = pBase->GetPubVarsNum(); - sp_pubvar_t *pubvar; - char *name, *file; - char pathfile[PLATFORM_MAX_PATH]; - - for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) - { - continue; - } - if (strncmp(pubvar->name, "__pl_", 5) == 0) - { - pl = (_pl *)pubvar->offs; - if (pBase->LocalToString(pl->file, &file) != SP_ERROR_NONE) - { - continue; - } - if (pBase->LocalToString(pl->name, &name) != SP_ERROR_NONE) - { - continue; - } - g_LibSys.GetFileFromPath(pathfile, sizeof(pathfile), pPlugin->GetFilename()); - if (strcmp(pathfile, file) == 0) - { - continue; - } - if (pl->required == false) - { - IPluginFunction *pFunc; - char buffer[64]; - UTIL_Format(buffer, sizeof(buffer), "__pl_%s_SetNTVOptional", &pubvar->name[5]); - if ((pFunc=pBase->GetFunctionByName(buffer))) - { - cell_t res; - pFunc->Execute(&res); - if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE) - { - if (error) - { - snprintf(error, maxlength, "Fatal error during initializing plugin load"); - } - return false; - } - } - } - else - { - /* Check that we aren't registering the same library twice */ - if (pPlugin->m_RequiredLibs.find(name) == pPlugin->m_RequiredLibs.end()) - { - pPlugin->m_RequiredLibs.push_back(name); - } - else - { - continue; - } - List::iterator iter; - CPlugin *pl; - bool found = false; - for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) - { - pl = (*iter); - if (pl->m_Libraries.find(name) != pl->m_Libraries.end()) - { - found = true; - break; - } - } - if (!found) - { - if (error) - { - snprintf(error, maxlength, "Could not find required plugin \"%s\"", name); - } - return false; - } - } - } - } - - return true; -} - -bool CPluginManager::LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength) -{ - /* Find any extensions this plugin needs */ - struct _ext - { - cell_t name; - cell_t file; - cell_t autoload; - cell_t required; - } *ext; - - IPluginContext *pBase = pPlugin->GetBaseContext(); - uint32_t num = pBase->GetPubVarsNum(); - sp_pubvar_t *pubvar; - IExtension *pExt; - char path[PLATFORM_MAX_PATH]; - char *file, *name; - for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) - { - continue; - } - if (strncmp(pubvar->name, "__ext_", 6) == 0) - { - ext = (_ext *)pubvar->offs; - if (pBase->LocalToString(ext->file, &file) != SP_ERROR_NONE) - { - continue; - } - if (pBase->LocalToString(ext->name, &name) != SP_ERROR_NONE) - { - continue; - } - if (pass == 1) - { - /* Attempt to auto-load if necessary */ - if (ext->autoload) - { - g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); - g_Extensions.LoadAutoExtension(path); - } - } - else if (pass == 2) - { - /* Is this required? */ - if (ext->required) - { - g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); - if ((pExt = g_Extensions.FindExtensionByFile(path)) == NULL) - { - pExt = g_Extensions.FindExtensionByName(name); - } - /* :TODO: should we bind to unloaded extensions? - * Currently the extension manager will ignore this. - */ - if (!pExt || !pExt->IsRunning(NULL, 0)) - { - if (error) - { - snprintf(error, maxlength, "Required extension \"%s\" file(\"%s\") not running", name, file); - } - return false; - } - else - { - g_Extensions.BindChildPlugin(pExt, pPlugin); - } - } - else - { - IPluginFunction *pFunc; - char buffer[64]; - UTIL_Format(buffer, sizeof(buffer), "__ext_%s_SetNTVOptional", &pubvar->name[6]); - - if ((pFunc = pBase->GetFunctionByName(buffer)) != NULL) - { - cell_t res; - pFunc->Execute(&res); - if (pPlugin->GetBaseContext()->GetLastNativeError() != SP_ERROR_NONE) - { - if (error) - { - UTIL_Format(error, maxlength, "Fatal error during plugin initialization (ext req)"); - } - return false; - } - } - } - } - } - } - - return true; -} - -bool CPluginManager::RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength) -{ - /* Second pass for extension requirements */ - if (!LoadOrRequireExtensions(pPlugin, 2, error, maxlength)) - { - return false; - } - - if (!FindOrRequirePluginDeps(pPlugin, error, maxlength)) - { - return false; - } - - /* Run another binding pass */ - g_ShareSys.BindNativesToPlugin(pPlugin, false); - - /* Find any unbound natives. Right now, these are not allowed. */ - IPluginContext *pContext = pPlugin->GetBaseContext(); - uint32_t num = pContext->GetNativesNum(); - sp_native_t *native; - for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) - { - break; - } - if (native->status == SP_NATIVE_UNBOUND - && native->name[0] != '@' - && !(native->flags & SP_NTVFLAG_OPTIONAL)) - { - if (error) - { - snprintf(error, maxlength, "Native \"%s\" was not found", native->name); - } - return false; - } - } - - /* Finish by telling all listeners */ - List::iterator iter; - IPluginsListener *pListener; - for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) - { - pListener = (*iter); - pListener->OnPluginLoaded(pPlugin); - } - - /* Tell this plugin to finish initializing itself */ - pPlugin->Call_OnPluginStart(); - - /* Now, if we have fake natives, go through all plugins that might need rebinding */ - if (pPlugin->GetStatus() <= Plugin_Paused && pPlugin->m_Natives.size()) - { - List::iterator pl_iter; - CPlugin *pOther; - for (pl_iter = m_plugins.begin(); - pl_iter != m_plugins.end(); - pl_iter++) - { - pOther = (*pl_iter); - if ((pOther->GetStatus() == Plugin_Error - && (pOther->m_FakeNativesMissing || pOther->m_LibraryMissing)) - || pOther->m_FakeNativesMissing) - { - TryRefreshDependencies(pOther); - } - else if ((pOther->GetStatus() == Plugin_Running - || pOther->GetStatus() == Plugin_Paused) - && pOther != pPlugin) - { - List::iterator nv_iter; - for (nv_iter = pPlugin->m_Natives.begin(); - nv_iter != pPlugin->m_Natives.end(); - nv_iter++) - { - g_ShareSys.BindNativeToPlugin(pOther, (*nv_iter)); - } - } - } - } - - /* Go through our libraries and tell other plugins they're added */ - List::iterator s_iter; - for (s_iter = pPlugin->m_Libraries.begin(); - s_iter != pPlugin->m_Libraries.end(); - s_iter++) - { - OnLibraryAction((*s_iter).c_str(), true, false); - } - - /* :TODO: optimize? does this even matter? */ - pPlugin->GetPhrases()->AddPhraseFile("core.phrases"); - - return true; -} - -void CPluginManager::TryRefreshDependencies(CPlugin *pPlugin) -{ - assert(pPlugin->GetBaseContext() != NULL); - - g_ShareSys.BindNativesToPlugin(pPlugin, false); - - List::iterator lib_iter; - List::iterator req_iter; - List::iterator pl_iter; - CPlugin *pl; - for (req_iter=pPlugin->m_RequiredLibs.begin(); req_iter!=pPlugin->m_RequiredLibs.end(); req_iter++) - { - bool found = false; - for (pl_iter=m_plugins.begin(); pl_iter!=m_plugins.end(); pl_iter++) - { - pl = (*pl_iter); - for (lib_iter=pl->m_Libraries.begin(); lib_iter!=pl->m_Libraries.end(); lib_iter++) - { - if ((*req_iter) == (*lib_iter)) - { - found = true; - } - } - } - if (!found) - { - pPlugin->SetErrorState(Plugin_Error, "Library not found: %s", (*req_iter).c_str()); - return; - } - } - - /* Find any unbound natives - * Right now, these are not allowed - */ - IPluginContext *pContext = pPlugin->GetBaseContext(); - uint32_t num = pContext->GetNativesNum(); - sp_native_t *native; - for (unsigned int i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) - { - break; - } - if (native->status == SP_NATIVE_UNBOUND && !(native->flags & SP_NTVFLAG_OPTIONAL)) - { - pPlugin->SetErrorState(Plugin_Error, "Native not found: %s", native->name); - return; - } - } - - if (pPlugin->GetStatus() == Plugin_Error) - { - /* If we got here, all natives are okay again! */ - pPlugin->m_status = Plugin_Running; - if (pPlugin->m_pRuntime->IsPaused()) - { - pPlugin->m_pRuntime->SetPauseState(false); - _SetPauseState(pPlugin, false); - } - } -} - -bool CPluginManager::UnloadPlugin(IPlugin *plugin) -{ - CPlugin *pPlugin = (CPlugin *)plugin; - - /* This prevents removal during insertion or anything else weird */ - if (m_plugins.find(pPlugin) == m_plugins.end()) - { - return false; - } - - IPluginContext *pContext = plugin->GetBaseContext(); - if (pContext != NULL && pContext->IsInExec()) - { - char buffer[255]; - UTIL_Format(buffer, sizeof(buffer), "sm plugins unload %s\n", plugin->GetFilename()); - engine->ServerCommand(buffer); - return false; - } - - /* Remove us from the lookup table and linked list */ - m_plugins.remove(pPlugin); - sm_trie_delete(m_LoadLookup, pPlugin->m_filename); - - /* Go through our libraries and tell other plugins they're gone */ - List::iterator s_iter; - for (s_iter = pPlugin->m_Libraries.begin(); - s_iter != pPlugin->m_Libraries.end(); - s_iter++) - { - OnLibraryAction((*s_iter).c_str(), true, true); - } - - List::iterator iter; - IPluginsListener *pListener; - - if (pPlugin->GetStatus() <= Plugin_Error) - { - /* Notify listeners of unloading */ - for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) - { - pListener = (*iter); - pListener->OnPluginUnloaded(pPlugin); - } - /* Notify plugin */ - pPlugin->Call_OnPluginEnd(); - } - - pPlugin->DropEverything(); - - for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) - { - /* Notify listeners of destruction */ - pListener = (*iter); - pListener->OnPluginDestroyed(pPlugin); - } - - /* Tell the plugin to delete itself */ - delete pPlugin; - - return true; -} - -IPlugin *CPluginManager::FindPluginByContext(const sp_context_t *ctx) -{ - IPlugin *pPlugin; - IPluginContext *pContext; - - pContext = reinterpret_cast(const_cast(ctx)); - - if (pContext->GetKey(2, (void **)&pPlugin)) - { - return pPlugin; - } - - return NULL; -} - -CPlugin *CPluginManager::GetPluginByCtx(const sp_context_t *ctx) -{ - return (CPlugin *)FindPluginByContext(ctx); -} - -unsigned int CPluginManager::GetPluginCount() -{ - return m_plugins.size(); -} - -void CPluginManager::AddPluginsListener(IPluginsListener *listener) -{ - m_listeners.push_back(listener); -} - -void CPluginManager::RemovePluginsListener(IPluginsListener *listener) -{ - m_listeners.remove(listener); -} - -IPluginIterator *CPluginManager::GetPluginIterator() -{ - if (m_iters.empty()) - { - return new CPluginIterator(&m_plugins); - } else { - CPluginIterator *iter = m_iters.front(); - m_iters.pop(); - iter->Reset(); - return iter; - } -} - -void CPluginManager::ReleaseIterator(CPluginIterator *iter) -{ - m_iters.push(iter); -} - -bool CPluginManager::TestAliasMatch(const char *alias, const char *localpath) -{ - /* As an optimization, we do not call strlen, but compute the length in the first pass */ - size_t alias_len = 0; - size_t local_len = 0; - - const char *ptr = alias; - unsigned int alias_explicit_paths = 0; - unsigned int alias_path_end = 0; - while (*ptr != '\0') - { - if (*ptr == '\\' || *ptr == '/') - { - alias_explicit_paths++; - alias_path_end = alias_len; - } - alias_len++; - ptr++; - } - - if (alias_explicit_paths && alias_path_end == alias_len - 1) - { - /* Trailing slash is totally invalid here */ - return false; - } - - ptr = localpath; - unsigned int local_explicit_paths = 0; - unsigned int local_path_end = 0; - while (*ptr != '\0') - { - if (*ptr == '\\' || *ptr == '/') - { - local_explicit_paths++; - local_path_end = local_len; - } - local_len++; - ptr++; - } - - /* If the alias has more explicit paths than the real path, - * no match will be possible. - */ - if (alias_explicit_paths > local_explicit_paths) - { - return false; - } - - if (alias_explicit_paths) - { - /* We need to find if the paths match now. For example, these should all match: - * csdm csdm - * csdm optional/csdm - * csdm/ban optional/crab/csdm/ban - */ - const char *aliasptr = alias; - const char *localptr = localpath; - bool match = true; - do - { - if (*aliasptr != *localptr) - { - /* We have to knock one path off */ - local_explicit_paths--; - if (alias_explicit_paths > local_explicit_paths) - { - /* Skip out if we're gonna have an impossible match */ - return false; - } - /* Eat up localptr tokens until we get a result */ - while (((localptr - localpath) < (int)local_path_end) - && *localptr != '/' - && *localptr != '\\') - { - localptr++; - } - /* Check if we hit the end of our searchable area. - * This probably isn't possible because of the path - * count check, but it's a good idea anyway. - */ - if ((localptr - localpath) >= (int)local_path_end) - { - return false; - } else { - /* Consume the slash token */ - localptr++; - } - /* Reset the alias pointer so we can continue consuming */ - aliasptr = alias; - match = false; - continue; - } - /* Note: - * This is safe because if localptr terminates early, aliasptr will too - */ - do - { - /* We should never reach the end of the string because of this check. */ - bool aliasend = (aliasptr - alias) > (int)alias_path_end; - bool localend = (localptr - localpath) > (int)local_path_end; - if (aliasend || localend) - { - if (aliasend && localend) - { - /* we matched, and we can break out now */ - match = true; - break; - } - /* Otherwise, we've hit the end somehow and rest won't match up. Break out. */ - match = false; - break; - } - - /* If we got here, it's safe to compare the next two tokens */ - if (*localptr != *aliasptr) - { - match = false; - break; - } - localptr++; - aliasptr++; - } while (true); - } while (!match); - } - - /* If we got here, it's time to compare filenames */ - const char *aliasptr = alias; - const char *localptr = localpath; - - if (alias_explicit_paths) - { - aliasptr = &alias[alias_path_end + 1]; - } - - if (local_explicit_paths) - { - localptr = &localpath[local_path_end + 1]; - } - - while (true) - { - if (*aliasptr == '*') - { - /* First, see if this is the last character */ - if ((unsigned)(aliasptr - alias) == alias_len - 1) - { - /* If so, there's no need to match anything else */ - return true; - } - /* Otherwise, we need to search for an appropriate matching sequence in local. - * Note that we only need to search up to the next asterisk. - */ - aliasptr++; - bool match = true; - const char *local_orig = localptr; - do - { - match = true; - while (*aliasptr != '\0' && *aliasptr != '*') - { - /* Since aliasptr is never '\0', localptr hitting the end will fail */ - if (*aliasptr != *localptr) - { - match = false; - break; - } - aliasptr++; - localptr++; - } - if (!match) - { - /* If we didn't get a match, we need to advance the search stream. - * This will let us skip tokens while still searching for another match. - */ - localptr = ++local_orig; - /* Make sure we don't go out of bounds */ - if (*localptr == '\0') - { - break; - } - } - } while (!match); - - if (!match) - { - return false; - } else { - /* If we got a match, move on to the next token */ - continue; - } - } else if (*aliasptr == '\0') { - if (*localptr == '\0' - || - strcmp(localptr, ".smx") == 0) - { - return true; - } else { - return false; - } - } else if (*aliasptr != *localptr) { - return false; - } - aliasptr++; - localptr++; - } - - return true; -} - -bool CPluginManager::IsLateLoadTime() const -{ - return (m_AllPluginsLoaded || !g_SourceMod.IsMapLoading()); -} - -void CPluginManager::OnSourceModAllInitialized() -{ - m_MyIdent = g_ShareSys.CreateCoreIdentity(); - - HandleAccess sec; - g_HandleSys.InitAccessDefaults(NULL, &sec); - - sec.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY; - sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY; - - g_PluginType = g_HandleSys.CreateType("Plugin", this, 0, NULL, &sec, m_MyIdent, NULL); - g_PluginIdent = g_ShareSys.CreateIdentType("PLUGIN"); - - g_RootMenu.AddRootConsoleCommand("plugins", "Manage Plugins", this); - - g_ShareSys.AddInterface(NULL, this); -} - -void CPluginManager::OnSourceModShutdown() -{ - g_RootMenu.RemoveRootConsoleCommand("plugins", this); - - UnloadAll(); - - g_HandleSys.RemoveType(g_PluginType, m_MyIdent); - g_ShareSys.DestroyIdentType(g_PluginIdent); - g_ShareSys.DestroyIdentity(m_MyIdent); -} - -void CPluginManager::OnHandleDestroy(HandleType_t type, void *object) -{ - /* We don't care about the internal object, actually */ -} - -bool CPluginManager::GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize) -{ - *pSize = ((CPlugin *)object)->CalcMemUsage(); - return true; -} - -IPlugin *CPluginManager::PluginFromHandle(Handle_t handle, HandleError *err) -{ - IPlugin *pPlugin; - HandleError _err; - - HandleSecurity sec; - - sec.pOwner = NULL; - sec.pIdentity = m_MyIdent; - - if ((_err=g_HandleSys.ReadHandle(handle, g_PluginType, &sec, (void **)&pPlugin)) != HandleError_None) - { - pPlugin = NULL; - } - - if (err) - { - *err = _err; - } - - return pPlugin; -} - -CPlugin *CPluginManager::GetPluginByOrder(int num) -{ - if (num < 1 || num > (int)GetPluginCount()) - { - return NULL; - } - - CPlugin *pl; - int id = 1; - - SourceHook::List::iterator iter; - for (iter = m_plugins.begin(); iter != m_plugins.end() && id < num; iter++, id++) {} - - pl = *iter; - - return pl; -} - -const char *CPluginManager::GetStatusText(PluginStatus st) -{ - switch (st) - { - case Plugin_Running: - return "Running"; - case Plugin_Paused: - return "Paused"; - case Plugin_Error: - return "Error"; - case Plugin_Uncompiled: - return "Uncompiled"; - case Plugin_BadLoad: - return "Bad Load"; - case Plugin_Failed: - return "Failed"; - default: - assert(false); - return "-"; - } -} - -void CPluginManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) -{ - int argcount = command.ArgC(); - if (argcount >= 3) - { - const char *cmd = command.Arg(2); - if (strcmp(cmd, "list") == 0) - { - char buffer[256]; - unsigned int id = 1; - int plnum = GetPluginCount(); - - if (!plnum) - { - g_RootMenu.ConsolePrint("[SM] No plugins loaded"); - return; - } - else - { - g_RootMenu.ConsolePrint("[SM] Listing %d plugin%s:", GetPluginCount(), (plnum > 1) ? "s" : ""); - } - - CPlugin *pl; - SourceHook::List::iterator iter; - SourceHook::List m_FailList; - - for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++) - { - pl = (*iter); - assert(pl->GetStatus() != Plugin_Created); - int len = 0; - const sm_plugininfo_t *info = pl->GetPublicInfo(); - if (pl->GetStatus() != Plugin_Running) - { - len += UTIL_Format(buffer, sizeof(buffer), " %02d <%s>", id, GetStatusText(pl->GetStatus())); - - if (pl->GetStatus() <= Plugin_Error) - { - /* Plugin has failed to load. */ - m_FailList.push_back(pl); - } - } - else - { - len += UTIL_Format(buffer, sizeof(buffer), " %02d", id); - } - if (pl->GetStatus() < Plugin_Created) - { - len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " \"%s\"", (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); - if (IS_STR_FILLED(info->version)) - { - len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", info->version); - } - if (IS_STR_FILLED(info->author)) - { - UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", info->author); - } - } - else - { - UTIL_Format(&buffer[len], sizeof(buffer)-len, " %s", pl->m_filename); - } - g_RootMenu.ConsolePrint("%s", buffer); - } - - if (!m_FailList.empty()) - { - g_RootMenu.ConsolePrint("Load Errors:"); - - SourceHook::List::iterator _iter; - - CPlugin *pl; - - for (_iter=m_FailList.begin(); _iter!=m_FailList.end(); _iter++) - { - pl = (CPlugin *)*_iter; - g_RootMenu.ConsolePrint("%s: %s",(IS_STR_FILLED(pl->GetPublicInfo()->name)) ? pl->GetPublicInfo()->name : pl->GetFilename(), pl->m_errormsg); - } - } - - return; - } - else if (strcmp(cmd, "load") == 0) - { - if (argcount < 4) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm plugins load "); - return; - } - - char error[128]; - bool wasloaded; - const char *filename = command.Arg(3); - - char pluginfile[256]; - const char *ext = g_LibSys.GetFileExtension(filename) ? "" : ".smx"; - UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", filename, ext); - - IPlugin *pl = LoadPlugin(pluginfile, false, PluginType_MapUpdated, error, sizeof(error), &wasloaded); - - if (wasloaded) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s is already loaded.", pluginfile); - return; - } - - if (pl) - { - g_RootMenu.ConsolePrint("[SM] Loaded plugin %s successfully.", pluginfile); - } - else - { - g_RootMenu.ConsolePrint("[SM] Plugin %s failed to load: %s.", pluginfile, error); - } - - return; - } - else if (strcmp(cmd, "unload") == 0) - { - if (argcount < 4) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm plugins unload <#|file>"); - return; - } - - CPlugin *pl; - char *end; - const char *arg = command.Arg(3); - int id = strtol(arg, &end, 10); - - if (*end == '\0') - { - pl = GetPluginByOrder(id); - if (!pl) - { - g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); - return; - } - } - else - { - char pluginfile[256]; - const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; - UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); - - if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); - return; - } - } - - char name[PLATFORM_MAX_PATH]; - - if (pl->GetStatus() < Plugin_Created) - { - const sm_plugininfo_t *info = pl->GetPublicInfo(); - UTIL_Format(name, sizeof(name), (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); - } - else - { - UTIL_Format(name, sizeof(name), "%s", pl->GetFilename()); - } - - if (UnloadPlugin(pl)) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s unloaded successfully.", name); - } - else - { - g_RootMenu.ConsolePrint("[SM] Failed to unload plugin %s.", name); - } - - return; - } - else if (strcmp(cmd, "unload_all") == 0) - { - g_PluginSys.UnloadAll(); - g_RootMenu.ConsolePrint("[SM] All plugins have been unloaded."); - return; - } - else if (strcmp(cmd, "load_lock") == 0) - { - if (m_LoadingLocked) - { - g_RootMenu.ConsolePrint("[SM] There is already a loading lock in effect."); - } - else - { - m_LoadingLocked = true; - g_RootMenu.ConsolePrint("[SM] Loading is now locked; no plugins will be loaded or re-loaded."); - } - return; - } - else if (strcmp(cmd, "load_unlock") == 0) - { - if (m_LoadingLocked) - { - m_LoadingLocked = false; - g_RootMenu.ConsolePrint("[SM] The loading lock is no longer in effect."); - } - else - { - g_RootMenu.ConsolePrint("[SM] There was no loading lock in effect."); - } - return; - } - else if (strcmp(cmd, "info") == 0) - { - if (argcount < 4) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm plugins info <#>"); - return; - } - - CPlugin *pl; - char *end; - const char *arg = command.Arg(3); - int id = strtol(arg, &end, 10); - - if (*end == '\0') - { - pl = GetPluginByOrder(id); - if (!pl) - { - g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); - return; - } - } - else - { - char pluginfile[256]; - const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; - UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); - - if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); - return; - } - } - - const sm_plugininfo_t *info = pl->GetPublicInfo(); - - g_RootMenu.ConsolePrint(" Filename: %s", pl->GetFilename()); - if (pl->GetStatus() <= Plugin_Error) - { - if (IS_STR_FILLED(info->name)) - { - if (IS_STR_FILLED(info->description)) - { - g_RootMenu.ConsolePrint(" Title: %s (%s)", info->name, info->description); - } else { - g_RootMenu.ConsolePrint(" Title: %s", info->name); - } - } - if (IS_STR_FILLED(info->author)) - { - g_RootMenu.ConsolePrint(" Author: %s", info->author); - } - if (IS_STR_FILLED(info->version)) - { - g_RootMenu.ConsolePrint(" Version: %s", info->version); - } - if (IS_STR_FILLED(info->url)) - { - g_RootMenu.ConsolePrint(" URL: %s", info->url); - } - if (pl->GetStatus() == Plugin_Error) - { - g_RootMenu.ConsolePrint(" Error: %s", pl->m_errormsg); - } - else - { - if (pl->GetStatus() == Plugin_Running) - { - g_RootMenu.ConsolePrint(" Status: running"); - } - else - { - g_RootMenu.ConsolePrint(" Status: not running"); - } - - const char *typestr = ""; - switch (pl->GetType()) - { - case PluginType_MapUpdated: - typestr = "Map Change if Updated"; - break; - case PluginType_MapOnly: - typestr = "Map Change"; - break; - case PluginType_Private: - case PluginType_Global: - typestr = "Never"; - break; - } - - g_RootMenu.ConsolePrint(" Reloads: %s", typestr); - } - if (pl->m_FileVersion >= 3) - { - g_RootMenu.ConsolePrint(" Timestamp: %s", pl->m_DateTime); - } - } - else - { - g_RootMenu.ConsolePrint(" Load error: %s", pl->m_errormsg); - if (pl->GetStatus() < Plugin_Created) - { - g_RootMenu.ConsolePrint(" File info: (title \"%s\") (version \"%s\")", - info->name ? info->name : "", - info->version ? info->version : ""); - if (IS_STR_FILLED(info->url)) - { - g_RootMenu.ConsolePrint(" File URL: %s", info->url); - } - } - } - - return; - } - else if (strcmp(cmd, "refresh") == 0) - { - g_SourceMod.DoGlobalPluginLoads(); - g_RootMenu.ConsolePrint("[SM] The plugin list has been refreshed and reloaded."); - return; - } - else if (strcmp(cmd, "reload") == 0) - { - if (argcount < 4) - { - g_RootMenu.ConsolePrint("[SM] Usage: sm plugins reload <#|file>"); - return; - } - - CPlugin *pl; - char *end; - const char *arg = command.Arg(3); - int id = strtol(arg, &end, 10); - - if (*end == '\0') - { - pl = GetPluginByOrder(id); - if (!pl) - { - g_RootMenu.ConsolePrint("[SM] Plugin index %d not found.", id); - return; - } - } - else - { - char pluginfile[256]; - const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; - UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); - - if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s is not loaded.", pluginfile); - return; - } - } - - char name[PLATFORM_MAX_PATH]; - const sm_plugininfo_t *info = pl->GetPublicInfo(); - strcpy(name, (IS_STR_FILLED(info->name)) ? info->name : pl->GetFilename()); - - if (ReloadPlugin(pl)) - { - g_RootMenu.ConsolePrint("[SM] Plugin %s reloaded successfully.", name); - } - else - { - g_RootMenu.ConsolePrint("[SM] Failed to reload plugin %s.", name); - } - - return; - } - } - - /* Draw the main menu */ - g_RootMenu.ConsolePrint("SourceMod Plugins Menu:"); - g_RootMenu.DrawGenericOption("info", "Information about a plugin"); - g_RootMenu.DrawGenericOption("list", "Show loaded plugins"); - g_RootMenu.DrawGenericOption("load", "Load a plugin"); - g_RootMenu.DrawGenericOption("load_lock", "Prevents any more plugins from being loaded"); - g_RootMenu.DrawGenericOption("load_unlock", "Re-enables plugin loading"); - g_RootMenu.DrawGenericOption("refresh", "Reloads/refreshes all plugins in the plugins folder"); - g_RootMenu.DrawGenericOption("reload", "Reloads a plugin"); - g_RootMenu.DrawGenericOption("unload", "Unload a plugin"); - g_RootMenu.DrawGenericOption("unload_all", "Unloads all plugins"); -} - -bool CPluginManager::ReloadPlugin(CPlugin *pl) -{ - List::iterator iter; - char filename[PLATFORM_MAX_PATH]; - bool wasloaded; - PluginType ptype; - IPlugin *newpl; - int id = 1; - - strcpy(filename, pl->m_filename); - ptype = pl->GetType(); - - for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++, id++) - { - if ((*iter) == pl) - { - break; - } - } - - if (!UnloadPlugin(pl)) - { - return false; - } - if (!(newpl=LoadPlugin(filename, true, ptype, NULL, 0, &wasloaded))) - { - return false; - } - - for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) - { - if ((*iter) == (CPlugin *)newpl) - { - m_plugins.erase(iter); - break; - } - } - - int i; - for (i=1, iter=m_plugins.begin(); iter!=m_plugins.end() && i::iterator iter; - List tmp_list = m_plugins; - CPlugin *pl; - time_t t; - - for (iter=tmp_list.begin(); iter!=tmp_list.end(); iter++) - { - pl = (*iter); - if (pl->GetType() == PluginType_MapOnly) - { - UnloadPlugin((IPlugin *)pl); - } - else if (pl->GetType() == PluginType_MapUpdated) - { - t = pl->GetFileTimeStamp(); - if (!t || t > pl->GetTimeStamp()) - { - pl->SetTimeStamp(t); - UnloadPlugin((IPlugin *)pl); - } - } - } -} - -void CPluginManager::_SetPauseState(CPlugin *pl, bool paused) -{ - List::iterator iter; - IPluginsListener *pListener; - for (iter=m_listeners.begin(); iter!=m_listeners.end(); iter++) - { - pListener = (*iter); - pListener->OnPluginPauseChange(pl, paused); - } -} - -void CPluginManager::AddFunctionsToForward(const char *name, IChangeableForward *pForward) -{ - List::iterator iter; - CPlugin *pPlugin; - IPluginFunction *pFunction; - - for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++) - { - pPlugin = (*iter); - - if (pPlugin->GetStatus() <= Plugin_Paused) - { - pFunction = pPlugin->GetBaseContext()->GetFunctionByName(name); - - if (pFunction) - { - pForward->AddFunction(pFunction); - } - } - } -} - -CPlugin *CPluginManager::GetPluginFromIdentity(IdentityToken_t *pToken) -{ - if (pToken->type != g_PluginIdent) - { - return NULL; - } - - return (CPlugin *)(pToken->ptr); -} - -void CPluginManager::OnLibraryAction(const char *lib, bool is_a_plugin, bool drop) -{ - List::iterator iter; - - struct _pl - { - cell_t name; - cell_t file; - cell_t required; - } *plc; - - struct _ext - { - cell_t name; - cell_t file; - cell_t autoload; - cell_t required; - } *ext; - - const char *name = drop ? "OnLibraryRemoved" : "OnLibraryAdded"; - - for (iter=m_plugins.begin(); - iter!=m_plugins.end(); - iter++) - { - CPlugin *pl = (*iter); - if (pl->GetStatus() != Plugin_Running) - { - continue; - } - IPluginContext *pContext = pl->GetBaseContext(); - IPluginFunction *pf = pContext->GetFunctionByName(name); - if (!pf) - { - continue; - } - uint32_t num_vars = pContext->GetPubVarsNum(); - for (uint32_t i=0; iGetPubvarByIndex(i, &pubvar) != SP_ERROR_NONE) - { - continue; - } - if (is_a_plugin && strncmp(pubvar->name, "__pl_", 5) == 0) - { - plc = (_pl *)pubvar->offs; - if (plc->required) - { - continue; - } - char *str; - pContext->LocalToString(plc->name, &str); - if (strcmp(str, lib) != 0) - { - continue; - } - pf->PushString(lib); - pf->Execute(NULL); - } - else if (!is_a_plugin && strncmp(pubvar->name, "__ext_", 6) == 0) - { - ext = (_ext *)pubvar->offs; - if (ext->required) - { - continue; - } - char *str; - pContext->LocalToString(ext->name, &str); - if (strcmp(str, lib) != 0) - { - continue; - } - pf->PushString(lib); - pf->Execute(NULL); - } - } - } -} - -bool CPluginManager::LibraryExists(const char *lib) -{ - List::iterator iter; - - for (iter=m_plugins.begin(); - iter!=m_plugins.end(); - iter++) - { - CPlugin *pl = (*iter); - if (pl->GetStatus() != Plugin_Running) - { - continue; - } - List::iterator s_iter; - for (s_iter = pl->m_Libraries.begin(); - s_iter != pl->m_Libraries.end(); - s_iter++) - { - if ((*s_iter).compare(lib) == 0) - { - return true; - } - } - } - - return false; -} - -void CPluginManager::AllPluginsLoaded() -{ - List::iterator iter; - CPlugin *pl; - - for (iter=m_plugins.begin(); iter!=m_plugins.end(); iter++) - { - pl = (*iter); - pl->Call_OnAllPluginsLoaded(); - } -} - -void CPluginManager::UnloadAll() -{ - List::iterator iter; - while ( (iter = m_plugins.begin()) != m_plugins.end() ) - { - UnloadPlugin((*iter)); - } -} - -int CPluginManager::GetOrderOfPlugin(IPlugin *pl) -{ - int id = 1; - List::iterator iter; - - for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++, id++) - { - if ((*iter) == pl) - { - return id; - } - } - - return -1; -} - -CPlugin *CPluginManager::FindPluginByConsoleArg(const char *arg) -{ - int id; - char *end; - CPlugin *pl; - - id = strtol(arg, &end, 10); - - if (*end == '\0') - { - pl = GetPluginByOrder(id); - if (!pl) - { - return NULL; - } - } - else - { - char pluginfile[256]; - const char *ext = g_LibSys.GetFileExtension(arg) ? "" : ".smx"; - UTIL_Format(pluginfile, sizeof(pluginfile), "%s%s", arg, ext); - - if (!sm_trie_retrieve(m_LoadLookup, pluginfile, (void **)&pl)) - { - return NULL; - } - } - - return pl; -} - -void CPluginManager::OnSourceModMaxPlayersChanged(int newvalue) -{ - SyncMaxClients(newvalue); -} - -void CPluginManager::SyncMaxClients(int max_clients) -{ - List::iterator iter; - - for (iter = m_plugins.begin(); iter != m_plugins.end(); iter++) - { - (*iter)->SyncMaxClients(max_clients); - } -} diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/PluginSys.h --- a/core/systems/PluginSys.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,458 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ -#define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sm_globals.h" -#include "PluginInfoDatabase.h" -#include "sm_trie.h" -#include "sourcemod.h" -#include -#if defined ORANGEBOX_BUILD -#include "convar_sm_ob.h" -#else -#include "convar_sm.h" -#endif -#include "ITranslator.h" -#include "NativeOwner.h" -#include "ShareSys.h" - -using namespace SourceHook; - -/** - * NOTES: - * - * UPDATE 2008-03-11: These comments are horribly out of date. They paint a good overall - * picture of how PluginSys works, but things like dependencies and fake natives have - * complicated things quite a bit. - * - * Currently this system needs a lot of work but it's good skeletally. Plugin creation - * is done without actually compiling anything. This is done by Load functions in the - * manager. This will need a rewrite when we add context switching. - * - * The plugin object itself has a few things to note. The most important is that it stores - * a table of function objects. The manager marshals allocation and freeing of these objects. - * The plugin object can be in erroneous states, they are: - * Plugin_Error --> Some error occurred any time during or after compilation. - * This error can be cleared since the plugin itself is valid. - * However, the state itself being set prevents any runtime action. - * Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it. - * - * If a plugin fails to load externally, it is never added to the internal tracker. However, - * plugins that failed to load from the internal loading mechanism are always tracked. This - * allows users to see which automatically loaded plugins failed, and makes the interface a bit - * more flexible. - * - * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid - * for execution. SourceMod is a two pass system, and even though the second pass is not implemented - * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When - * implemented, a Created plugin will be switched to Error in the second pass if it not loadable. - * - * The two pass loading mechanism is described below. Modules/natives are not implemented yet. - * PASS ONE: All loadable plugins are found and have the following steps performed: - * 1. Loading and compilation is attempted. - * 2. If successful, all natives from Core are added. - * 3. OnPluginLoad() is called. - * 4. If failed, any user natives are scrapped and the process halts here. - * 5. If successful, the plugin is ready for Pass 2. - * INTERMEDIATE: - * 1. All forced modules are loaded. - * PASS TWO: All loaded plugins are found and have these steps performed: - * 1. Any modules referenced in the plugin that are not already loaded, are loaded. - * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6. - * 3. If any natives are unresolved, check if they are found in the user-natives pool. - * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter. - * 5. If the filter fails, the plugin is marked as failed. - * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped. - * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran. - * PASS THREE (not a real pass): - * 7. Once all plugins are deemed to be loaded, OnPluginStart() is called - */ - -enum LoadRes -{ - LoadRes_Successful, - LoadRes_AlreadyLoaded, - LoadRes_Failure, - LoadRes_NeverLoad -}; - -struct AutoConfig -{ - String autocfg; - String folder; - bool create; -}; - -class CPlugin; - -class CPlugin : - public IPlugin, - public CNativeOwner -{ - friend class CPluginManager; - friend class CFunction; -public: - CPlugin(const char *file); - ~CPlugin(); -public: - PluginType GetType(); - SourcePawn::IPluginContext *GetBaseContext(); - sp_context_t *GetContext(); - void *GetPluginStructure(); - const char *GetFilename(); - bool IsDebugging(); - PluginStatus GetStatus(); - const sm_plugininfo_t *GetPublicInfo(); - bool SetPauseState(bool paused); - unsigned int GetSerial(); - IdentityToken_t *GetIdentity(); - unsigned int CalcMemUsage(); - bool SetProperty(const char *prop, void *ptr); - bool GetProperty(const char *prop, void **ptr, bool remove=false); - void DropEverything(); - SourcePawn::IPluginRuntime *GetRuntime(); -public: - /** - * Creates a plugin object with default values. - * If an error buffer is specified, and an error occurs, the error will be copied to the buffer - * and NULL will be returned. - * If an error buffer is not specified, the error will be copied to an internal buffer and - * a valid (but error-stated) CPlugin will be returned. - */ - static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength); -public: - - /** - * Sets an error state on the plugin - */ - void SetErrorState(PluginStatus status, const char *error_fmt, ...); - - /** - * Initializes the plugin's identity information - */ - void InitIdentity(); - - /** - * Calls the OnPluginLoad function, and sets any failed states if necessary. - * NOTE: Valid pre-states are: Plugin_Created - * NOTE: If validated, plugin state is changed to Plugin_Loaded - * - * If the error buffer is NULL, the error message is cached locally. - */ - bool Call_AskPluginLoad(char *error, size_t maxlength); - - /** - * Calls the OnPluginStart function. - * NOTE: Valid pre-states are: Plugin_Created - * NOTE: Post-state will be Plugin_Running - */ - void Call_OnPluginStart(); - - /** - * Calls the OnPluginEnd function. - */ - void Call_OnPluginEnd(); - - /** - * Calls the OnAllPluginsLoaded function. - */ - void Call_OnAllPluginsLoaded(); - - /** - * Returns true if a plugin is usable. - */ - bool IsRunnable(); - - /** - * Get languages info. - */ - IPhraseCollection *GetPhrases(); -public: - /** - * Returns the modification time during last plugin load. - */ - time_t GetTimeStamp(); - - /** - * Returns the current modification time of the plugin file. - */ - time_t GetFileTimeStamp(); - - /** - * Returns true if the plugin was running, but is now invalid. - */ - bool WasRunning(); - - Handle_t GetMyHandle(); - - bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func); - void AddConfig(bool autoCreate, const char *cfg, const char *folder); - unsigned int GetConfigCount(); - AutoConfig *GetConfig(unsigned int i); - inline void AddLibrary(const char *name) - { - m_Libraries.push_back(name); - } - void LibraryActions(bool dropping); - void SyncMaxClients(int max_clients); -protected: - bool UpdateInfo(); - void SetTimeStamp(time_t t); - void DependencyDropped(CPlugin *pOwner); -private: - PluginType m_type; - char m_filename[PLATFORM_MAX_PATH]; - PluginStatus m_status; - unsigned int m_serial; - sm_plugininfo_t m_info; - char m_errormsg[256]; - time_t m_LastAccess; - IdentityToken_t *m_ident; - Handle_t m_handle; - bool m_WasRunning; - IPhraseCollection *m_pPhrases; - List m_RequiredLibs; - List m_Libraries; - Trie *m_pProps; - bool m_FakeNativesMissing; - bool m_LibraryMissing; - CVector m_configs; - bool m_bGotAllLoaded; - int m_FileVersion; - char m_DateTime[256]; - IPluginRuntime *m_pRuntime; - IPluginContext *m_pContext; - sp_pubvar_t *m_MaxClientsVar; -}; - -class CPluginManager : - public IPluginManager, - public SMGlobalClass, - public IHandleTypeDispatch, - public IRootConsoleCommand -{ - friend class CPlugin; -public: - CPluginManager(); - ~CPluginManager(); -public: - /* Implements iterator class */ - class CPluginIterator : public IPluginIterator - { - public: - CPluginIterator(List *mylist); - virtual ~CPluginIterator(); - virtual bool MorePlugins(); - virtual IPlugin *GetPlugin(); - virtual void NextPlugin(); - void Release(); - public: - void Reset(); - private: - List *mylist; - List::iterator current; - }; - friend class CPluginManager::CPluginIterator; -public: //IPluginManager - IPlugin *LoadPlugin(const char *path, - bool debug, - PluginType type, - char error[], - size_t maxlength, - bool *wasloaded); - bool UnloadPlugin(IPlugin *plugin); - IPlugin *FindPluginByContext(const sp_context_t *ctx); - unsigned int GetPluginCount(); - IPluginIterator *GetPluginIterator(); - void AddPluginsListener(IPluginsListener *listener); - void RemovePluginsListener(IPluginsListener *listener); -public: //SMGlobalClass - void OnSourceModAllInitialized(); - void OnSourceModShutdown(); - void OnSourceModMaxPlayersChanged(int newvalue); -public: //IHandleTypeDispatch - void OnHandleDestroy(HandleType_t type, void *object); - bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); -public: //IRootConsoleCommand - void OnRootConsoleCommand(const char *cmdname, const CCommand &command); -public: - /** - * Loads all plugins not yet loaded - */ - void LoadAll_FirstPass(const char *config, const char *basedir); - - /** - * Runs the second loading pass for all plugins - */ - void LoadAll_SecondPass(); - - /** - * Tests a plugin file mask against a local folder. - * The alias is searched backwards from localdir - i.e., given this input: - * csdm/ban csdm/ban - * ban csdm/ban - * csdm/ban optional/csdm/ban - * All of these will return true for an alias match. - * Wildcards are allowed in the filename. - */ - bool TestAliasMatch(const char *alias, const char *localdir); - - /** - * Returns whether anything loaded will be a late load. - */ - bool IsLateLoadTime() const; - - /** - * Converts a Handle to an IPlugin if possible. - */ - IPlugin *PluginFromHandle(Handle_t handle, HandleError *err); - - /** - * Finds a plugin based on its index. (starts on index 1) - */ - CPlugin *GetPluginByOrder(int num); - - int GetOrderOfPlugin(IPlugin *pl); - - /** - * Internal version of FindPluginByContext() - */ - CPlugin *GetPluginByCtx(const sp_context_t *ctx); - - /** - * Gets status text for a status code - */ - const char *GetStatusText(PluginStatus status); - - /** - * Reload or update plugins on level shutdown. - */ - void ReloadOrUnloadPlugins(); - - /** - * Add public functions from all running or paused - * plugins to the specified forward if the names match. - */ - void AddFunctionsToForward(const char *name, IChangeableForward *pForward); - - /** - * Iterates through plugins to call OnAllPluginsLoaded. - */ - void AllPluginsLoaded(); - - CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken); - - void Shutdown(); - - void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop); - - bool LibraryExists(const char *lib); - - bool ReloadPlugin(CPlugin *pl); - - void UnloadAll(); - - CPlugin *FindPluginByConsoleArg(const char *arg); - - void SyncMaxClients(int max_clients); -private: - LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength); - - void LoadAutoPlugin(const char *plugin); - - /** - * Recursively loads all plugins in the given directory. - */ - void LoadPluginsFromDir(const char *basedir, const char *localdir); - - /** - * Adds a plugin object. This is wrapped by LoadPlugin functions. - */ - void AddPlugin(CPlugin *pPlugin); - - /** - * Runs the second loading pass on a plugin. - */ - bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength); - - /** - * Runs an extension pass on a plugin. - */ - bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength); - - /** - * Manages required natives. - */ - bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength); - - void _SetPauseState(CPlugin *pPlugin, bool pause); -protected: - /** - * Caching internal objects - */ - void ReleaseIterator(CPluginIterator *iter); -public: - inline IdentityToken_t *GetIdentity() - { - return m_MyIdent; - } -private: - void TryRefreshDependencies(CPlugin *pOther); -private: - List m_listeners; - List m_plugins; - CStack m_iters; - CPluginInfoDatabase m_PluginInfo; - Trie *m_LoadLookup; - bool m_AllPluginsLoaded; - IdentityToken_t *m_MyIdent; - - /* Dynamic native stuff */ - List m_Natives; - - bool m_LoadingLocked; -}; - -extern CPluginManager g_PluginSys; - -#endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/ShareSys.cpp --- a/core/systems/ShareSys.cpp Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,529 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#include "ShareSys.h" -#include "HandleSys.h" -#include "ExtensionSys.h" -#include "LibrarySys.h" -#include "PluginSys.h" -#include "sm_stringutil.h" - -ShareSystem g_ShareSys; -static unsigned int g_mark_serial = 0; - -ShareSystem::ShareSystem() -{ - m_IdentRoot.ident = 0; - m_TypeRoot = 0; - m_IfaceType = 0; - m_CoreType = 0; -} - -IdentityToken_t *ShareSystem::CreateCoreIdentity() -{ - if (!m_CoreType) - { - m_CoreType = CreateIdentType("CORE"); - } - - return CreateIdentity(m_CoreType, this); -} - -void ShareSystem::Initialize() -{ - TypeAccess sec; - - g_HandleSys.InitAccessDefaults(&sec, NULL); - sec.ident = GetIdentRoot(); - - m_TypeRoot = g_HandleSys.CreateType("Identity", this, 0, &sec, NULL, NULL, NULL); - m_IfaceType = g_HandleSys.CreateType("Interface", this, 0, NULL, NULL, GetIdentRoot(), NULL); - - /* Initialize our static identity handle */ - m_IdentRoot.ident = g_HandleSys.CreateHandle(m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL); - - /* Add the Handle System and others... they are too innocent and pure to do it themselves */ - AddInterface(NULL, &g_HandleSys); - AddInterface(NULL, &g_LibSys); -} - -void ShareSystem::OnSourceModShutdown() -{ - if (m_CoreType) - { - g_HandleSys.RemoveType(m_CoreType, GetIdentRoot()); - } - - g_HandleSys.RemoveType(m_IfaceType, GetIdentRoot()); - g_HandleSys.RemoveType(m_TypeRoot, GetIdentRoot()); -} - -IdentityType_t ShareSystem::FindIdentType(const char *name) -{ - HandleType_t type; - - if (g_HandleSys.FindHandleType(name, &type)) - { - if (g_HandleSys.TypeCheck(type, m_TypeRoot)) - { - return type; - } - } - - return 0; -} - -IdentityType_t ShareSystem::CreateIdentType(const char *name) -{ - if (!m_TypeRoot) - { - return 0; - } - - return g_HandleSys.CreateType(name, this, m_TypeRoot, NULL, NULL, GetIdentRoot(), NULL); -} - -void ShareSystem::OnHandleDestroy(HandleType_t type, void *object) -{ - /* THIS WILL NEVER BE CALLED FOR ANYTHING WITH THE IDENTITY TYPE */ -} - -IdentityToken_t *ShareSystem::CreateIdentity(IdentityType_t type, void *ptr) -{ - if (!m_TypeRoot) - { - return 0; - } - - /* :TODO: Cache? */ - IdentityToken_t *pToken = new IdentityToken_t; - - HandleSecurity sec; - sec.pOwner = sec.pIdentity = GetIdentRoot(); - - pToken->ident = g_HandleSys.CreateHandleInt(type, NULL, &sec, NULL, NULL, true); - pToken->ptr = ptr; - pToken->type = type; - - return pToken; -} - -bool ShareSystem::AddInterface(IExtension *myself, SMInterface *iface) -{ - if (!iface) - { - return false; - } - - IfaceInfo info; - - info.owner = myself; - info.iface = iface; - - m_Interfaces.push_back(info); - - return true; -} - -bool ShareSystem::RequestInterface(const char *iface_name, - unsigned int iface_vers, - IExtension *myself, - SMInterface **pIface) -{ - /* See if the interface exists */ - List::iterator iter; - SMInterface *iface; - IExtension *iface_owner; - bool found = false; - for (iter=m_Interfaces.begin(); iter!=m_Interfaces.end(); iter++) - { - IfaceInfo &info = (*iter); - iface = info.iface; - if (strcmp(iface->GetInterfaceName(), iface_name) == 0) - { - if (iface->GetInterfaceVersion() == iface_vers - || iface->IsVersionCompatible(iface_vers)) - { - iface_owner = info.owner; - found = true; - break; - } - } - } - - if (!found) - { - return false; - } - - /* Add a dependency node */ - if (iface_owner) - { - IfaceInfo info; - info.iface = iface; - info.owner = iface_owner; - g_Extensions.BindDependency(myself, &info); - } - - if (pIface) - { - *pIface = iface; - } - - return true; -} - -void ShareSystem::AddNatives(IExtension *myself, const sp_nativeinfo_t *natives) -{ - CNativeOwner *pOwner; - - pOwner = g_Extensions.GetNativeOwner(myself); - - pOwner->AddNatives(natives); -} - -void ShareSystem::DestroyIdentity(IdentityToken_t *identity) -{ - HandleSecurity sec; - - sec.pOwner = GetIdentRoot(); - sec.pIdentity = GetIdentRoot(); - - g_HandleSys.FreeHandle(identity->ident, &sec); - delete identity; -} - -void ShareSystem::DestroyIdentType(IdentityType_t type) -{ - g_HandleSys.RemoveType(type, GetIdentRoot()); -} - -void ShareSystem::RemoveInterfaces(IExtension *pExtension) -{ - List::iterator iter = m_Interfaces.begin(); - - while (iter != m_Interfaces.end()) - { - if ((*iter).owner == pExtension) - { - iter = m_Interfaces.erase(iter); - } - else - { - iter++; - } - } -} - -void ShareSystem::AddDependency(IExtension *myself, const char *filename, bool require, bool autoload) -{ - g_Extensions.AddDependency(myself, filename, require, autoload); -} - -void ShareSystem::RegisterLibrary(IExtension *myself, const char *name) -{ - g_Extensions.AddLibrary(myself, name); -} - -void ShareSystem::OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives) -{ - unsigned int i; - NativeEntry *pEntry; - CNativeOwner *pOwner; - - pOwner = g_Extensions.GetNativeOwner(myself); - - for (i = 0; natives[i].func != NULL && natives[i].name != NULL; i++) - { - if ((pEntry = FindNative(natives[i].name)) == NULL) - { - continue; - } - - if (pEntry->owner != g_pCoreNatives) - { - continue; - } - - if (pEntry->replacement.owner != NULL) - { - continue; - } - - /* Now it's safe to add the override */ - pEntry->replacement.func = natives[i].func; - pEntry->replacement.owner = pOwner; - pOwner->AddReplacedNative(pEntry); - } -} - -NativeEntry *ShareSystem::FindNative(const char *name) -{ - NativeEntry **ppEntry; - - if ((ppEntry = m_NtvCache.retrieve(name)) == NULL) - { - return NULL; - } - - return *ppEntry; -} - -void ShareSystem::BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly) -{ - NativeEntry *pEntry; - sp_native_t *native; - uint32_t i, native_count; - IPluginContext *pContext; - - pContext = pPlugin->GetBaseContext(); - - /* Generate a new serial ID, mark our dependencies with it. */ - g_mark_serial++; - pPlugin->PropogateMarkSerial(g_mark_serial); - - native_count = pContext->GetNativesNum(); - for (i = 0; i < native_count; i++) - { - if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE) - { - continue; - } - - /* If we're bound, check if there is a replacement available. - * If not, this native is totally finalized. - */ - if (native->status == SP_NATIVE_BOUND) - { - pEntry = (NativeEntry *)native->user; - assert(pEntry != NULL); - if (pEntry->replacement.owner == NULL - || (pEntry->replacement.owner != NULL - && pEntry->replacement.func == native->pfn)) - { - continue; - } - } - /* Otherwise, the native must be in our cache. */ - else if ((pEntry = FindNative(native->name)) == NULL) - { - continue; - } - - if (bCoreOnly && pEntry->owner != g_pCoreNatives) - { - continue; - } - - BindNativeToPlugin(pPlugin, native, i, pEntry); - } -} - -void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, NativeEntry *pEntry) -{ - uint32_t i; - sp_native_t *native; - IPluginContext *pContext; - - pContext = pPlugin->GetBaseContext(); - - if (pContext->FindNativeByName(pEntry->name, &i) != SP_ERROR_NONE) - { - return; - } - if (pContext->GetNativeByIndex(i, &native) != SP_ERROR_NONE) - { - return; - } - - if (native->status == SP_NATIVE_BOUND) - { - return; - } - - BindNativeToPlugin(pPlugin, native, i, pEntry); -} - -void ShareSystem::BindNativeToPlugin(CPlugin *pPlugin, - sp_native_t *native, - uint32_t index, - NativeEntry *pEntry) -{ - /* Mark as bound... we do the rest next. */ - native->status = SP_NATIVE_BOUND; - native->user = pEntry; - - /* See if a replacement is available. */ - if (pEntry->replacement.owner != NULL) - { - /* Perform a replacement bind. */ - native->pfn = pEntry->replacement.func; - pEntry->replacement.owner->AddWeakRef(WeakNative(pPlugin, index, pEntry)); - } - else - { - /* Perform a normal bind. */ - native->pfn = pEntry->func; - - /* We don't bother with dependency crap if the owner is Core. */ - if (pEntry->owner != g_pCoreNatives) - { - /* The native is optional, this is a special case */ - if ((native->flags & SP_NTVFLAG_OPTIONAL) == SP_NTVFLAG_OPTIONAL) - { - /* Only add if there is a valid owner. */ - if (pEntry->owner != NULL) - { - pEntry->owner->AddWeakRef(WeakNative(pPlugin, index)); - } - else - { - native->status = SP_NATIVE_UNBOUND; - } - } - /* Otherwise, we're a strong dependent and not a weak one */ - else - { - /* See if this has already been marked as a dependent. - * If it has, it means this relationship has already occurred, - * and there is no reason to do it again. - */ - if (pEntry->owner != pPlugin - && pEntry->owner->GetMarkSerial() != g_mark_serial) - { - /* This has not been marked as a dependency yet */ - //pPlugin->AddDependency(pEntry->owner); - pEntry->owner->AddDependent(pPlugin); - pEntry->owner->SetMarkSerial(g_mark_serial); - } - } - } - } -} - -NativeEntry *ShareSystem::AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv) -{ - NativeEntry *pEntry; - - if ((pEntry = FindNative(ntv->name)) == NULL) - { - pEntry = new NativeEntry; - - pEntry->owner = pOwner; - pEntry->name = ntv->name; - pEntry->func = ntv->func; - pEntry->replacement.func = NULL; - pEntry->replacement.owner = NULL; - pEntry->fake = NULL; - - m_NtvCache.insert(ntv->name, pEntry); - - return pEntry; - } - - if (pEntry->owner != NULL) - { - return NULL; - } - - pEntry->owner = pOwner; - pEntry->func = ntv->func; - pEntry->name = ntv->name; - - return pEntry; -} - -void ShareSystem::ClearNativeFromCache(CNativeOwner *pOwner, const char *name) -{ - NativeEntry *pEntry; - - if ((pEntry = FindNative(name)) == NULL) - { - return; - } - - if (pEntry->owner != pOwner) - { - return; - } - - if (pEntry->fake != NULL) - { - g_pSourcePawn2->DestroyFakeNative(pEntry->func); - delete pEntry->fake; - pEntry->fake = NULL; - } - - pEntry->func = NULL; - pEntry->name = NULL; - pEntry->owner = NULL; - pEntry->replacement.func = NULL; - pEntry->replacement.owner = NULL; -} - -NativeEntry *ShareSystem::AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func) -{ - FakeNative *pFake; - NativeEntry *pEntry; - SPVM_NATIVE_FUNC gate; - - if ((pEntry = FindNative(name)) != NULL && pEntry->owner != NULL) - { - return NULL; - } - - pFake = new FakeNative; - - if ((gate = g_pSourcePawn2->CreateFakeNative(func, pFake)) == NULL) - { - delete pFake; - return NULL; - } - - if (pEntry == NULL) - { - pEntry = new NativeEntry; - m_NtvCache.insert(name, pEntry); - } - - pFake->call = pFunc; - pFake->ctx = pFunc->GetParentContext(); - strncopy(pFake->name, name, sizeof(pFake->name)); - - pEntry->fake = pFake; - pEntry->func = gate; - pEntry->name = pFake->name; - pEntry->owner = g_PluginSys.GetPluginByCtx(pFake->ctx->GetContext()); - pEntry->replacement.func = NULL; - pEntry->replacement.owner = NULL; - - return pEntry; -} diff -r 9981b8ecf935 -r 31f49899c0ae core/systems/ShareSys.h --- a/core/systems/ShareSys.h Mon Nov 24 08:25:36 2008 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. - * ============================================================================= - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, version 3.0, as published by the - * Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - * - * As a special exception, AlliedModders LLC gives you permission to link the - * code of this program (as well as its derivative works) to "Half-Life 2," the - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software - * by the Valve Corporation. You must obey the GNU General Public License in - * all respects for all other code used. Additionally, AlliedModders LLC grants - * this exception to all derivative works. AlliedModders LLC defines further - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), - * or . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_SHARESYSTEM_H_ -#define _INCLUDE_SOURCEMOD_SHARESYSTEM_H_ - -#include -#include -#include -#include -#include "sm_globals.h" -#include "sourcemod.h" - -using namespace SourceHook; - -namespace SourceMod -{ - struct IdentityToken_t - { - Handle_t ident; - void *ptr; - IdentityType_t type; - }; -}; - -struct IfaceInfo -{ - bool operator ==(const IfaceInfo &info) - { - return (info.iface == iface && info.owner == owner); - } - SMInterface *iface; - IExtension *owner; -}; - -class CNativeOwner; -class CPlugin; -struct NativeEntry; - -struct ReplaceNative -{ - CNativeOwner *owner; - SPVM_NATIVE_FUNC func; -}; - -struct FakeNative -{ - char name[64]; - IPluginContext *ctx; - IPluginFunction *call; -}; - -struct NativeEntry -{ - CNativeOwner *owner; - SPVM_NATIVE_FUNC func; - const char *name; - ReplaceNative replacement; - FakeNative *fake; -}; - -class ShareSystem : - public IShareSys, - public SMGlobalClass, - public IHandleTypeDispatch -{ - friend class CNativeOwner; -public: - ShareSystem(); -public: //IShareSys - bool AddInterface(IExtension *myself, SMInterface *pIface); - bool RequestInterface(const char *iface_name, - unsigned int iface_vers, - IExtension *myself, - SMInterface **pIface); - void AddNatives(IExtension *myself, const sp_nativeinfo_t *natives); - IdentityType_t CreateIdentType(const char *name); - IdentityType_t FindIdentType(const char *name); - IdentityToken_t *CreateIdentity(IdentityType_t type, void *ptr); - void DestroyIdentType(IdentityType_t type); - void DestroyIdentity(IdentityToken_t *identity); - void AddDependency(IExtension *myself, const char *filename, bool require, bool autoload); - void RegisterLibrary(IExtension *myself, const char *name); - void OverrideNatives(IExtension *myself, const sp_nativeinfo_t *natives); -public: //SMGlobalClass - /* Pre-empt in case anything tries to register idents early */ - void Initialize(); - void OnSourceModShutdown(); -public: //IHandleTypeDispatch - void OnHandleDestroy(HandleType_t type, void *object); -public: - IdentityToken_t *CreateCoreIdentity(); - void RemoveInterfaces(IExtension *pExtension); -public: - inline IdentityToken_t *GetIdentRoot() - { - return &m_IdentRoot; - } -public: - void BindNativesToPlugin(CPlugin *pPlugin, bool bCoreOnly); - void BindNativeToPlugin(CPlugin *pPlugin, NativeEntry *pEntry); - NativeEntry *AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func); -private: - NativeEntry *AddNativeToCache(CNativeOwner *pOwner, const sp_nativeinfo_t *ntv); - void ClearNativeFromCache(CNativeOwner *pOwner, const char *name); - NativeEntry *FindNative(const char *name); - void BindNativeToPlugin(CPlugin *pPlugin, - sp_native_t *ntv, - uint32_t index, - NativeEntry *pEntry); -private: - List m_Interfaces; - HandleType_t m_TypeRoot; - IdentityToken_t m_IdentRoot; - HandleType_t m_IfaceType; - IdentityType_t m_CoreType; - KTrie m_NtvCache; -}; - -extern ShareSystem g_ShareSys; - -#endif //_INCLUDE_SOURCEMOD_SHARESYSTEM_H_