AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Snippets and Tutorials (https://forums.alliedmods.net/forumdisplay.php?f=112)
-   -   [Tutorial] ClientPrefs (https://forums.alliedmods.net/showthread.php?t=228244)

KyleS 10-16-2013 13:20

[Tutorial] ClientPrefs
 
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 rukia


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));



TnTSCS 10-16-2013 16:50

Re: [Tutorial] ClientPrefs
 
Thank you... learned something new about SetCookiePrefabMenu

shavit 10-18-2013 15:08

Re: [Tutorial] ClientPrefs
 
Thanks :)
The way you loop through players is unique :O

Drixevel 10-19-2013 15:08

Re: [Tutorial] ClientPrefs
 
Appreciate the example! Any quick leads on how to have a specific message pop up when the client select on or off in the menu? Can't seem to find much after searching a bit.

KyleS 10-19-2013 15:24

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by r3dw3r3w0lf (Post 2050894)
Appreciate the example! Any quick leads on how to have a specific message pop up when the client select on or off in the menu? Can't seem to find much after searching a bit.

In MenuSelect you can print a message, or do whatever you want.

Bacardi 10-20-2013 14:59

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by KyleS (Post 2049566)
...
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_settings. This can provoke undefined behaviour, which can wreck you depending on your sanitation. In our case, we're making our cookie Private.
...

You mean command sm_cookies ?? :bee:
Spoiler

KyleS 10-20-2013 15:01

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Bacardi (Post 2051248)
You mean command sm_cookies ?? :bee:

Correct!

Arkarr 11-26-2013 08:04

Re: [Tutorial] ClientPrefs
 
I don't get it... Why my cookie are always lost when I reload my plugin and players are in game ?

I can restart my server and player have their correct cookie [OK]
I can restart map and player have their correct cookie [OK]
I reload my plugin and players are in game, they lost their cookies !!! [X]

NOTE:
I didn't understand everything you wrote, so I'm not sure if you speak about this...

Original thread

friagram 11-26-2013 08:28

Re: [Tutorial] ClientPrefs
 
OnClientCookiesCached will only fire when they connect. If you yeload the plugin, you need to loop over the players and do AreClientCookiesCached in OnPluginStart... Look at the example.

Arkarr 11-26-2013 08:41

Re: [Tutorial] ClientPrefs
 
Yep, I did, but cookies still lost.

KyleS 11-26-2013 10:50

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Arkarr (Post 2065549)
Yep, I did, but cookies still lost.

Can you show your more up to date code?

Arkarr 11-26-2013 12:31

Re: [Tutorial] ClientPrefs
 
(plugin bugged. removed.)

RedSword 11-26-2013 17:19

Re: [Tutorial] ClientPrefs
 
Because when the plugins load, it doesn't get the clients' cookie. What it does is check that the clients have their cookie loaded (but not in your plugin's memory) using OnClientCookiesCached ==> why even do this ?.

Try to simply run OnClientPutInServer() for every InGame client OnPluginStart() rather than L33-37.


EDIT nvm; misread code; didn't notice at 2 places you were getting client cookies

KyleS 11-26-2013 19:04

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by RedSword (Post 2065770)
Because when the plugins load, it doesn't get the clients' cookie. What it does is check that the clients have their cookie loaded (but not in your plugin's memory) using OnClientCookiesCached ==> why even do this ?.

Try to simply run OnClientPutInServer() for every InGame client OnPluginStart() rather than L33-37.

:?

Quote:

Originally Posted by Arkarr (Post 2065661)
Here is it.

It looks like you've preserved the load code in OnClientPutInServer, which is horribly incorrect. It also looks like you only store the client data when they disconnect, which would reproduce the plugin reload bug. If you're worried about the overhead of updating your cookie data over and over again in your timer, you can use OnPluginEnd; but that's a thing.


EDIT: Unrelated to ClientPrefs, you should return Plugin_Stop in your Timer, otherwise you're leaking timers. Your plugin will eventually auto-unload after enough clients connect, from running out of Handles. You should also pass the flag TIMER_FLAG_NO_MAPCHANGE, as all of your timers should die then anyways.

friagram 11-27-2013 03:57

Re: [Tutorial] ClientPrefs
 
...
Ok

Mabye you are writing over them. Are you by chance using onclientputinserver to initialize stuff! Because careclientcookiescached can happen before or after that. You may want to use onclientdonnected instead, to pre-initialize vars while you wait for cookies to load in. It may be that they are loading, but you are overwriting them with junk data later.

Edit:didnt see this second page, but looks like is still probly relevant.

Arkarr 11-27-2013 12:44

Re: [Tutorial] ClientPrefs
 
Oka, thanks for help. I updated this plugin.

ddhoward 01-16-2014 16:34

Re: [Tutorial] ClientPrefs
 
Does clientprefs keep track of which plugin assigned which cookie? Or should plugin authors be careful to ensure that the cookie name is unique?

Root_ 01-16-2014 17:33

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by ddhoward (Post 2086911)
Does clientprefs keep track of which plugin assigned which cookie?

Not really.
Quote:

Originally Posted by ddhoward (Post 2086911)
Or should plugin authors be careful to ensure that the cookie name is unique?

This. For example, in some of my plugins I retrieve quake sounds client preferences this way.
Code:
new bool:QPreferences[MAXPLAYERS + 1]; public OnClientCookiesCached(client) {     if (AreClientCookiesCached(client))     {         decl String:value[5], Handle:preferences;         preferences = FindClientCookie("Quake Sound Pref");         GetClientCookie(client, preferences, value, sizeof(value));         CloseHandle(preferences);         QPreferences[client] = bool:!StringToInt(value);     } }

ddhoward 01-18-2014 19:06

Re: [Tutorial] ClientPrefs
 
Good to know. I recently saw a plugin (not on this site) that named its cookie "cookie." Was wondering if this might conflict with any other plugin with the same non-descriptive cookie name. Now I know it will lol

Mitchell 01-20-2014 10:47

Re: [Tutorial] ClientPrefs
 
Should also mention that cookies should not be used to store stats, as in time played. It makes it nearly impossible to reset all the players in that cookie back to 0, etc. Should only be used as preferences.

Root_ 01-20-2014 11:05

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Mitchell (Post 2088534)
Should also mention that cookies should not be used to store stats, as in time played. It makes it nearly impossible to reset all the players in that cookie back to 0, etc. Should only be used as preferences.

But why? Clientprefs is fine to store simple stats with time stamps. It's also have simply database structure, so you can do simply queries (see this).

Powerlord 01-20-2014 12:08

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Root_ (Post 2088556)
But why? Clientprefs is fine to store simple stats with time stamps. It's also have simply database structure, so you can do simply queries (see this).

If you're manually running queries against the ClientPrefs database, you're (by definition) doing it wrong.

(Actually, your plugin already covers the one exception to that rule)

Chdata 01-23-2014 14:24

Re: [Tutorial] ClientPrefs
 
I tend to avoid making cookies for FakeClients because well... they'll never use them.

Arkarr 04-03-2014 15:22

Re: [Tutorial] ClientPrefs
 
Well, my cookies are restored when I'm on my server and I manually reload the plugin (sm plugins reload XXXX or sm plugins unload XXXX + sm plugins load XXXX) BUT are not restored when I join the server.

NOTE: I don't think it's a "late load", because I have put the GetClientCookie() code when OnClientCookiesCached(). Also, my cookies are saved when player leave server (they are saved OnPluginEnd() too.).

Can someone explain me what I have done wrong ? Should I give you my code ?

ddhoward 04-03-2014 15:27

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Arkarr (Post 2119721)
Should I give you my code ?

Yes.

KyleS 04-03-2014 21:52

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Arkarr (Post 2119721)
NOTE: I don't think it's a "late load", because I have put the GetClientCookie() code when OnClientCookiesCached().

Can you confirm this forward is called? To my recollection, it is not called on late load.

Arkarr 04-04-2014 07:06

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by ddhoward (Post 2119723)
Yes.

Here : https://forums.alliedmods.net/showthread.php?t=236670
I already made a post for this, but it seems none saw it, or was able to fix it (or just wasn't interested).


Quote:

Originally Posted by KyleS (Post 2119874)
Can you confirm this forward is called? To my recollection, it is not called on late load.

I don't get it... (damn, my english restrained me again)
Not sure if it's what you are asking for but, yes, it's called. I have print some debug text on OnClientCookiesCached() and GetClientCookies work for my other cookies.

EDIT: Really ? No one wanna give me a hint ? Damn it !

Bacardi 04-05-2014 15:14

Re: [Tutorial] ClientPrefs
 
I think this example have little mistake,
in cookie menu handler
- CookieMenuAction_SelectOption action happens when you pick option "TestCookie" from cookie menu.
So, you are picking old cookie value on OnClientCookiesCached(),
So so, cookie OnOff integer value get change and saved AFTER you have choose menu option "On/Off".
So so so, you need choose twice "on/off" option from menu to update rigth value in global variable g_bClientPreference[client].

how ever, I do have SourceMod Version: 1.5.2, has this feature changed for now ??

*edit
could problem be SetCookiePrefabMenu ? bad timing

8guawong 02-07-2015 01:25

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Bacardi (Post 2120596)
So so so, you need choose twice "on/off" option from menu to update rigth value in global variable g_bClientPreference[client].

hi i notice this problem as well!!

Chdata 02-07-2015 01:29

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Arkarr (Post 2119721)
Well, my cookies are restored when I'm on my server and I manually reload the plugin (sm plugins reload XXXX or sm plugins unload XXXX + sm plugins load XXXX) BUT are not restored when I join the server.

NOTE: I don't think it's a "late load", because I have put the GetClientCookie() code when OnClientCookiesCached(). Also, my cookies are saved when player leave server (they are saved OnPluginEnd() too.).

Can someone explain me what I have done wrong ? Should I give you my code ?

Like the tutorial says, did you

OnPluginStart -> For 1 < i <= MaxClients > IsClientInGame(i) && AreClientCookiesCached(i) > OnClientCookiesCached(i)

Here's another 'gotcha' the plugin tutorial forgot.

BAD
PHP Code:

#define COOKIES_NOTLOADEDYET    -1
#define COOKIES_ARELOADEDNOW    1

static g_iCookieSetting[MAXPLAYERS+1];

public 
OnClientPostAdminCheck(iClient// OnClientPutInServer(iClient)
{
    
g_iCookieSetting[iClient] = COOKIE_NOTLOADEDYET;
}

public 
OnClientCookiesCached(iClient)
{
    
g_iCookieSetting[iClient] = COOKIES_ARELOADEDNOW;


Every time OnClientCookiesCached fires before OnClientPostAdminCheck, you are now saying their cookies were never loaded, when they were. You just undid your own logic for them.

GOOD
PHP Code:

#define COOKIES_NOTLOADEDYET    -1
#define COOKIES_ARELOADEDNOW    1

static g_iCookieSetting[MAXPLAYERS+1];

public 
OnClientPostAdminCheck(iClient)
{
    if (!
AreClientCookiesCached(iClient))
    {
        
g_iCookieSetting[iClient] = COOKIE_NOTLOADEDYET;
    }
}


public 
OnClientCookiesCached(iClient)
{
    
g_iCookieSetting[iClient] = COOKIES_ARELOADEDNOW;



ddhoward 08-01-2016 17:28

Re: [Tutorial] ClientPrefs
 
It should be documented somewhere that the maximum length of a cookie is 100 characters. I'd like to see it in the API documentation, but here would be good also.

Papero 10-07-2017 12:27

Re: [Tutorial] ClientPrefs
 
Sorry, but

is this part optional?

Code:

g_bClientPreference[client] = (sValue[0] != '\0' && StringToInt(sValue));
or I need it to make the cookies work properly?

Addicted. 10-07-2017 13:24

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Papero (Post 2553077)
Sorry, but

is this part optional?

Code:

g_bClientPreference[client] = (sValue[0] != '\0' && StringToInt(sValue));
or I need it to make the cookies work properly?

All that does is make sure the string isn't empty

Papero 10-07-2017 13:35

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Addicted. (Post 2553083)
All that does is make sure the string isn't empty

Yeah, but if the string is empty StringToInt will return anyway 0/false
Doc:
Quote:

Integer conversion of string, or 0 on failure

Addicted. 10-07-2017 13:36

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by Papero (Post 2553085)
Yeah, but if the string is empty StringToInt will return anyway 0/false(From doc: Integer conversion of string, or 0 on failure.)

So there is your answer :bacon!:

eyal282 07-10-2018 04:19

Re: [Tutorial] ClientPrefs
 
Why do you use Private cookies when it clearly looks like you're trying to make the cookie available for all? Why not Public cookie?

ddhoward 07-10-2018 05:29

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by eyal282 (Post 2602536)
Why do you use Private cookies when it clearly looks like you're trying to make the cookie available for all? Why not Public cookie?

https://sm.alliedmods.net/new-api/cl...s/CookieAccess

This selection configures whether the user can change the cookie value manually, or whether they can even see the current value via sm_cookies.

CookieAccess_Public means that the user can insert any string into the cookie by using sm_cookies.

CookieAccess_Restricted means that the user can use sm_cookies to view the raw value of the cookie as stored in the database, but cannot change it.

CookieAccess_Private means that the cookie will not show up in the sm_cookies list, and cannot be viewed or changed. The value of the cookie is instead exclusively changed through other means, such as a menu or something.

The vast majority of cookies will be private. There is almost no reason to do anything else. Any cookie that takes a user-defined string would probably be better off having a separate, dedicated command to changing it and reading it. I really can't think of any valid reason to use anything other than CookieAccess_Private.

eyal282 07-11-2018 05:28

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by ddhoward (Post 2602556)
https://sm.alliedmods.net/new-api/cl...s/CookieAccess

This selection configures whether the user can change the cookie value manually, or whether they can even see the current value via sm_cookies.

CookieAccess_Public means that the user can insert any string into the cookie by using sm_cookies.

CookieAccess_Restricted means that the user can use sm_cookies to view the raw value of the cookie as stored in the database, but cannot change it.

CookieAccess_Private means that the cookie will not show up in the sm_cookies list, and cannot be viewed or changed. The value of the cookie is instead exclusively changed through other means, such as a menu or something.

The vast majority of cookies will be private. There is almost no reason to do anything else. Any cookie that takes a user-defined string would probably be better off having a separate, dedicated command to changing it and reading it. I really can't think of any valid reason to use anything other than CookieAccess_Private.

I am using cookies to determine which guns you'll have in a gun menu. If you found the cookie in sm_cookies, you should be able to change one gun with a keybind without returning to menu :)

ddhoward 07-16-2018 17:58

Re: [Tutorial] ClientPrefs
 
Quote:

Originally Posted by eyal282 (Post 2602873)
I am using cookies to determine which guns you'll have in a gun menu. If you found the cookie in sm_cookies, you should be able to change one gun with a keybind without returning to menu :)

This has nothing to do with a Cookie's CookieAccess setting. This can be accomplished with any of the three settings. You'd just use a command other than sm_cookies.

eyal282 08-08-2018 17:34

Re: [Tutorial] ClientPrefs
 
https://forums.alliedmods.net/showthread.php?t=309777

General thoughts about using ClientPrefs for saving EXP or score a.k.a progress in a server?


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

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