2008-11-14 11:04:02 +01:00
|
|
|
/**
|
2009-10-29 22:23:25 +01:00
|
|
|
* vim: set ts=4 sw=4 tw=99 noet :
|
2008-11-14 11:04:02 +01:00
|
|
|
* ======================================================
|
|
|
|
* Metamod:Source
|
2009-10-29 22:23:25 +01:00
|
|
|
* Copyright (C) 2004-2009 AlliedModders LLC and authors.
|
2008-11-14 11:04:02 +01:00
|
|
|
* All rights reserved.
|
|
|
|
* ======================================================
|
|
|
|
*
|
|
|
|
* This software is provided 'as-is', without any express or implied warranty.
|
|
|
|
* In no event will the authors be held liable for any damages arising from
|
|
|
|
* the use of this software.
|
|
|
|
*
|
|
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
|
|
* including commercial applications, and to alter it and redistribute it
|
|
|
|
* freely, subject to the following restrictions:
|
|
|
|
*
|
|
|
|
* 1. The origin of this software must not be misrepresented; you must not
|
|
|
|
* claim that you wrote the original software. If you use this software in a
|
|
|
|
* product, an acknowledgment in the product documentation would be
|
|
|
|
* appreciated but is not required.
|
|
|
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
* misrepresented as being the original software.
|
|
|
|
* 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2023-02-16 07:13:42 +01:00
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
#include <amtl/am-string.h>
|
2008-11-14 11:04:02 +01:00
|
|
|
#include "metamod_oslink.h"
|
|
|
|
#include "metamod.h"
|
|
|
|
#include "metamod_plugins.h"
|
|
|
|
#include "metamod_util.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Implements functions from CPlugin.h
|
|
|
|
* @file CPlugin.cpp
|
|
|
|
*/
|
|
|
|
|
|
|
|
using namespace SourceMM;
|
|
|
|
|
|
|
|
#define ITER_PLEVENT(evn, plid) \
|
|
|
|
CPluginManager::CPlugin *_Xpl; \
|
|
|
|
SourceHook::List<IMetamodListener *>::iterator event; \
|
|
|
|
IMetamodListener *api; \
|
|
|
|
for (PluginIter iter = g_PluginMngr._begin(); iter != g_PluginMngr._end(); iter++) { \
|
|
|
|
_Xpl = (*iter); \
|
|
|
|
if (_Xpl->m_Id == plid) \
|
|
|
|
continue; \
|
|
|
|
for (event=_Xpl->m_Events.begin(); event!=_Xpl->m_Events.end(); event++) { \
|
|
|
|
api = (*event); \
|
|
|
|
api->evn(plid); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager g_PluginMngr;
|
|
|
|
|
|
|
|
MetamodVersionInfo GlobVersionInfo =
|
|
|
|
{
|
|
|
|
METAMOD_API_MAJOR,
|
|
|
|
METAMOD_API_MINOR,
|
|
|
|
SH_IFACE_VERSION,
|
|
|
|
SH_IMPL_VERSION,
|
|
|
|
PLAPI_MIN_VERSION,
|
|
|
|
METAMOD_PLAPI_VERSION,
|
2009-10-29 22:23:25 +01:00
|
|
|
SOURCE_ENGINE_UNKNOWN,
|
|
|
|
NULL
|
2008-11-14 11:04:02 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
CPluginManager::CPluginManager()
|
|
|
|
{
|
|
|
|
m_LastId = Pl_MinId;
|
|
|
|
m_AllLoaded = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager::~CPluginManager()
|
|
|
|
{
|
|
|
|
SourceHook::List<CNameAlias *>::iterator iter;
|
|
|
|
|
|
|
|
for (iter = m_Aliases.begin(); iter != m_Aliases.end(); iter++)
|
|
|
|
{
|
|
|
|
delete (*iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_Aliases.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *CPluginManager::LookupAlias(const char *alias)
|
|
|
|
{
|
|
|
|
SourceHook::List<CNameAlias *>::iterator iter;
|
|
|
|
CNameAlias *p;
|
|
|
|
|
|
|
|
for (iter = m_Aliases.begin(); iter != m_Aliases.end(); iter++)
|
|
|
|
{
|
|
|
|
p = (*iter);
|
|
|
|
if (p->alias.compare(alias) == 0)
|
|
|
|
{
|
|
|
|
return p->value.c_str();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceHook::List<CNameAlias *>::iterator CPluginManager::_alias_begin()
|
|
|
|
{
|
|
|
|
return m_Aliases.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceHook::List<CNameAlias *>::iterator CPluginManager::_alias_end()
|
|
|
|
{
|
|
|
|
return m_Aliases.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::SetAlias(const char *alias, const char *value)
|
|
|
|
{
|
|
|
|
SourceHook::List<CNameAlias *>::iterator iter;
|
|
|
|
CNameAlias *p;
|
|
|
|
|
|
|
|
for (iter = m_Aliases.begin(); iter != m_Aliases.end(); iter++)
|
|
|
|
{
|
|
|
|
p = (*iter);
|
|
|
|
if (p->alias.compare(alias) == 0)
|
|
|
|
{
|
|
|
|
if (value[0] == '\0')
|
|
|
|
{
|
|
|
|
iter = m_Aliases.erase(iter);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p->value.assign(value);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value[0] != '\0')
|
|
|
|
{
|
|
|
|
p = new CNameAlias;
|
|
|
|
|
|
|
|
p->alias.assign(alias);
|
|
|
|
p->value.assign(value);
|
|
|
|
|
|
|
|
m_Aliases.push_back(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager::CPlugin::CPlugin() : m_Id(0), m_Source(0), m_API(NULL), m_Lib(NULL), m_UnloadFn(NULL)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginId CPluginManager::Load(const char *file, PluginId source, bool &already, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
already = false;
|
|
|
|
//Check if we're about to reload an old plugin
|
|
|
|
PluginIter i = m_Plugins.begin();
|
|
|
|
while (i != m_Plugins.end())
|
|
|
|
{
|
|
|
|
if ((*i) && UTIL_PathCmp(file, (*i)->m_File.c_str()))
|
|
|
|
{
|
|
|
|
if ((*i)->m_Status < Pl_Paused)
|
|
|
|
{
|
|
|
|
//Attempt to load the plugin again
|
|
|
|
already = true;
|
|
|
|
i = m_Plugins.erase(i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//No need to load it
|
|
|
|
already = true;
|
|
|
|
return (*i)->m_Id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPlugin *pl = _Load(file, source, error, maxlen);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return Pl_BadLoad;
|
|
|
|
}
|
|
|
|
|
|
|
|
ITER_PLEVENT(OnPluginLoad, pl->m_Id);
|
|
|
|
|
|
|
|
return pl->m_Id;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager::CPlugin *CPluginManager::FindById(PluginId id)
|
|
|
|
{
|
|
|
|
PluginIter i;
|
|
|
|
|
|
|
|
for (i=m_Plugins.begin(); i!=m_Plugins.end(); i++)
|
|
|
|
{
|
|
|
|
if ( (*i)->m_Id == id )\
|
|
|
|
{
|
|
|
|
return (*i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::SetAllLoaded()
|
|
|
|
{
|
|
|
|
if (m_AllLoaded)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_AllLoaded = true;
|
|
|
|
PluginIter i;
|
|
|
|
|
|
|
|
for (i=m_Plugins.begin(); i!=m_Plugins.end(); i++)
|
|
|
|
{
|
|
|
|
if ( (*i) && (*i)->m_Status == Pl_Running && (*i)->m_API )
|
|
|
|
{
|
|
|
|
//API 4 is when we added this callback
|
|
|
|
//Min version is now 5, so we ignore this check
|
|
|
|
//if ( (*i)->m_API->GetApiVersion() >= 004 )
|
|
|
|
(*i)->m_API->AllPluginsLoaded();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::Pause(PluginId id, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindById(id);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin id not found");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
if ((ret=_Pause(pl, error, maxlen)) == true)
|
|
|
|
{
|
|
|
|
ITER_PLEVENT(OnPluginPause, pl->m_Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::Unpause(PluginId id, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindById(id);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin id not found");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
if ( (ret=_Unpause(pl, error, maxlen)) == true )
|
|
|
|
{
|
|
|
|
ITER_PLEVENT(OnPluginUnpause, pl->m_Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::Unload(PluginId id, bool force, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindById(id);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin %d not found", id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret;
|
|
|
|
PluginId old_id = pl->m_Id;
|
|
|
|
if ( (ret=_Unload(pl, force, error, maxlen)) == true )
|
|
|
|
{
|
|
|
|
ITER_PLEVENT(OnPluginUnload, old_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::Retry(PluginId id, char *error, size_t len)
|
|
|
|
{
|
|
|
|
PluginIter i;
|
|
|
|
char buffer[64];
|
|
|
|
for (i=m_Plugins.begin(); i!=m_Plugins.end(); i++)
|
|
|
|
{
|
|
|
|
if ( (*i) && (*i)->m_Id == id )
|
|
|
|
{
|
|
|
|
if ( (*i)->m_Status >= Pl_Paused)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, len, "Plugin %d is already running.", id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CPlugin *pl = _Load((*i)->m_File.c_str(), Pl_Console, error, len);
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (pl->m_Status >= Pl_Paused)
|
|
|
|
{
|
|
|
|
//Now it gets crazy... unload the original copy.
|
|
|
|
_Unload( (*i), true, buffer, sizeof(buffer)-1 );
|
|
|
|
|
|
|
|
//Set the new copy's id
|
|
|
|
pl->m_Id = id;
|
|
|
|
|
|
|
|
//We just wasted an id... reclaim it
|
|
|
|
m_LastId--;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//don't really care about the buffer here
|
|
|
|
_Unload(pl, true, buffer, sizeof(buffer)-1);
|
|
|
|
|
|
|
|
//We just wasted an id... reclaim it
|
|
|
|
m_LastId--;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UTIL_Format(error, len, "Plugin %d not found,", id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
CPluginManager::CPlugin *CPluginManager::FindByAPI(ISmmPlugin *api)
|
|
|
|
{
|
|
|
|
PluginIter i;
|
|
|
|
|
|
|
|
//don't find bad plugins!
|
|
|
|
if (!api)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=m_Plugins.begin(); i!=m_Plugins.end(); i++)
|
|
|
|
{
|
|
|
|
if ((*i)->m_API == api)
|
|
|
|
{
|
|
|
|
return (*i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CPluginManager::GetPluginCount()
|
|
|
|
{
|
|
|
|
return (int)m_Plugins.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *CPluginManager::GetStatusText(CPlugin *pl)
|
|
|
|
{
|
|
|
|
switch (pl->m_Status)
|
|
|
|
{
|
|
|
|
case Pl_NotFound:
|
|
|
|
return "NOFILE";
|
|
|
|
case Pl_Error:
|
|
|
|
return "ERROR";
|
|
|
|
case Pl_Refused:
|
|
|
|
return "FAILED";
|
|
|
|
case Pl_Paused:
|
|
|
|
return "PAUSED";
|
|
|
|
case Pl_Running:
|
|
|
|
{
|
|
|
|
if (pl->m_API && pl->m_API->QueryRunning(NULL, 0))
|
|
|
|
{
|
|
|
|
return "STOPPED";
|
|
|
|
} else {
|
|
|
|
return "RUNNING";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return "-";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-12 22:53:56 +01:00
|
|
|
struct Unloader : public SourceHook::Impl::UnloadListener
|
|
|
|
{
|
|
|
|
CPluginManager::CPlugin *plugin_;
|
|
|
|
bool destroy_;
|
|
|
|
|
|
|
|
Unloader(CPluginManager::CPlugin *plugin, bool destroy)
|
|
|
|
: plugin_(plugin), destroy_(destroy)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
void ReadyToUnload(SourceHook::Plugin plug)
|
|
|
|
{
|
|
|
|
if (plugin_->m_UnloadFn != NULL)
|
|
|
|
plugin_->m_UnloadFn();
|
|
|
|
|
|
|
|
dlclose(plugin_->m_Lib);
|
|
|
|
|
|
|
|
if (destroy_)
|
|
|
|
{
|
|
|
|
delete plugin_;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
plugin_->m_Lib = NULL;
|
|
|
|
plugin_->m_API = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2008-11-14 11:04:02 +01:00
|
|
|
CPluginManager::CPlugin *CPluginManager::_Load(const char *file, PluginId source, char *error, size_t maxlen)
|
|
|
|
{
|
2023-02-16 07:13:42 +01:00
|
|
|
std::unique_ptr<FILE, decltype(&::fclose)> fp(nullptr, ::fclose);
|
2008-11-14 11:04:02 +01:00
|
|
|
CPlugin *pl;
|
|
|
|
|
|
|
|
pl = new CPlugin();
|
|
|
|
*error = '\0';
|
|
|
|
|
|
|
|
//Add plugin to list
|
|
|
|
pl->m_Id = m_LastId;
|
|
|
|
pl->m_File.assign(file);
|
|
|
|
m_Plugins.push_back(pl);
|
|
|
|
m_LastId++;
|
|
|
|
|
2023-02-16 07:13:42 +01:00
|
|
|
if (ke::EndsWith(file, BINARY_EXT))
|
2008-11-14 11:04:02 +01:00
|
|
|
{
|
2023-02-16 07:13:42 +01:00
|
|
|
//Check if the file even exists
|
|
|
|
fp.reset(fopen(file, "r"));
|
|
|
|
if (!fp)
|
2008-11-14 11:04:02 +01:00
|
|
|
{
|
2023-02-16 07:13:42 +01:00
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "File not found: %s", file);
|
|
|
|
}
|
|
|
|
pl->m_Status = Pl_NotFound;
|
2008-11-14 11:04:02 +01:00
|
|
|
}
|
2023-02-16 07:13:42 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
UTIL_Format(error, maxlen, "File type not supported");
|
2008-11-14 11:04:02 +01:00
|
|
|
pl->m_Status = Pl_NotFound;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fp)
|
|
|
|
{
|
2023-02-16 07:13:42 +01:00
|
|
|
fp.reset();
|
2008-11-14 11:04:02 +01:00
|
|
|
|
|
|
|
//Load the file
|
|
|
|
pl->m_Lib = dlmount(file);
|
|
|
|
if (!pl->m_Lib)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "%s", dlerror());
|
|
|
|
}
|
|
|
|
pl->m_Status = Pl_Error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pl->m_API = NULL;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* First, try the new "advanced" interface
|
|
|
|
*/
|
|
|
|
METAMOD_FN_LOAD fnLoad = (METAMOD_FN_LOAD)dlsym(pl->m_Lib, "CreateInterface_MMS");
|
|
|
|
if (fnLoad != NULL)
|
|
|
|
{
|
|
|
|
if (GlobVersionInfo.source_engine == SOURCE_ENGINE_UNKNOWN)
|
|
|
|
{
|
|
|
|
GlobVersionInfo.source_engine = g_Metamod.GetSourceEngineBuild();
|
|
|
|
}
|
2009-10-29 22:23:25 +01:00
|
|
|
if (GlobVersionInfo.game_dir == NULL)
|
|
|
|
{
|
|
|
|
GlobVersionInfo.game_dir = strrchr(g_Metamod.GetBaseDir(), PATH_SEP_CHAR) + 1;
|
|
|
|
}
|
2008-11-14 11:04:02 +01:00
|
|
|
|
|
|
|
/* Build path information */
|
|
|
|
char file_path[256];
|
|
|
|
size_t len = g_Metamod.PathFormat(file_path, sizeof(file_path), "%s", file);
|
|
|
|
|
2011-04-24 04:35:10 +02:00
|
|
|
for (size_t i = len - 1; i < len; i--)
|
2008-11-14 11:04:02 +01:00
|
|
|
{
|
|
|
|
if (_IsPathSepChar(file_path[i]))
|
|
|
|
{
|
|
|
|
file_path[i] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MetamodLoaderInfo info;
|
|
|
|
info.pl_file = file;
|
|
|
|
info.pl_path = file_path;
|
|
|
|
|
|
|
|
pl->m_API = fnLoad(&GlobVersionInfo, &info);
|
2013-07-31 12:49:21 +02:00
|
|
|
#if SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_TF2
|
2009-10-29 22:23:25 +01:00
|
|
|
/* For plugin compat - try loading again using original OB if OB-Valve has failed */
|
|
|
|
if (pl->m_API == NULL)
|
|
|
|
{
|
2013-07-31 12:49:21 +02:00
|
|
|
GlobVersionInfo.source_engine = SOURCE_ENGINE_ORANGEBOXVALVE_DEPRECATED;
|
2009-10-29 22:23:25 +01:00
|
|
|
pl->m_API = fnLoad(&GlobVersionInfo, &info);
|
2013-07-31 12:49:21 +02:00
|
|
|
if (pl->m_API == NULL)
|
|
|
|
{
|
|
|
|
GlobVersionInfo.source_engine = SOURCE_ENGINE_ORANGEBOX;
|
|
|
|
pl->m_API = fnLoad(&GlobVersionInfo, &info);
|
|
|
|
}
|
|
|
|
GlobVersionInfo.source_engine = g_Metamod.GetSourceEngineBuild();
|
2009-10-29 22:23:25 +01:00
|
|
|
}
|
|
|
|
#endif
|
2008-11-14 11:04:02 +01:00
|
|
|
pl->m_UnloadFn = (METAMOD_FN_UNLOAD)dlsym(pl->m_Lib, "UnloadInterface_MMS");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If we didn't get anything, try the normal/simple interface.
|
|
|
|
*/
|
|
|
|
if (pl->m_API == NULL)
|
|
|
|
{
|
|
|
|
CreateInterfaceFn pfn = (CreateInterfaceFn)(dlsym(pl->m_Lib, PL_EXPOSURE_C));
|
|
|
|
if (!pfn)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Function %s not found", PL_EXPOSURE_C);
|
|
|
|
}
|
|
|
|
pl->m_Status = Pl_Error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pl->m_API = static_cast<ISmmPlugin *>((pfn)(METAMOD_PLAPI_NAME, NULL));
|
|
|
|
|
|
|
|
if (!pl->m_API)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Failed to get API");
|
|
|
|
}
|
|
|
|
pl->m_Status = Pl_Error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl->m_API != NULL)
|
|
|
|
{
|
|
|
|
int api = pl->m_API->GetApiVersion();
|
|
|
|
if (api < PLAPI_MIN_VERSION)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
if (api == 13)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin uses experimental Metamod build, probably 1.6.x (%d < %d)", api, PLAPI_MIN_VERSION);
|
|
|
|
}
|
|
|
|
else if (api <= 12 && api >= 7)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Older Metamod version required, probably 1.4.x (%d < %d)", api, PLAPI_MIN_VERSION);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Older Metamod version required, probably 1.0 (%d < %d)", api, PLAPI_MIN_VERSION);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pl->m_Status = Pl_Error;
|
|
|
|
}
|
|
|
|
else if (api > METAMOD_PLAPI_VERSION)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin requires newer Metamod version (%d > %d)", api, METAMOD_PLAPI_VERSION);
|
|
|
|
}
|
|
|
|
pl->m_Status = Pl_Error;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pl->m_API->Load(pl->m_Id, &g_Metamod, error, maxlen, m_AllLoaded))
|
|
|
|
{
|
|
|
|
pl->m_Status = Pl_Running;
|
|
|
|
if (m_AllLoaded)
|
|
|
|
{
|
|
|
|
pl->m_API->AllPluginsLoaded();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pl->m_Status = Pl_Refused;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl->m_Lib && (pl->m_Status < Pl_Paused))
|
|
|
|
{
|
|
|
|
pl->m_Events.clear();
|
|
|
|
UnregAllConCmds(pl);
|
2011-12-12 22:53:56 +01:00
|
|
|
g_SourceHook.UnloadPlugin(pl->m_Id, new Unloader(pl, false));
|
2008-11-14 11:04:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return pl;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::_Unload(CPluginManager::CPlugin *pl, bool force, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
*error = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl->m_API && pl->m_Lib)
|
|
|
|
{
|
|
|
|
//Note, we'll always tell the plugin it will be unloading...
|
|
|
|
if (pl->m_API->Unload(error, maxlen) || force)
|
|
|
|
{
|
|
|
|
pl->m_Events.clear();
|
|
|
|
UnregAllConCmds(pl);
|
|
|
|
|
|
|
|
//Remove the plugin from the list
|
|
|
|
PluginIter i;
|
|
|
|
for (i=m_Plugins.begin(); i!=m_Plugins.end(); i++)
|
|
|
|
{
|
|
|
|
if ( (*i)->m_Id == pl->m_Id )
|
|
|
|
{
|
|
|
|
i = m_Plugins.erase(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-12 22:53:56 +01:00
|
|
|
//Make sure to detach it from sourcehook!
|
|
|
|
g_SourceHook.UnloadPlugin(pl->m_Id, new Unloader(pl, true));
|
2008-11-14 11:04:02 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//The plugin is not valid, and let's just remove it from the list anyway
|
|
|
|
PluginIter i;
|
|
|
|
for (i=m_Plugins.begin(); i!=m_Plugins.end(); i++)
|
|
|
|
{
|
|
|
|
if ( (*i)->m_Id == pl->m_Id )
|
|
|
|
{
|
|
|
|
i = m_Plugins.erase(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete pl;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::_Pause(CPluginManager::CPlugin *pl, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
*error = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl->m_Status != Pl_Running || !pl->m_API)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin cannot be paused");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pl->m_API->Pause(error, maxlen))
|
|
|
|
{
|
|
|
|
g_SourceHook.PausePlugin(pl->m_Id);
|
|
|
|
pl->m_Status = Pl_Paused;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::_Unpause(CPluginManager::CPlugin *pl, char *error, size_t maxlen)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
*error = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pl->m_Status != Pl_Paused || !pl->m_API)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlen, "Plugin cannot be unpaused");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pl->m_API->Unpause(error, maxlen))
|
|
|
|
{
|
|
|
|
g_SourceHook.UnpausePlugin(pl->m_Id);
|
|
|
|
pl->m_Status = Pl_Running;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::UnloadAll()
|
|
|
|
{
|
|
|
|
PluginIter i;
|
|
|
|
|
|
|
|
char error[128];
|
|
|
|
bool status = true;
|
|
|
|
|
|
|
|
while ((i = m_Plugins.begin()) != m_Plugins.end())
|
|
|
|
{
|
|
|
|
if ( !_Unload( (*i), true, error, sizeof(error)) )
|
|
|
|
{
|
|
|
|
status = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::Query(PluginId id, const char **file, Pl_Status *status, PluginId *source)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindById(id);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file != NULL)
|
|
|
|
{
|
|
|
|
*file = pl->m_File.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status != NULL)
|
|
|
|
{
|
|
|
|
*status = pl->m_Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (source != NULL)
|
|
|
|
{
|
|
|
|
*source = pl->m_Source;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::QueryRunning(PluginId id, char *error, size_t maxlength)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindById(id);
|
|
|
|
|
|
|
|
if (!pl || !pl->m_API)
|
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
UTIL_Format(error, maxlength, "Plugin not valid");
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pl->m_API->QueryRunning(error, maxlength);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CPluginManager::QueryHandle(PluginId id, void **handle)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindById(id);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handle)
|
|
|
|
{
|
|
|
|
*handle = static_cast<void *>(pl->m_Lib);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginIter CPluginManager::_begin()
|
|
|
|
{
|
|
|
|
return m_Plugins.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
PluginIter CPluginManager::_end()
|
|
|
|
{
|
|
|
|
return m_Plugins.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::AddPluginCvar(ISmmPlugin *api, ConCommandBase *pCvar)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindByAPI(api);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pl->m_Cvars.push_back(pCvar);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::AddPluginCmd(ISmmPlugin *api, ConCommandBase *pCmd)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindByAPI(api);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pl->m_Cmds.push_back(pCmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::RemovePluginCvar(ISmmPlugin *api, ConCommandBase *pCvar)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindByAPI(api);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pl->m_Cvars.remove(pCvar);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::RemovePluginCmd(ISmmPlugin *api, ConCommandBase *pCmd)
|
|
|
|
{
|
|
|
|
CPlugin *pl = FindByAPI(api);
|
|
|
|
|
|
|
|
if (!pl)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pl->m_Cmds.remove(pCmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CPluginManager::UnregAllConCmds(CPlugin *pl)
|
|
|
|
{
|
|
|
|
SourceHook::List<ConCommandBase *>::iterator i;
|
|
|
|
|
|
|
|
/* :TODO: */
|
|
|
|
for (i=pl->m_Cvars.begin(); i!=pl->m_Cvars.end(); i++)
|
|
|
|
{
|
|
|
|
g_Metamod.UnregisterConCommandBase(pl->m_Id, (*i) );
|
|
|
|
}
|
|
|
|
pl->m_Cvars.clear();
|
|
|
|
|
|
|
|
for (i=pl->m_Cmds.begin(); i!=pl->m_Cmds.end(); i++)
|
|
|
|
{
|
|
|
|
g_Metamod.UnregisterConCommandBase(pl->m_Id, (*i) );
|
|
|
|
}
|
|
|
|
pl->m_Cmds.clear();
|
|
|
|
}
|
|
|
|
|