View Single Post
Author Message
KyleS
SourceMod Plugin Approver
Join Date: Jul 2009
Location: Segmentation Fault.
Old 10-16-2013 , 13:20   [Tutorial] ClientPrefs
Reply With Quote #1

Alrighty, so using ClientPrefs is pretty straight forward. However, not a lot of plugins include support for it. Hopefully this helps someone.

The first thing you need to determine is what you need to store. Is it just a simple on off value? A name? In this case, let's store a simple bool.

PHP Code:
new bool:g_bClientPreference[MAXPLAYERS+1]; 
The next thing you need to do is RegClientCookie. This is where you determine the internal (32 character) name of your cookie, a subsequently a description. This is also where you determine the CookieAccess of a cookie. This can make or break you quite easily. If you make your cookie public, clients can raw set values using sm_cookies. This can provoke undefined behaviour, which can wreck you depending on your sanitation. In our case, we're making our cookie Private.

PHP Code:
new Handle:g_hClientCookie INVALID_HANDLE;

public 
OnPluginStart()
{
    
g_hClientCookie RegClientCookie("OurTestCookie""A Test Cookie for use in our Tutorial"CookieAccess_Private);

In our case, we're going to add our Cookie to the sm_settings menu. Because we're using a simple on and off switch, we can easily use SetCookiePrefabMenu. We're going to use CookieMenu_OnOff_Int, because in our case we only want to store 1 or 0.

PHP Code:
public OnPluginStart()
{
    
g_hClientCookie RegClientCookie("OurTestCookie""A Test Cookie for use in our Tutorial"CookieAccess_Private);
    
SetCookiePrefabMenu(g_hClientCookieCookieMenu_OnOff_Int"TestCookie"TestCookieHandler);

Just with that one line, you have sm_settings support.

We now need to define our CookieMenuHandler. The handler is pretty straight forward if you've used the menu API. If not, you may need to brush up on Menus. In this specific case, we only need to worry about the CookieMenuAction_SelectOption. We need to simply cover setting the boolean so our internal state holds consistent. The major failure of the API is when the setting changes, buffer is always "", which makes me sad, but oh well.
PHP Code:
public TestCookieHandler(clientCookieMenuAction:actionany:infoString:buffer[], maxlen)
{
    switch (
action)
    {
        case 
CookieMenuAction_DisplayOption:
        {
        }
        
        case 
CookieMenuAction_SelectOption:
        {
            
OnClientCookiesCached(client);
        }
    }

The next gotcha with ClientPrefs (and SourceMod in general) is late-load support for plugins. You should probably support late-loading, because you can get into a weird state if you don't. It's pretty easy to do so with ClientPrefs.

PHP Code:
public OnPluginStart()
{
    for (new 
MaxClients0; --i)
    {
        if (!
AreClientCookiesCached(i))
        {
            continue;
        }
        
        
OnClientCookiesCached(i);
    }

Don't worry about bots/fake clients, they too have cookies.

Last but not least, the magical OnClientCookiesCached forward. Here you're just reading a value out of a cookie. You can definitely register multiple cookies, or no cookies. OnClientCookiesCached has no defined order, and can happen before or after OnClientPostAdminCheck. If you have a protected admin cookie, you're going to need to have your own custom handler, which is beyond the scope of this tutorial (at the moment). In our case, we're reading a simple one byte value from a String. In our case, we're going to use 8 bytes.

PHP Code:
public OnClientCookiesCached(client)
{
    
decl String:sValue[8];
    
GetClientCookie(clientg_hClientCookiesValuesizeof(sValue));
    
    
g_bClientPreference[client] = (sValue[0] != '\0' && StringToInt(sValue));

Hopefully this helped. There are more, customized ways to handle Cookies. I tried to cover the basics, if you'd like me to go further into depth, just say so


EDIT: The final Plugin Looks Like:
PHP Code:
#pragma semicolon 1
#include <sourcemod>
#include <clientprefs>

new Handle:g_hClientCookie INVALID_HANDLE;
new 
bool:g_bClientPreference[MAXPLAYERS+1];

public 
OnPluginStart()
{
    
g_hClientCookie RegClientCookie("OurTestCookie""A Test Cookie for use in our Tutorial"CookieAccess_Private);
    
SetCookiePrefabMenu(g_hClientCookieCookieMenu_OnOff_Int"TestCookie"TestCookieHandler);
    for (new 
MaxClients0; --i)
    {
        if (!
AreClientCookiesCached(i))
        {
            continue;
        }
        
        
OnClientCookiesCached(i);
    }
}

public 
TestCookieHandler(clientCookieMenuAction:actionany:infoString:buffer[], maxlen)
{
    switch (
action)
    {
        case 
CookieMenuAction_DisplayOption:
        {
        }
        
        case 
CookieMenuAction_SelectOption:
        {
            
OnClientCookiesCached(client);
        }
    }
}

public 
OnClientCookiesCached(client)
{
    
decl String:sValue[8];
    
GetClientCookie(clientg_hClientCookiesValuesizeof(sValue));
    
    
g_bClientPreference[client] = (sValue[0] != '\0' && StringToInt(sValue));


Last edited by KyleS; 10-20-2013 at 15:01. Reason: Bacardi is correct.
KyleS is offline