Raised This Month: $ Target: $400
 0% 

[INC] Hard linking ConVars to variables


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
FaTony
Veteran Member
Join Date: Aug 2008
Old 01-01-2011 , 15:15   [INC] Hard linking ConVars to variables
Reply With Quote #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(convarConVarChange);
}

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(convarConVarBoolbuffer);
}

/**
 * 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(convarConVarIntbuffer);
}

/**
 * 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(convarConVarFloatbuffer);
}

/**
 * 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:convarString:buffer[], const size)
{
    if (
convar == INVALID_HANDLE)
    {
        return 
false;
    }
    if (
ConVarTrie == INVALID_HANDLE)
    {
        
ConVarTrie CreateTrie();
    }
    
decl String:convarname[CONVAR_NAME_SIZE];
    
GetConVarName(convarconvarnameCONVAR_NAME_SIZE);
    
SetTrieValue(ConVarTrieconvarnameCreateStringHandle(buffersize));
    
HookConVarChange(convarConVarStringChange);
    
GetConVarString(convarbuffersize);
    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(namedefaultvaluedescriptionflagstrue0.0true1.0);
    if (!
LinkConVar(convarConVarBoolbuffer))
    {
        
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 0bool:hasmin falseFloat:min 0.0bool:hasmax falseFloat:max 0.0)
{
    new 
Handle:convar CreateConVar(namedefaultvaluedescriptionflagshasminminhasmaxmax);
    if (!
LinkConVar(convarConVarIntbuffer))
    {
        
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 0bool:hasmin falseFloat:min 0.0bool:hasmax falseFloat:max 0.0)
{
    new 
Handle:convar CreateConVar(namedefaultvaluedescriptionflagshasminminhasmaxmax);
    if (!
LinkConVar(convarConVarFloatbuffer))
    {
        
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(namedefaultvaluedescriptionflags);
    if (!
LinkConVarString(convarbuffersize))
    {
        
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(convarconvarnameCONVAR_NAME_SIZE);
    switch (
type)
    {
        case 
ConVarBool:
        {
            
SetTrieValue(ConVarTrieconvarnameCreateCellHandle(buffer));
            
HookConVarChange(convarConVarBoolChange);
            
buffer GetConVarBool(convar);
        }
        case 
ConVarInt:
        {
            
SetTrieValue(ConVarTrieconvarnameCreateCellHandle(buffer));
            
HookConVarChange(convarConVarIntChange);
            
buffer GetConVarInt(convar);
        }
        case 
ConVarFloat:
        {
            
SetTrieValue(ConVarTrieconvarnameCreateCellHandle(buffer));
            
HookConVarChange(convarConVarFloatChange);
            
buffer GetConVarFloat(convar);
        }
    }
    return 
true;
}

public 
ConVarBoolChange(Handle:convar, const String:oldvalue[], const String:newvalue[])
{
    
ConVarChange(convarConVarBoolnewvalue);
}

public 
ConVarIntChange(Handle:convar, const String:oldvalue[], const String:newvalue[])
{
    
ConVarChange(convarConVarIntnewvalue);
}

public 
ConVarFloatChange(Handle:convar, const String:oldvalue[], const String:newvalue[])
{
    
ConVarChange(convarConVarFloatnewvalue);
}

public 
ConVarStringChange(Handle:convar, const String:oldvalue[], const String:newvalue[])
{
    
ConVarChange(convarConVarStringnewvalue);
}

stock static ConVarChange(const Handle:convar, const ConVarType:type, const String:newvalue[])
{
    
decl String:convarname[CONVAR_NAME_SIZE];
    
GetConVarName(convarconvarnameCONVAR_NAME_SIZE);
    new 
Handle:buffer;
    if (!
GetTrieValue(ConVarTrieconvarnamebuffer))
    {
        return;
    }
    switch (
type)
    {
        case 
ConVarBool:
        {
            
SetHandleCell(bufferGetConVarBool(convar));
        }
        case 
ConVarInt:
        {
            
SetHandleCell(bufferGetConVarInt(convar));
        }
        case 
ConVarFloat:
        {
            
SetHandleCell(bufferGetConVarFloat(convar));
        }
        case 
ConVarString:
        {
            
SetHandleString(buffernewvalueGetHandleStringSize(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(clientargs)
{
    
ReplyToCommand(client"Value = %d"testvalue);
    return 
Plugin_Handled;

convarlink.inc should be placed under scripting/ftz_public.

Comments, suggestions?
Attached Files
File Type: inc convarlink.inc (9.1 KB, 294 views)
__________________

Last edited by FaTony; 02-27-2011 at 20:30.
FaTony is offline
DarkEnergy
SourceMod Donor
Join Date: Apr 2008
Location: Georgia Tech, MSECE
Old 01-01-2011 , 21:12   Re: [INC] Hard linking ConVars to variables
Reply With Quote #2

why not make an simple stock to create a linked convar

stock CreateConVarLineked(String:cvar[],String:value[],&linkedvariable);
__________________
War3:Source Developer
"Your CPU is just a bunch of Muxes"
DarkEnergy is offline
DarkEnergy
SourceMod Donor
Join Date: Apr 2008
Location: Georgia Tech, MSECE
Old 01-01-2011 , 22:37   Re: [INC] Hard linking ConVars to variables
Reply With Quote #3

stock CreateConVarLinked(String:cvar[],String:value[],Stringescription[],&linkedvariable);

don't care about the other args cuz most people dont use them
the cvar handle will be used to link and never returned to the caller, it essentially is a memory leak, but cvar handles are never destroyed anyway
__________________
War3:Source Developer
"Your CPU is just a bunch of Muxes"
DarkEnergy is offline
bl4nk
SourceMod Developer
Join Date: Jul 2007
Old 01-01-2011 , 22:42   Re: [INC] Hard linking ConVars to variables
Reply With Quote #4

Code:
stock static Handle:ConVarTrie = INVALID_HANDLE;
Shouldn't be stock (not a function)..
bl4nk is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 01-01-2011 , 23:17   Re: [INC] Hard linking ConVars to variables
Reply With Quote #5

Quote:
Originally Posted by Pawn Language Guide
A global variable may be declared as “stock”. A stock declaration is one
that the parser may remove or ignore if the variable turns out not to be used
in the program.
As for CreateConVarLinked, in that case this function should return a handle to the convar, otherwise there would be no easy way to set it's value. Only FindConVar.
__________________
FaTony is offline
bl4nk
SourceMod Developer
Join Date: Jul 2007
Old 01-02-2011 , 00:02   Re: [INC] Hard linking ConVars to variables
Reply With Quote #6

Hm, that makes sense, but why you're using it really does not. It's going to be used for sure, so why even bother?
bl4nk is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 01-02-2011 , 00:11   Re: [INC] Hard linking ConVars to variables
Reply With Quote #7

Well in case that inc with extension will be added into some compilation or library with single inc that will include all files, like windows.h. I'm thinking of scalability here.
__________________
FaTony is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 01-02-2011 , 14:47   Re: [INC] Hard linking ConVars to variables
Reply With Quote #8

Added CreateConVarLinked family of functions.
__________________
FaTony is offline
FaTony
Veteran Member
Join Date: Aug 2008
Old 02-27-2011 , 19:31   Re: [INC] Hard linking ConVars to variables
Reply With Quote #9

Updated to coincide with the release of [ANY] Money system.
Updated folder structure, improved documentation.
__________________
FaTony is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 18:22.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode