From 22aa6561b5f47f2dd5a2789541231c0df77222e5 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Mon, 9 Jun 2008 07:12:32 +0000 Subject: [PATCH] Added SetUserInfo command to sdktools. --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%402247 --- extensions/sdktools/vnatives.cpp | 61 ++++++++++++++++++++++++++ gamedata/sdktools.games.ep2.txt | 30 +++++++++++++ gamedata/sdktools.games.txt | 30 +++++++++++++ plugins/include/sdktools_functions.inc | 12 +++++ 4 files changed, 133 insertions(+) diff --git a/extensions/sdktools/vnatives.cpp b/extensions/sdktools/vnatives.cpp index f966dd7e5..711360df3 100644 --- a/extensions/sdktools/vnatives.cpp +++ b/extensions/sdktools/vnatives.cpp @@ -37,6 +37,8 @@ #include "vhelpers.h" #include "vglobals.h" #include "CellRecipientFilter.h" +#include +#include SourceHook::List g_RegCalls; SourceHook::List g_CallWraps; @@ -916,6 +918,64 @@ static cell_t ActivateEntity(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t SetClientInfo(IPluginContext *pContext, const cell_t *params) +{ + IGamePlayer *player = playerhelpers->GetGamePlayer(params[1]); + if (player == NULL) + { + return pContext->ThrowNativeError("Invalid client index %d", params[1]); + } + if (!player->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + static ValveCall *pCall = NULL; + if (!pCall) + { + ValvePassInfo params[2]; + InitPass(params[0], Valve_String, PassType_Basic, PASSFLAG_BYVAL); + InitPass(params[1], Valve_String, PassType_Basic, PASSFLAG_BYVAL); + + if (!CreateBaseCall("SetUserCvar", ValveCall_Entity, NULL, params, 2, &pCall)) + { + return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod"); + } + else if (!pCall) + { + return pContext->ThrowNativeError("\"SetUserCvar\" wrapper failed to initialized"); + } + } + + static int changedOffset = -1; + + if (changedOffset == -1) + { + if (!g_pGameConf->GetOffset("InfoChanged", &changedOffset)) + { + return pContext->ThrowNativeError("\"SetUserCvar\" not supported by this mod"); + } + } + + INetChannel *pNetChan = static_cast(engine->GetPlayerNetInfo(params[1])); + IClient *pClient = static_cast(pNetChan->GetMsgHandler()); + + unsigned char *CGameClient = (unsigned char *)pClient - 4; + + START_CALL(); + /* Not really a CBaseEntity* but this works */ + CBaseEntity **ebuf = (CBaseEntity **)vptr; + *ebuf = (CBaseEntity *)CGameClient; + DECODE_VALVE_PARAM(2, vparams, 0); + DECODE_VALVE_PARAM(3, vparams, 1); + FINISH_CALL_SIMPLE(NULL); + + uint8_t* changed = (uint8_t *)(CGameClient + changedOffset); + *changed = 1; + + return 1; +} + sp_nativeinfo_t g_Natives[] = { {"ExtinguishEntity", ExtinguishEntity}, @@ -942,5 +1002,6 @@ sp_nativeinfo_t g_Natives[] = {"GetServerNetStats", GetServerNetStats}, {"EquipPlayerWeapon", WeaponEquip}, {"ActivateEntity", ActivateEntity}, + {"SetClientInfo", SetClientInfo}, {NULL, NULL}, }; diff --git a/gamedata/sdktools.games.ep2.txt b/gamedata/sdktools.games.ep2.txt index 1230f7a0a..d405491d7 100644 --- a/gamedata/sdktools.games.ep2.txt +++ b/gamedata/sdktools.games.ep2.txt @@ -411,4 +411,34 @@ } } } + + /* SetUserInfo data */ + "#default" + { + "Offsets" + { + /** + * CBaseClient::SetUserCVar(char const*,char const*); + * Linux offset straight from VTable dump. + * Windows offset is crazy. Found the windows 'SetName' function using string "(%d)%-0.*s" + * Cross referenced back to the vtable and counted manually (SetUserCvar is 1 higher, offsets start from 1) + */ + "SetUserCvar" + { + "windows" "17" + "linux" "55" + } + /** + * Offset into CBaseClient - Used by CBaseServer::UpdateUserSettings to determine when changes have been made. + * Find CBaseClient::UpdateUserSettings (strings "net_maxroutable", "cl_updaterate" etc) and the offset is set to 0 near the end. + * Linux: mov byte ptr [esi+98h], 0 + * Win: mov byte ptr [esi+0A4h], 0 + */ + "InfoChanged" + { + "windows" "164" + "linux" "152" + } + } + } } diff --git a/gamedata/sdktools.games.txt b/gamedata/sdktools.games.txt index a081fba10..8002b2037 100644 --- a/gamedata/sdktools.games.txt +++ b/gamedata/sdktools.games.txt @@ -1858,5 +1858,35 @@ } } } + + /* SetUserInfo data */ + "#default" + { + "Offsets" + { + /** + * CBaseClient::SetUserCVar(char const*,char const*); + * Linux offset straight from VTable dump. + * Windows offset is crazy. Found the windows 'SetName' function using string "(%d)%-0.*s" + * Cross referenced back to the vtable and counted manually (SetUserCvar is 1 higher, offsets start from 1) + */ + "SetUserCvar" + { + "windows" "17" + "linux" "53" + } + /** + * Offset into CBaseClient - Used by CBaseServer::UpdateUserSettings to determine when changes have been made. + * Find CBaseClient::UpdateUserSettings (strings "net_maxroutable", "cl_updaterate" etc) and the offset is set to 0 near the end. + * Linux: mov byte ptr [esi+98h], 0 + * Win: mov byte ptr [esi+0A4h], 0 + */ + "InfoChanged" + { + "windows" "164" + "linux" "152" + } + } + } } diff --git a/plugins/include/sdktools_functions.inc b/plugins/include/sdktools_functions.inc index 1d6d0eb30..f3d5f8fa0 100644 --- a/plugins/include/sdktools_functions.inc +++ b/plugins/include/sdktools_functions.inc @@ -308,3 +308,15 @@ native EquipPlayerWeapon(client, weapon); * @error Invalid entity or lack of mod support. */ native ActivateEntity(entity); + +/** + * Sets values to client info buffer keys and notifies the engine of the change. + * The change does not get propogated to mods until the next frame. + * + * @param client Player's index. + * @param key Key string. + * @param value Value string. + * @noreturn + * @error Invalid client index, or client not connected. + */ +native SetClientInfo(client, const String:key[], const String:value[]);