SourcePawn Basics - Customization through ConVars

From AlliedModders Wiki
Revision as of 06:25, 5 November 2016 by NgBUCKWANGS (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Prerequisite: This guide assumes you have read Introduction to SourcePawn and Introduction to SourceMod Plugins.

Server owners like customization. Lots of customization. So, how do you add customization to your plugin? The simplest route is by adding ConVars.

General Info

Console Variables (ConVars or CVars for short) are server settings that can be changed by server owners. Anything you put into your server's server.cfg file is a convar.

Just like the server itself, plugins can create their own convars. A convar is different from a regular variable in that it is visible outside your plugin.

Creating your own ConVars

ConVars should always be created in OnPluginStart.

When you create your ConVars, they should name them consistently. A good naming practice is nameofplugin_settingname or nameofplugin_setting_name.

ConVars are created via the CreateConVar command. The order of arguments are:

name 
The convar name, preferably using the naming scheme above
defaultValue 
A string with the default value.
description 
A description of what this ConVar does for the find or listcvars commands
flags 
An | separate list of convar flags
hasMin 
A bool stating whether the min argument exists
min 
A Float with the minimum allowed value
hasMax 
A bool stating whether the max argument exists
min 
A Float with the maximum allowed value

Useful ConVar Flags

  • FCVAR_NONE - The default, no flags set.
  • FCVAR_PROTECTED - Used for passwords. Don't send this value to clients who ask for it, only send 1 if it's set or 0 if it isn't.
  • FCVAR_SPONLY - Not changeable in multiplayer
  • FCVAR_NOTIFY - Players are informed when this ConVar changes. ConVars marked with this attribute are also visible to tracking services.
  • FCVAR_NEVER_AS_STRING - Don't try to print this out
  • FCVAR_DONTRECORD - Not recorded in demo files. Also, not recorded in AutoExecConfig files.
  • FCVAR_PLUGIN - Defined by a 3rd Party plugin. SourceMod probably adds this already even if you don't.

Standard ConVars

There are a few standard ConVars that most plugins have.

The first is a version ConVar. Unlike most ConVars, you don't need to store the Handle of this one as you never need to read its value.

Normally, your version ConVar will look a little like this.

 #define VERSION "1.0"
 
 public OnPluginStart()
 {
     CreateConVar("test_version", VERSION, "Test Plugin version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_PLUGIN|FCVAR_SPONLY);
 }

|FCVAR_PLUGIN|FCVAR_SPONLY is not strictly required here, but it is fairly common to see them set.

VERSION is defined here so that it can be used in the plugin's myinfo struct as well.

A second common ConVar is the enable ConVar.

 new Handle:g_hEnabled;
 
 public OnPluginStart()
 {
     g_hEnabled = CreateConVar("test_enable", "1", "Test Plugin Enable", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0);
 }

Note that FCVAR_DONTRECORD is still present. This is a recommendation in case your plugin has an AutoExecConfig file so that your enable ConVar can be controlled by map configurations or server.cfg.

FCVAR_NOTIFY is here as well so that GameTracker and the like can tell if the plugin is enabled.

Loading ConVars from a file

SourceMod has a command AutoExecConfig to automatically read/create a ConVar configuration file. To use it, put this at the end of OnPluginStart after your CreateConVar lines:

     AutoExecConfig(true, "nameofplugin");

which will create a file named cfg/sourcemod/nameofplugin.cfg

Locating ConVars from the game or other plugins

Looking up plugins is an expensive operation and should be done just once if you can help it.

The best location to do this is in the OnAllPluginsLoaded callback.

For example, here is how to look up the mp_autoteambalance ConVar

 new Handle:g_hAutoTeamBalance;
 
 public OnAllPluginsLoaded()
 {
     g_hAutoTeamBalance = FindConVar("mp_autoteambalance");
 }

Reading and Setting ConVars

Both ConVars from your plugin and ConVars from the game or other plugins can be read the same way.

ConVars can be read as several different kinds of values: cell (int), bool, Float, or String.

Here is how you read a bool ConVar:

     new bool:value = GetConVarBool(g_hAutoTeamBalance);

and to set the same ConVar:

     SetConVarBool(g_hAutoTeamBalance, true);

Int and Floats work roughly the same way with GetConVarInt / GetConVarFloat / SetConVarInt / SetConVarFloat.

Strings work the same for setting, but reading a String ConVar works slightly differently:

   decl String:value[64];
   GetConVarString(myConVar, value, sizeof(value));

This pattern is common for functions that do String manipulations.

Catching that a ConVar changed

In addition to just reading and changing ConVar values, you can also catch when a ConVar's value is changed.

This is done through a ConVarChange hook. ConVarChange hooks only fire when the value changes from one state to another. So, if a ConVar is already set to 1 and a config changes it to 1 (the same value), the ConVarChange hook will not fire.

You can add a ConVarChange hook like this:

 new Handle:g_hEnabled;
 
 public OnPluginStart()
 {
     g_hEnabled = CreateConVar("test_enable", "1", "Test Plugin Enable", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0);
     HookConVarChange(g_hEnabled, ConVar_EnableChange);
 }
 
 public ConVar_EnableChange(Handle:convar, const String:oldValue[], const String:newValue[])
 {
     new enabled = GetConVarBool(g_hEnabled);
 
     if (enabled)
     {
         // We were just enabled
     }
     else
     {
         // We were just disabled
     }
 }

Note that it's quicker to use GetConVarBool, Int, or Float instead of converting oldValue or newValue to the appropriate types.

There is also an UnhookConVarChange, but it is largely unnecessary as these hooks are automatically released when your plugin unloads.

Make sure you hook the ConVar in the same function you Create or Find it. It's also a good idea to check the return value of FindConVar before hooking it, as a game that doesn't support a ConVar will return INVALID_HANDLE

Reading ConVars from server.cfg ASAP

On server first launch, ConVars in server.cfg are loaded later than OnPluginStart (e.g, see Q: Can I get cvar values in OnPluginStart? on this page http://www.sourcemod.net/devlog/?p=126). A working example of how to read from server.cfg ASAP but falling back to a default value in case it doesn't exist follows.

   ConVar g_YourConVar;
   new g_YourGlobalVar;
   
   public OnPluginStart() {
       g_YourConVar = CreateConVar("your_convar", "your_default_value");
       g_YourGlobalVar = g_YourConVar.IntValue;
       HookConVarChange(g_YourConVar, YourGlobalVarHook);
   }
   
   public YourGlobalVarHook(Handle:convar, const String:oldValue[], const String:newValue[]) {
       g_YourGlobalVar = GetConVarInt(g_YourConVar);
       PrintToServer("g_YourGlobalVar Has Changed: %d", g_YourGlobalVar);
   }