Raised This Month: $ Target: $400
 0% 

Iterating over players to get info


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
supergreg
Senior Member
Join Date: Jul 2007
Location: Oslo, Norway
Old 06-22-2009 , 18:22   Iterating over players to get info
Reply With Quote #1

I am having some problems iterating over the players to get their rates.
First I was just using this:
PHP Code:
for ( 1<= 32i++ )
{
    if ( !
is_user_bot(i) && is_user_connected(i) )
    {
        
// work my magic
    
}

Then I read in a thread here that looping over players this way may lead to unreliable results, like if a client disconnects and leaves an index declared but pointing to "null"/nonexisting player.
Question 1. Doesn't the user_connected() check eliminate events where that could be a real problem?

So I copied some code (thanks) from that thread which I cannot find again, and had:
PHP Code:
new players[32], numi;
get_players(playersnum);
for ( 
1<= numi++ )
{
    new 
player players[i];
    if ( !
is_user_bot(player) && is_user_connected(player) )
    {
        
// work my magic
    
}
    else
    {
        
// woops.
        // dumped values: i = 1. player = 0.
    
}

This was pretty much copy+paste, but gave me problems. It triggered the else-clause. I dumped my variables as you can see in the comments. I tried this while I was alone on the server, I had client id 1, and it tried to query client 0.
Question 2. Am I just being a moron here, did I implement the code snippet incorrectly, or was that iterating-players snippet I copied really for other use cases? What would be different?

Current code:
PHP Code:
new lineOut[BIG_BUFF 1];
new 
players[MAX_PLAYERS], numi;
get_players(playersnum);
for ( 
1<= numi++ )
{
    if ( !
is_user_bot(i) && is_user_connected(i) )
    {
        new 
tempNick[MAX_NICKLENGTH 1];
        
copy(tempNickMAX_NICKLENGTHg_szNicks[i]);
        
replace_all(tempNickMAX_NICKLENGTH"<""");
        
replace_all(tempNickMAX_NICKLENGTH">""");

        
get_user_info(i"rate"g_iRates[i], MAX_RATE);
        
get_user_info(i"cl_updaterate"g_iClUpdateRates[i], MAX_RATE);
        
get_user_info(i"cl_dlmax"g_iDlMax[i], MAX_RATE);

        
query_client_cvar(i"cl_cmdrate""cvarCheck");
        
query_client_cvar(i"ex_interp""cvarCheck");
        
//query_client_cvar(player, "cl_rate", "cvarCheck");

        
formatex(lineOutBIG_BUFF"rate: %s^tcl_updaterate: %s^tcl_cmdrate: %s^tex_interp: %s^tcl_dlmax: %s^t^t| %s"g_iRates[i], g_iClUpdateRates[i], g_iClCmdRates[i], g_iInterp[i], g_iDlMax[i], tempNick);
        
client_print(idprint_consolelineOut);          
    }
    else
    {
        
server_print("reported as bot/not connected. Stored nick %s. i=%i. player=%i."g_szNicks[i], iplayer);
    }

I am currently using this, it seems to work fine in my tests (1-3 players). I am wondering though if this code falls victim of Question 1. The only problem I currently have with this code is that sometimes the values which needs to be ran through query_client.. (like interp) is not reported on the first run of this function, but on all consecutive runs.
Question 3. Why are interp+cmdrate values blank after the first run, what can I do to avoid that?
Question 4. Why does querying for "cl_rate" always yield errors or bad response?

Sample output for the last code:
Code:
] amx_listrates 
~~~~~~~~~~~~~~~~~~~~~~~ Listing player rates ~~~~~~~~~~~~~~~~~~~~~~~
rate: 25000    cl_updaterate: 80    cl_cmdrate:     ex_interp:     cl_dlmax: 100    cc
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
] amx_listrates  
~~~~~~~~~~~~~~~~~~~~~~~ Listing player rates ~~~~~~~~~~~~~~~~~~~~~~~
rate: 25000    cl_updaterate: 80    cl_cmdrate: 76    ex_interp: 0.013    cl_dlmax: 100    cc
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sorry for such a lengthy post on this, hopefully it is generic enough to be of use to many others.

Last edited by supergreg; 06-22-2009 at 18:25. Reason: add sample output
supergreg is offline
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 06-22-2009 , 23:36   Re: Iterating over players to get info
Reply With Quote #2

1. If you are going to iterate from 1 to X for players then you should use a global var that holds get_maxplayers() instead of using 32. If you use 32 and the server is set to hold a max of, lets say, 20 players then anything above 20 can be used as an entity index..not player id which would result in errors depending on what you're doing.

PHP Code:
new g_iMaxPlayers;

g_iMaxPlayers get_maxplayers()

for ( new 
<= g_iMaxPlayers i++ )
     if ( 
is_user_connected) )
          
//connected player 
2. If you use get_players() then you do not need to do an is_user_connected() check since that will return only connected players. The usage is also incorrect because the for-loop index should go from 0 to num-1 and not 1 to <= num. The player variable should also not be declared within the loop.
PHP Code:
new players[32], numplayer;
get_players(playersnum);
for ( new 
num i++ )
{
    
player players[i];


Your code is totally wrong. get_players() puts player id's into the array. You must then iterate from 0 to num-1 to retrieve the player-id's from the array.

Here's a snippet of your code with corrections made
PHP Code:
//....
//Though passing flags to get_players() is no longer supported, you could
//pass a flag that will ignore bots: get_players( players , num , "c" );
//I've never ran into any bugs with the "c" flag w\ get_players().
//Doing this will remove the need to check if bot within the loop
get_playersplayers num );
for ( 
0numi++ )
{
     
//player id's are stored in the players array when you call get_players.
    
player players[i];

    
//All of your 'i's should be 'player'. 
    
if ( !is_user_bot(player) )
    {
          
//.... 
3 & 4. Show your cvarCheck if the above didn't solve your problem.
__________________

Last edited by Bugsy; 06-22-2009 at 23:50.
Bugsy is offline
supergreg
Senior Member
Join Date: Jul 2007
Location: Oslo, Norway
Old 06-23-2009 , 08:25   Re: Iterating over players to get info
Reply With Quote #3

Quote:
Your code is totally wrong.
Always reassuring

Thanks for such a detailed reply. I have now split this function into a separate plugin, pasted at the end of the post.

I am still having the same issues:
1. the values from query_client.. does not show on first run
2. Have yet to get cl_rate to work
Code:
Client console:
] amx_listrates
~~~~~~~~~~~~~~~~~~~~~~~ Listing player rates ~~~~~~~~~~~~~~~~~~~~~~~
rate: 25000    cl_updaterate: 76    cl_cmdrate:     ex_interp:     cl_dlmax: 100        | player
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
] amx_listrates 
~~~~~~~~~~~~~~~~~~~~~~~ Listing player rates ~~~~~~~~~~~~~~~~~~~~~~~
rate: 25000    cl_updaterate: 76    cl_cmdrate: 76    ex_interp: 0.013    cl_dlmax: 100        | player
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Server console:
[ag_connection] player (STEAM_0:0:123) has blocked his cl_rate cvar
Current code:
Put a couple of questions in the comments, would appreciate pointers. Am also wondering how I am supposed to know how big buffers I need for things such as the lineOut var (the line printed to console), or the responses from query_client_cvar - where/how would I find out? Ref. my poorly thought out define section..
PHP Code:
#include <amxmodx>
#include <amxmisc>

#define VERSION "1.0"
#define AUTHOR "supergreg"
#define IS_ADMIN        ADMIN_BAN
#define MAX_PLAYERS        32
#define MAX_NICKLENGTH        31
#define MAX_RATE        35
#define MED_BUFF        191
#define BIG_BUFF        256

// Global vars
new g_iDebugLevel 4;
new const 
g_szPluginName[] = "ag_connection";
new 
g_szNicks[MAX_PLAYERS][MAX_NICKLENGTH 1];
new 
g_iRates[MAX_PLAYERS][MAX_NICKLENGTH 1];
new 
g_iClRates[MAX_PLAYERS][MAX_NICKLENGTH 1];
new 
g_iClCmdRates[MAX_PLAYERS][MAX_NICKLENGTH 1];
new 
g_iClUpdateRates[MAX_PLAYERS][MAX_NICKLENGTH 1];
new 
g_iInterp[MAX_PLAYERS][MAX_NICKLENGTH 1];
new 
g_iDlMax[MAX_PLAYERS][MAX_NICKLENGTH 1];

public 
plugin_init()
{
    
register_plugin(g_szPluginNameVERSIONAUTHOR);
    
register_clcmd("amx_listrates""listRates"IS_ADMIN"List player rates in console");
}

public 
listRates(id)
{
    if (!
is_user_connected(id) || is_user_bot(id) || !(get_user_flags(id) & IS_ADMIN)) { return PLUGIN_HANDLED; }

    
client_print(idprint_console"~~~~~~~~~~~~~~~~~~~~~~~ Listing player rates ~~~~~~~~~~~~~~~~~~~~~~~");
    if(
g_iDebugLevel >= 3)
    {
        
server_print("~~~~~~~~~~~~~~~~~~~~~~~ Listing player rates ~~~~~~~~~~~~~~~~~~~~~~~");
    }
    new 
lineOut[BIG_BUFF 1];
    new 
players[MAX_PLAYERS], numi;
    
get_players(playersnum);

    for ( 
0numi++ )
    {
        new 
player players[i];
        if ( !
is_user_bot(player) )
        {
            new 
tempNick[MAX_NICKLENGTH 1];
            if(
strlen(g_szNicks[player]) > 1)
            {
                
copy(tempNickMAX_NICKLENGTHg_szNicks[player]);
            }
            else
            {
                
get_user_info(player"name"tempNickMAX_NICKLENGTH);
                
replace_all(tempNickMAX_NICKLENGTH"<""");
                
replace_all(tempNickMAX_NICKLENGTH">""");
                
copy(g_szNicks[player], MAX_NICKLENGTHtempNick);
            }

            
get_user_info(player"rate"g_iRates[player], MAX_RATE);
            
get_user_info(player"cl_updaterate"g_iClUpdateRates[player], MAX_RATE);
            
get_user_info(player"cl_dlmax"g_iDlMax[player], MAX_RATE);

            
query_client_cvar(player"cl_cmdrate""cvarCheck");
            
query_client_cvar(player"ex_interp""cvarCheck");
            
query_client_cvar(player"cl_rate""cvarCheck");

            
formatex(lineOutBIG_BUFF"rate: %s^tcl_rate: %s^tcl_updaterate: %s^tcl_cmdrate: %s^tex_interp: %s^tcl_dlmax: %s^t^t| %s"g_iRates[player], g_iClRates[player], g_iClUpdateRates[player], g_iClCmdRates[player], g_iInterp[player], g_iDlMax[player], tempNick);
            
client_print(idprint_consolelineOut);
            if(
g_iDebugLevel >= 3)
            {
                
server_print(lineOut);
            }            
        }
        else
        {
            
server_print("reported as bot/not connected. Stored nick %s. i=%i. player=%i."g_szNicks[player], iplayer);
        }
    }
    
client_print(idprint_console"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    if(
g_iDebugLevel >= 3)
    {
        
server_print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    }

    return 
PLUGIN_HANDLED;
}


public 
cvarCheck(index, const cvar_name[], const cvar_value[])
{
    if(
equali(cvar_value"Bad CVAR request") && g_iDebugLevel >= 1)
    {
        new 
msg[MED_BUFF 1];
        new 
auth[MAX_NICKLENGTH 1];
        
get_user_authid(indexauthMAX_NICKLENGTH);
        
formatex(msgMED_BUFF"[%s] %s (%s) has blocked his %s cvar"g_szPluginNameg_szNicks[index], authcvar_name);
        
log_amx(msg);
        
server_print(msg);
    }
    else
    {
        
// Can Pawn handle a switch here? case "cl_rate" etc?
        
if(equali(cvar_name"cl_rate"))
        {
            
// formatex(g_iClRates[index], strlen(cvar_value), "%s", cvar_value);
            // ^ also working, but is it copy or format which is the preferable one in this case?
            // If not using format, should one do str_to_num() to assure one has the correct type of data?
            
copy(g_iClRates[index], MAX_NICKLENGTHcvar_value);
        }
        else if(
equali(cvar_name"cl_cmdrate"))
        {
            
copy(g_iClCmdRates[index], MAX_NICKLENGTHcvar_value);
        }
        else if(
equali(cvar_name"ex_interp"))
        {
            
copy(g_iInterp[index], MAX_NICKLENGTHcvar_value);
        }
        if(
g_iDebugLevel >= 4)
        {
            
server_print("Queried client %i for %s, result: %s."indexcvar_namecvar_value);
        }
    }
    return 
true;

supergreg is offline
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 06-23-2009 , 09:24   Re: Iterating over players to get info
Reply With Quote #4

1. the values from query_client.. does not show on first run

This is because the way query_client_cvar works. It does the
request and then the response is handled by a separate function.
In this case, the response has not yet been received from the client
at the time that you print to the user. What you can do is make
the user notification occur at the final requested cvar value in your
handler function. ie, suppose you request cl_rate, cl_updaterate and ex_interp.
When ex_interp is received in cvarCheck function, print all your cvar values.

2. Have yet to get cl_rate to work

IIRC, this one may not be retrievable. Don't quote me on it but I
do recall there being some client values that are not retrievable.

Am also wondering how I am supposed to know how big buffers I need for things such as the lineOut var (the line printed to console), or the responses from query_client_cvar - where/how would I find out?

You need to know the maximum possible string that will be stored in
the buffer and use that + 1. This is why everyone uses 31 or 32 for
strings to hold user names.

Can Pawn handle a switch here? case "cl_rate" etc?

No, you cannot switch a string value

formatex(g_iClRates[index], strlen(cvar_value), "%s", cvar_value);
^ also working, but is it copy or format which is the preferable one in this case?
If not using format, should one do str_to_num() to assure one has the correct type of data?


Any time you are working with an integer value it is always better to
use an integer type. As you know, pawn does not have any data types
so you will just be using a standard array. [not 2-dimension as you would
for string]

g_iClRates[index] = str_to_num( cvar_value );

and you have to redimension the array

new g_iClRates[MAX_PLAYERS]
__________________

Last edited by Bugsy; 06-23-2009 at 09:27.
Bugsy is offline
supergreg
Senior Member
Join Date: Jul 2007
Location: Oslo, Norway
Old 07-11-2009 , 10:58   Re: Iterating over players to get info
Reply With Quote #5

Thanks for all your help, appreciated!

Quote:
Originally Posted by Bugsy View Post
Am also wondering how I am supposed to know how big buffers I need for things such as the lineOut var (the line printed to console), or the responses from query_client_cvar - where/how would I find out?

You need to know the maximum possible string that will be stored in
the buffer and use that + 1. This is why everyone uses 31 or 32 for
strings to hold user names.
I knew as much, my question should have been more specific; how do I find the maximum length a native can return? My own strings are not a problem, but not certain about for instance query_client_cvar or other natives that may return strings. Is the return length mentioned in the funcwiki or elsewhere, or do I need to dig in the amxx core? Where is it documented that usernames will contain max 32 chars?
supergreg is offline
Bugsy
AMX Mod X Moderator
Join Date: Feb 2005
Location: NJ, USA
Old 07-13-2009 , 10:26   Re: Iterating over players to get info
Reply With Quote #6

Quote:
Originally Posted by supergreg View Post
Thanks for all your help, appreciated!



I knew as much, my question should have been more specific; how do I find the maximum length a native can return? My own strings are not a problem, but not certain about for instance query_client_cvar or other natives that may return strings. Is the return length mentioned in the funcwiki or elsewhere, or do I need to dig in the amxx core? Where is it documented that usernames will contain max 32 chars?
I don't know if there are any limiters in place that will restrict the size of a returned string. I don't imagine there is since returning an array is really only returning the memory address of the first array element.

Is there something you are having a problem with or are you just curious?
__________________
Bugsy is offline
supergreg
Senior Member
Join Date: Jul 2007
Location: Oslo, Norway
Old 07-14-2009 , 12:26   Re: Iterating over players to get info
Reply With Quote #7

Simply trying to learn best practices, so I was wondering if I had missed a field in the function/native reference which indicate the expected return length for the natives that returns strings or multiple values. I see return values are stated for the natives that return a value from an enumerated set, but what about the others? For now I just get on by test/fail.

I think this is kinda related to my previous Q; Where is the nick length limit of ~32 chars set? Somewhere in Half-Life? Maybe a link to some HL docs would answer my questions.
Rephrased: If you did not already know for a fact the max length for nicks is 32, how would you proceed to find out?

English isn't my first language, so this is as clear as I can define my problem. Thanks for hanging in there
supergreg is offline
Reply



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 15:44.


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