|
Veteran Member
|

01-01-2011
, 15:15
[INC] Hard linking ConVars to variables
|
#1
|
Hard linking ConVars to variables Before I start with describing this include file, let's first observe the typical workflow with ConVars in SourcePawn. Here I'll be focusing on the case when ConVar itself doesn't get modified by a plugin, but the user changes to it must be accounted immediately:
PHP Code:
new Handle:convar = INVALID_HANDLE;
public OnPluginStart() { convar = CreateConVar("myconvar", "0"); }
MyFunction() { new value = GetConVarInt(convar); //some code }
This code looks innocent as long as MyFunction gets called not very often. Now suppose your plugin is complex and MyFunction gets called several times OnGameFrame. Now you would want to optimize all those calls to GetConVarInt. How do you do it? You hook it.
PHP Code:
new value;
public OnPluginStart() { new Handle:convar = CreateConVar("myconvar", "0"); HookConVarChange(convar, ConVarChange); }
public ConVarChange(Handle:convar, const String:oldvalue[], const String:newvalue[]) { value = StringToInt(newvalue); }
MyFunction() { //some code }
This looks like a good solution. But what if your plugin is continuing to grow and you add new ConVars of this kind every now and them? You'll probably start to copy-paste hooks. After some time you'll probably have hundreds of copy-pasted lines of code you'll want to optimize. How do you store many variables? Trie? But to get the value you'll have to call GetTrieValue which is not faster than GetConVarInt. It seems like there is no universal way to solve this issue. You need to somehow store the value in a variable you don't know the name of. So, for this exact problem the Handles to SourcePawn variables extension has been created. Together with this include file it adds the ability to work with ConVars as almost like normal variables. Let's return back to our example, here we don't need to change the value of the ConVar, so we don't need a handle to it. The code will look like this:
PHP Code:
#include "ftz_public/convarlink"
new value;
public OnPluginStart() { CreateConVarLinkedInt("myconvar", "0", value); }
MyFunction() { //some code }
As you can see, we've saved 1 function call compared to the first example and 5 lines of code compared to the second. For each N ConVars we'll be saving N function calls (per MyFunction execution) or 5*N lines of code. If you want to change the value of ConVar, you still have to call SetConVarInt (or similar), because implementing this kind of hook will add too many additional overhead and defeat the purpose.
Here is the full include file:
PHP Code:
#if defined _convarlink_included #endinput #endif #define _convarlink_included
#include "ftz_public/spvhandles"
#define CONVAR_NAME_SIZE 32
enum ConVarType { ConVarBool, ConVarInt, ConVarFloat, ConVarString }
stock static Handle:ConVarTrie = INVALID_HANDLE;
/** * Links ConVar to a bool. * * @param convar ConVar to link. * @param buffer Buffer to link to. * @return True on success, false otherwise. */ stock bool:LinkConVarBool(const Handle:convar, &bool:buffer) { return LinkConVar(convar, ConVarBool, buffer); }
/** * Links ConVar to an int. * * @param convar ConVar to link. * @param buffer Buffer to link to. * @return True on success, false otherwise. */ stock bool:LinkConVarInt(const Handle:convar, &buffer) { return LinkConVar(convar, ConVarInt, buffer); }
/** * Links ConVar to a float. * * @param convar ConVar to link. * @param buffer Buffer to link to. * @return True on success, false otherwise. */ stock bool:LinkConVarFloat(const Handle:convar, &Float:buffer) { return LinkConVar(convar, ConVarFloat, buffer); }
/** * Links ConVar to a string. * * @param convar ConVar to link. * @param buffer Buffer to link to. * @param size Size of the provided buffer. * @return True on success, false otherwise. */ stock bool:LinkConVarString(const Handle:convar, String:buffer[], const size) { if (convar == INVALID_HANDLE) { return false; } if (ConVarTrie == INVALID_HANDLE) { ConVarTrie = CreateTrie(); } decl String:convarname[CONVAR_NAME_SIZE]; GetConVarName(convar, convarname, CONVAR_NAME_SIZE); SetTrieValue(ConVarTrie, convarname, CreateStringHandle(buffer, size)); HookConVarChange(convar, ConVarStringChange); GetConVarString(convar, buffer, size); return true; }
/** * Creates a new boolean console variable and links it to a provided buffer. * * @param name Name of new convar. * @param defaultvalue String containing the default value of new convar. * @param buffer Buffer to link to. * @param description Optional description of the convar. * @param flags Optional bitstring of flags determining how the convar * should be handled. See FCVAR_* constants for more * details. * @return A handle to the newly created convar. If the convar * already exists, a handle to it will still be returned. * @error Convar name is blank or is the same as an existing * console command. */ stock Handle:CreateConVarLinkedBool(const String:name[], const String:defaultvalue[], &bool:buffer, const String:description[] = "", flags = 0) { new Handle:convar = CreateConVar(name, defaultvalue, description, flags, true, 0.0, true, 1.0); if (!LinkConVar(convar, ConVarBool, buffer)) { CloseHandle(convar); return INVALID_HANDLE; } return convar; }
/** * Creates a new integer console variable and links it to a provided buffer. * * @param name Name of new convar. * @param defaultvalue String containing the default value of new convar. * @param buffer Buffer to link to. * @param description Optional description of the convar. * @param flags Optional bitstring of flags determining how the convar * should be handled. See FCVAR_* constants for more * details. * @param hasmin Optional boolean that determines if the convar has a * minimum value. * @param min Minimum floating point value that the convar can have if * hasmin is true. * @param hasmax Optional boolean that determines if the convar has a * maximum value. * @param max Maximum floating point value that the convar can have if * hasmax is true. * @return A handle to the newly created convar. If the convar * already exists, a handle to it will still be returned. * @error Convar name is blank or is the same as an existing * console command. */ stock Handle:CreateConVarLinkedInt(const String:name[], const String:defaultvalue[], &buffer, const String:description[] = "", flags = 0, bool:hasmin = false, Float:min = 0.0, bool:hasmax = false, Float:max = 0.0) { new Handle:convar = CreateConVar(name, defaultvalue, description, flags, hasmin, min, hasmax, max); if (!LinkConVar(convar, ConVarInt, buffer)) { CloseHandle(convar); return INVALID_HANDLE; } return convar; }
/** * Creates a new float console variable and links it to a provided buffer. * * @param name Name of new convar. * @param defaultvalue String containing the default value of new convar. * @param buffer Buffer to link to. * @param description Optional description of the convar. * @param flags Optional bitstring of flags determining how the convar * should be handled. See FCVAR_* constants for more * details. * @param hasmin Optional boolean that determines if the convar has a * minimum value. * @param min Minimum floating point value that the convar can have if * hasmin is true. * @param hasmax Optional boolean that determines if the convar has a * maximum value. * @param max Maximum floating point value that the convar can have if * hasmax is true. * @return A handle to the newly created convar. If the convar * already exists, a handle to it will still be returned. * @error Convar name is blank or is the same as an existing * console command. */ stock Handle:CreateConVarLinkedFloat(const String:name[], const String:defaultvalue[], &Float:buffer, const String:description[]= "", flags = 0, bool:hasmin = false, Float:min = 0.0, bool:hasmax = false, Float:max = 0.0) { new Handle:convar = CreateConVar(name, defaultvalue, description, flags, hasmin, min, hasmax, max); if (!LinkConVar(convar, ConVarFloat, buffer)) { CloseHandle(convar); return INVALID_HANDLE; } return convar; }
/** * Creates a new string console variable and links it to a provided buffer. * * @param name Name of new convar. * @param defaultvalue String containing the default value of new convar. * @param buffer Buffer to link to. * @param size Size of the provided buffer. * @param description Optional description of the convar. * @param flags Optional bitstring of flags determining how the convar * should be handled. See FCVAR_* constants for more * details. * @return A handle to the newly created convar. If the convar * already exists, a handle to it will still be returned. * @error Convar name is blank or is the same as an existing * console command. */ stock Handle:CreateConVarLinkedString(const String:name[], const String:defaultvalue[], String:buffer[], const size, const String:description[] = "", flags = 0) { new Handle:convar = CreateConVar(name, defaultvalue, description, flags); if (!LinkConVarString(convar, buffer, size)) { CloseHandle(convar); return INVALID_HANDLE; } return convar; }
//////////////////////////////////////////////////////////////////////////////// // Internal functions below this line ////////////////////////////////////////////////////////////////////////////////
stock static bool:LinkConVar(const Handle:convar, const ConVarType:type, &any:buffer) { if (convar == INVALID_HANDLE) { return false; } if (ConVarTrie == INVALID_HANDLE) { ConVarTrie = CreateTrie(); } decl String:convarname[CONVAR_NAME_SIZE]; GetConVarName(convar, convarname, CONVAR_NAME_SIZE); switch (type) { case ConVarBool: { SetTrieValue(ConVarTrie, convarname, CreateCellHandle(buffer)); HookConVarChange(convar, ConVarBoolChange); buffer = GetConVarBool(convar); } case ConVarInt: { SetTrieValue(ConVarTrie, convarname, CreateCellHandle(buffer)); HookConVarChange(convar, ConVarIntChange); buffer = GetConVarInt(convar); } case ConVarFloat: { SetTrieValue(ConVarTrie, convarname, CreateCellHandle(buffer)); HookConVarChange(convar, ConVarFloatChange); buffer = GetConVarFloat(convar); } } return true; }
public ConVarBoolChange(Handle:convar, const String:oldvalue[], const String:newvalue[]) { ConVarChange(convar, ConVarBool, newvalue); }
public ConVarIntChange(Handle:convar, const String:oldvalue[], const String:newvalue[]) { ConVarChange(convar, ConVarInt, newvalue); }
public ConVarFloatChange(Handle:convar, const String:oldvalue[], const String:newvalue[]) { ConVarChange(convar, ConVarFloat, newvalue); }
public ConVarStringChange(Handle:convar, const String:oldvalue[], const String:newvalue[]) { ConVarChange(convar, ConVarString, newvalue); }
stock static ConVarChange(const Handle:convar, const ConVarType:type, const String:newvalue[]) { decl String:convarname[CONVAR_NAME_SIZE]; GetConVarName(convar, convarname, CONVAR_NAME_SIZE); new Handle:buffer; if (!GetTrieValue(ConVarTrie, convarname, buffer)) { return; } switch (type) { case ConVarBool: { SetHandleCell(buffer, GetConVarBool(convar)); } case ConVarInt: { SetHandleCell(buffer, GetConVarInt(convar)); } case ConVarFloat: { SetHandleCell(buffer, GetConVarFloat(convar)); } case ConVarString: { SetHandleString(buffer, newvalue, GetHandleStringSize(buffer)); } } }
And here's complete example plugin:
PHP Code:
#include <sourcemod>
#include "ftz_public/convarlink"
#define PLUGIN_VERSION "1.0.0.2"
public Plugin:myinfo = { name = "Convar linking test", author = "FaTony", description = "Test", version = PLUGIN_VERSION, url = "http://fatony.com/" };
new testvalue;
public OnPluginStart() { CreateConVarLinkedInt("ftz_testconvar", "0", testvalue); RegConsoleCmd("ftz_checkconvar", Command_CheckConVar); }
public Action:Command_CheckConVar(client, args) { ReplyToCommand(client, "Value = %d", testvalue); return Plugin_Handled; }
convarlink.inc should be placed under scripting/ftz_public.
Comments, suggestions?
__________________
Last edited by FaTony; 02-27-2011 at 20:30.
|
|