Added client convar querying :o

Also added ISourceMod::GetModFolderName() for returning the name of the mod directory by itself

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40702
This commit is contained in:
Scott Ehlert 2007-04-14 04:27:47 +00:00
parent c8f507561c
commit 77e49c855f
11 changed files with 297 additions and 62 deletions

View File

@ -13,6 +13,7 @@
*/
#include "ConVarManager.h"
#include "HalfLife2.h"
#include "PluginSys.h"
#include "ForwardSys.h"
#include "HandleSys.h"
@ -22,10 +23,13 @@
ConVarManager g_ConVarManager;
SH_DECL_HOOK5_void(IServerGameDLL, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
SH_DECL_HOOK5_void(IServerPluginCallbacks, OnQueryCvarValueFinished, SH_NOATTRIB, 0, QueryCvarCookie_t, edict_t *, EQueryCvarValueStatus, const char *, const char *);
const ParamType CONVARCHANGE_PARAMS[] = {Param_Cell, Param_String, Param_String};
typedef List<const ConVar *> ConVarList;
ConVarManager::ConVarManager() : m_ConVarType(0)
ConVarManager::ConVarManager() : m_ConVarType(0), m_VSPIface(NULL), m_CanQueryConVars(false)
{
/* Create a convar lookup trie */
m_ConVarCache = sm_trie_create();
@ -49,6 +53,13 @@ ConVarManager::~ConVarManager()
void ConVarManager::OnSourceModAllInitialized()
{
/* Only valid with ServerGameDLL006 or greater */
if (g_SMAPI->GetGameDLLVersion() >= 6)
{
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
m_CanQueryConVars = true;
}
HandleAccess sec;
/* Set up access rights for the 'ConVar' handle type */
@ -65,13 +76,19 @@ void ConVarManager::OnSourceModAllInitialized()
void ConVarManager::OnSourceModShutdown()
{
if (m_CanQueryConVars)
{
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, OnQueryCvarValueFinished, gamedll, this, &ConVarManager::OnQueryCvarValueFinished, false);
SH_REMOVE_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, m_VSPIface, this, &ConVarManager::OnQueryCvarValueFinished, false);
}
IChangeableForward *fwd;
List<ConVarInfo *>::iterator i;
/* Iterate list of ConVarInfo structures */
for (i = m_ConVars.begin(); i != m_ConVars.end(); i++)
{
fwd = (*i)->changeForward;
fwd = (*i)->pChangeForward;
/* Free any convar-change forwards that still exist */
if (fwd)
@ -87,6 +104,29 @@ void ConVarManager::OnSourceModShutdown()
g_HandleSys.RemoveType(m_ConVarType, g_pCoreIdent);
}
void ConVarManager::OnSourceModVSPReceived(IServerPluginCallbacks *iface)
{
/* This will be called after OnSourceModAllInitialized(), so...
*
* If we haven't been able to hook the IServerGameDLL version at this point,
* then use hook IServerPluginCallbacks version from the engine.
*/
/* The Ship currently only supports ServerPluginCallbacks001, but we need 002 */
if (g_IsOriginalEngine)
{
return;
}
if (!m_CanQueryConVars)
{
m_VSPIface = iface;
SH_ADD_HOOK_MEMFUNC(IServerPluginCallbacks, OnQueryCvarValueFinished, iface, this, &ConVarManager::OnQueryCvarValueFinished, false);
m_CanQueryConVars = true;
}
}
void ConVarManager::OnPluginUnloaded(IPlugin *plugin)
{
ConVarList *pConVarList;
@ -190,7 +230,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo = new ConVarInfo();
pInfo->handle = hndl;
pInfo->sourceMod = false;
pInfo->changeForward = NULL;
pInfo->pChangeForward = NULL;
pInfo->origCallback = pConVar->GetCallback();
/* Insert struct into caches */
@ -201,7 +241,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
}
}
// To prevent creating a convar that has the same name as a console command... ugh
/* To prevent creating a convar that has the same name as a console command... ugh */
ConCommandBase *pBase = icvar->GetCommands();
while (pBase)
@ -227,7 +267,7 @@ Handle_t ConVarManager::CreateConVar(IPluginContext *pContext, const char *name,
pInfo = new ConVarInfo();
pInfo->handle = hndl;
pInfo->sourceMod = true;
pInfo->changeForward = NULL;
pInfo->pChangeForward = NULL;
pInfo->origCallback = NULL;
/* Insert struct into caches */
@ -265,7 +305,7 @@ Handle_t ConVarManager::FindConVar(const char *name)
pInfo = new ConVarInfo();
pInfo->handle = hndl;
pInfo->sourceMod = false;
pInfo->changeForward = NULL;
pInfo->pChangeForward = NULL;
pInfo->origCallback = pConVar->GetCallback();
/* Insert struct into our caches */
@ -284,13 +324,13 @@ void ConVarManager::HookConVarChange(ConVar *pConVar, IPluginFunction *pFunction
if (sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), (void **)&pInfo))
{
/* Get the forward */
pForward = pInfo->changeForward;
pForward = pInfo->pChangeForward;
/* If forward does not exist, create it */
if (!pForward)
{
pForward = g_Forwards.CreateForwardEx(NULL, ET_Ignore, 3, CONVARCHANGE_PARAMS);
pInfo->changeForward = pForward;
pInfo->pChangeForward = pForward;
/* Install our own callback */
pConVar->InstallChangeCallback(OnConVarChanged);
@ -311,7 +351,7 @@ void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFuncti
if (sm_trie_retrieve(m_ConVarCache, pConVar->GetName(), (void **)&pInfo))
{
/* Get the forward */
pForward = pInfo->changeForward;
pForward = pInfo->pChangeForward;
/* If the forward doesn't exist, we can't unhook anything */
if (!pForward)
@ -332,7 +372,7 @@ void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFuncti
{
/* Free this forward */
g_Forwards.ReleaseForward(pForward);
pInfo->changeForward = NULL;
pInfo->pChangeForward = NULL;
/* Put back the original convar callback */
pConVar->InstallChangeCallback(pInfo->origCallback);
@ -340,6 +380,24 @@ void ConVarManager::UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFuncti
}
}
QueryCvarCookie_t ConVarManager::QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback, Handle_t hndl)
{
QueryCvarCookie_t cookie;
/* Call StartQueryCvarValue() in either the IVEngineServer or IServerPluginHelpers depending on situation */
if (!m_VSPIface)
{
cookie = engine->StartQueryCvarValue(pPlayer, name);
} else {
cookie = serverpluginhelpers->StartQueryCvarValue(pPlayer, name);
}
ConVarQuery query = {cookie, pCallback, hndl};
m_ConVarQueries.push_back(query);
return cookie;
}
void ConVarManager::AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar)
{
ConVarList *pConVarList;
@ -388,7 +446,7 @@ void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
sm_trie_retrieve(pCache, pConVar->GetName(), (void **)&pInfo);
FnChangeCallback origCallback = pInfo->origCallback;
IChangeableForward *pForward = pInfo->changeForward;
IChangeableForward *pForward = pInfo->pChangeForward;
/* If there was a change callback installed previously, call it */
if (origCallback)
@ -403,6 +461,49 @@ void ConVarManager::OnConVarChanged(ConVar *pConVar, const char *oldValue)
pForward->Execute(NULL);
}
void ConVarManager::OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result, const char *cvarName, const char *cvarValue)
{
IPluginFunction *pCallback = NULL;
cell_t value = 0;
List<ConVarQuery>::iterator iter;
for (iter = m_ConVarQueries.begin(); iter != m_ConVarQueries.end(); iter++)
{
ConVarQuery &query = (*iter);
if (query.cookie == cookie)
{
pCallback = query.pCallback;
value = query.value;
break;
}
}
if (pCallback)
{
cell_t ret;
pCallback->PushCell(cookie);
pCallback->PushCell(engine->IndexOfEdict(pPlayer));
pCallback->PushCell(result);
pCallback->PushString(cvarName);
if (result == eQueryCvarValueStatus_ValueIntact)
{
pCallback->PushString(cvarValue);
} else {
pCallback->PushString("\0");
}
pCallback->PushCell(value);
pCallback->Execute(&ret);
m_ConVarQueries.erase(iter);
}
}
static int s_YamagramState = 0;
void _YamagramPrinterTwoPointOhOh(int yamagram)

View File

@ -33,10 +33,20 @@ struct ConVarInfo
{
Handle_t handle; /**< Handle to convar */
bool sourceMod; /**< Determines whether or not convar was created by a SourceMod plugin */
IChangeableForward *changeForward; /**< Forward associated with convar */
IChangeableForward *pChangeForward; /**< Forward associated with convar */
FnChangeCallback origCallback; /**< The original callback function */
};
/**
* Holds information about a client convar query
*/
struct ConVarQuery
{
QueryCvarCookie_t cookie; /**< Cookie that identifies query */
IPluginFunction *pCallback; /**< Function that will be called when query is finished */
cell_t value; /**< Optional value passed to query function */
};
class ConVarManager :
public SMGlobalClass,
public IHandleTypeDispatch,
@ -49,6 +59,7 @@ public:
public: // SMGlobalClass
void OnSourceModAllInitialized();
void OnSourceModShutdown();
void OnSourceModVSPReceived(IServerPluginCallbacks *iface);
public: // IHandleTypeDispatch
void OnHandleDestroy(HandleType_t type, void *object);
public: // IPluginsListener
@ -92,6 +103,12 @@ public:
* Remove a function from the forward that will be called when the specified convar changes.
*/
void UnhookConVarChange(ConVar *pConVar, IPluginFunction *pFunction);
/**
* Starts a query to find the value of a client convar.
*/
QueryCvarCookie_t QueryClientConVar(edict_t *pPlayer, const char *name, IPluginFunction *pCallback,
Handle_t hndl);
private:
/**
* Adds a convar to a plugin's list.
@ -99,13 +116,22 @@ private:
static void AddConVarToPluginList(IPluginContext *pContext, const ConVar *pConVar);
/**
* Static callback that Valve's ConVar class executes when the convar's value changes.
* Static callback that Valve's ConVar object executes when the convar's value changes.
*/
static void OnConVarChanged(ConVar *pConVar, const char *oldValue);
/**
* Callback for when StartQueryCvarValue() has finished.
*/
void OnQueryCvarValueFinished(QueryCvarCookie_t cookie, edict_t *pPlayer, EQueryCvarValueStatus result,
const char *cvarName, const char *cvarValue);
private:
HandleType_t m_ConVarType;
List<ConVarInfo *> m_ConVars;
List<ConVarQuery> m_ConVarQueries;
Trie *m_ConVarCache;
IServerPluginCallbacks *m_VSPIface;
bool m_CanQueryConVars;
};
extern ConVarManager g_ConVarManager;

View File

@ -301,28 +301,7 @@ void GameConfigManager::OnSourceModStartup(bool late)
{
LoadGameConfigFile("core.games", &g_pGameConf, NULL, 0);
char mod[255];
engine->GetGameDir(mod, sizeof(mod));
g_mod[0] = '\0';
size_t len = strlen(mod);
for (size_t i=len-1; i>=0 && i<len; i++)
{
if (mod[i] == '/')
{
if (i == len-1)
{
mod[i] = '\0';
continue;
}
strcpy(g_mod, &mod[i]);
break;
}
}
if (g_mod[0] != '\0')
{
strcpy(g_mod, mod);
}
strncopy(g_mod, g_SourceMod.GetModFolderName(), sizeof(g_mod));
}
void GameConfigManager::OnSourceModAllInitialized()

View File

@ -13,6 +13,7 @@
*/
#include "HalfLife2.h"
#include "sourcemod.h"
#include "sourcemm_api.h"
CHalfLife2 g_HL2;
@ -70,8 +71,12 @@ CSharedEdictChangeInfo *g_pSharedChangeInfo = NULL;
void CHalfLife2::OnSourceModStartup(bool late)
{
if (!g_IsOriginalEngine && !g_pSharedChangeInfo)
/* The Ship currently is the only known game to use an older version of the engine */
if (strcasecmp(g_SourceMod.GetModFolderName(), "ship") == 0)
{
/* :TODO: Better engine versioning - perhaps something added to SourceMM? */
g_IsOriginalEngine = true;
} else if (!g_pSharedChangeInfo) {
g_pSharedChangeInfo = engine->GetSharedEdictChangeInfo();
}
}

View File

@ -13,6 +13,7 @@
*/
#include "sm_globals.h"
#include "HalfLife2.h"
#include "sourcemm_api.h"
#include "HandleSys.h"
#include "ConVarManager.h"
@ -342,6 +343,48 @@ static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
return 1;
}
static bool s_QueryAlreadyWarned = false;
static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
{
CPlayer *pPlayer;
char *name;
IPluginFunction *pCallback;
if (g_IsOriginalEngine)
{
if (!s_QueryAlreadyWarned)
{
s_QueryAlreadyWarned = true;
return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
}
return 0;
}
pPlayer = g_Players.GetPlayerByIndex(params[1]);
if (!pPlayer)
{
return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
}
if (!pPlayer->IsConnected() || pPlayer->IsFakeClient())
{
return pContext->ThrowNativeError("Player %d is either not connected or a bot", params[1]);
}
pContext->LocalToString(params[2], &name);
pCallback = pContext->GetFunctionById(params[3]);
if (!pCallback)
{
return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
}
return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
}
static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
{
char *name,*help;
@ -584,6 +627,7 @@ REGISTER_NATIVES(consoleNatives)
{"GetConVarMin", sm_GetConVarMin},
{"GetConVarMax", sm_GetConVarMax},
{"ResetConVar", sm_ResetConVar},
{"QueryClientConVar", sm_QueryClientConVar},
{"RegServerCmd", sm_RegServerCmd},
{"RegConsoleCmd", sm_RegConsoleCmd},
{"GetCmdArgString", sm_GetCmdArgString},

View File

@ -100,7 +100,21 @@ ConfigResult SourceModBase::OnSourceModConfigChanged(const char *key,
bool SourceModBase::InitializeSourceMod(char *error, size_t err_max, bool late)
{
g_BaseDir.assign(g_SMAPI->GetBaseDir());
const char *gamepath = g_SMAPI->GetBaseDir();
/* Store full path to game */
g_BaseDir.assign(gamepath);
/* Store name of game directory by itself */
size_t len = strlen(gamepath);
for (size_t i = len - 1; i >= 0; i--)
{
if (gamepath[i] == PLATFORM_SEP_CHAR)
{
strncopy(m_ModDir, &gamepath[++i], sizeof(m_ModDir));
break;
}
}
/* Initialize CoreConfig so we can get SourceMod base path properly - this basically parses core.cfg */
g_CoreConfig.Initialize();
@ -528,6 +542,11 @@ Handle_t SourceModBase::GetDataPackHandleType(bool readonly)
return 0;
}
const char *SourceModBase::GetModFolderName() const
{
return m_ModDir;
}
SMGlobalClass *SMGlobalClass::head = NULL;
SMGlobalClass::SMGlobalClass()

View File

@ -94,6 +94,7 @@ public: // ISourceMod
void FreeDataPack(IDataPack *pack);
HandleType_t GetDataPackHandleType(bool readonly=false);
KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false);
const char *GetModFolderName() const;
private:
/**
* @brief Loading plugins
@ -108,6 +109,7 @@ private:
CStack<CDataPack *> m_freepacks;
char m_SMBaseDir[PLATFORM_MAX_PATH];
char m_SMRelDir[PLATFORM_MAX_PATH];
char m_ModDir[32];
bool m_IsMapLoading;
bool m_ExecPluginReload;
unsigned int m_target;

View File

@ -711,7 +711,7 @@ int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *sour
len = strlen(source);
dest = (char *)(ctx->memory + local_addr);
if ((size_t)len >= bytes)
if (len >= bytes)
{
len = bytes - 1;
}

View File

@ -18,6 +18,17 @@
#endif
#define _console_included
/**
* Console variable query result values.
*/
enum ConVarQueryResult
{
ConVarQuery_Okay = 0, /**< Retrieval of client convar value was successful. */
ConVarQuery_NotFound, /**< Client convar was not found. */
ConVarQuery_NotValid, /**< A console command with the same name was found, but there is no convar. */
ConVarQuery_Protected /**< Client convar was found, but it is protected. The server cannot retrieve its value. */
};
/**
* @section Flags for console commands and console variables. The descriptions
* for each constant come directly from the Source SDK.
@ -398,3 +409,44 @@ native bool:GetConVarMax(Handle:convar, &Float:max);
* @error Invalid or corrupt Handle.
*/
native ResetConVar(Handle:convar);
funcenum ConVarQueryFinished
{
/**
* Called when a query to retrieve a client's console variable has finished.
*
* @param cookie Unique identifier of query.
* @param client Player index.
* @param result Result of query that tells one whether or not query was successful.
* See ConVarQueryResult enum for more details.
* @param convarName Name of client convar that was queried.
* @param convarValue Value of client convar that was queried if successful. This will be "" if it was not.
* @param value Value that was passed when query was started.
* @noreturn
*/
public(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[], any:value),
/**
* Called when a query to retrieve a client's console variable has finished.
*
* @param cookie Unique identifier of query.
* @param client Player index.
* @param result Result of query that tells one whether or not query was successful.
* See ConVarQueryResult enum for more details.
* @param convarName Name of client convar that was queried.
* @param convarValue Value of client convar that was queried if successful. This will be "" if it was not.
* @noreturn
*/
public(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[])
};
/**
* Starts a query to retrieve the value of a client's console variable.
*
* @param client Player index.
* @param name Name of client convar to query.
* @param callback A function to use as a callback when the query has finished.
* @param value Optional value to pass to the callback function.
* @return A cookie that uniquely identifies the query.
*/
native QueryCookie:QueryClientConVar(client, const String:cvarName[], ConVarQueryFinished:callback, any:value=0);

View File

@ -28,7 +28,7 @@ struct PlVers
};
/**
* Function helper values
* Function helper values.
*/
enum Function
{

View File

@ -28,7 +28,7 @@
#include <IDataPack.h>
#define SMINTERFACE_SOURCEMOD_NAME "ISourceMod"
#define SMINTERFACE_SOURCEMOD_VERSION 1
#define SMINTERFACE_SOURCEMOD_VERSION 2
/**
* @brief Forward declaration of the KeyValues class.
@ -138,29 +138,36 @@ namespace SourceMod
*/
virtual void FreeDataPack(IDataPack *pack) =0;
/**
* @brief Returns the automated data pack handle type.
*
* The readonly data type is the parent of the writable type.
* Note that calling CloseHandle() on either type will release the data pack.
* The readonly type is inheritable, but due to limitations of the Handle System,
* the writable type is not.
*
* @param readonly If true, the readonly type will be returned.
* @return The Handle type for storing generic data packs.
*/
virtual HandleType_t GetDataPackHandleType(bool readonly=false) =0;
/**
* @brief Returns the automated data pack handle type.
*
* The readonly data type is the parent of the writable type.
* Note that calling CloseHandle() on either type will release the data pack.
* The readonly type is inheritable, but due to limitations of the Handle System,
* the writable type is not.
*
* @param readonly If true, the readonly type will be returned.
* @return The Handle type for storing generic data packs.
*/
virtual HandleType_t GetDataPackHandleType(bool readonly=false) =0;
/**
* @brief Retrieves a KeyValues pointer from a handle.
*
* @param hndl Handle_t from which to retrieve contents.
* @param err Optional address to store a possible handle error.
* @param root If true it will return the root KeyValues pointer for the whole structure.
*
* @return The KeyValues pointer, or NULL for any error encountered.
*/
virtual KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false) =0;
/**
* @brief Retrieves a KeyValues pointer from a handle.
*
* @param hndl Handle_t from which to retrieve contents.
* @param err Optional address to store a possible handle error.
* @param root If true it will return the root KeyValues pointer for the whole structure.
*
* @return The KeyValues pointer, or NULL for any error encountered.
*/
virtual KeyValues *ReadKeyValuesHandle(Handle_t hndl, HandleError *err=NULL, bool root=false) =0;
/**
* @brief Returns the name of the directory in which the mod or game's gameinfo.txt resides.
*
* @return A string containing the name of the mod directory.
*/
virtual const char *GetModFolderName() const =0;
};
}