Page 1 of 1

[TUT] Tries

Posted: Sat Sep 26, 2026 5:21 pm
by Archive-Poster
Before we start, this tutorial is for everyone that don’t know how to use tries. I will assume that you know the basics of pawn / amxx language.

Basically, tries let you store data associated to a key (string). The data usually is an integer or a string.

- All the examples will be generally simple. I just want to show you how to use tries

So, for the first example I will demonstrate how to detect if a player has a Steam ID in the black list.

Code: Select all

#include < amxmodx >

new Trie:g_tAuthIdBlackList // g means global; t means trie

public plugin_init( )
{
    register_plugin( "trie example", "1.0.1", "guipatinador" )
    
    g_tAuthIdBlackList = TrieCreate( ) // Create the trie...
    
    TrieSetCell( g_tAuthIdBlackList, "STEAM_2:3:123456", 1 )
    TrieSetCell( g_tAuthIdBlackList, "STEAM_2:3:654321", 1 )
    TrieSetCell( g_tAuthIdBlackList, "STEAM_2:3:123654", 1 )
    TrieSetCell( g_tAuthIdBlackList, "STEAM_2:3:456123", 1 )
    
    // First param = name of the trie
    // Second param = string with the Steam ID blacklisted (this is the key and it should be unique)
    // Third param = it is our integer, we will not use this
}

public plugin_end( )
{
    TrieDestroy( g_tAuthIdBlackList ) // Destroys the trie when the map changes or the server shuts down
}

public client_authorized( id )
{
    new szAuthID[ 35 ]; get_user_authid( id, szAuthID, charsmax( szAuthID ) )
    
    if( TrieKeyExists( g_tAuthIdBlackList, szAuthID ) )
    {
        // The Steam ID (key), exist in the trie
        server_cmd( "kick #%i ^"Your Steam ID is blacklisted^"", get_user_userid( id ) )
    }
    
    else
    {
        // The Steam ID is not in the trie
        // We won’t do anything because the Steam ID is not blacklisted
    }
} 
For the second example, we will associate a Steam ID with an integer, and check if the player is an ADMIN, MOD or VIP.

Code: Select all

#include < amxmodx >

new Trie:g_tAuthIdOfPeople

#define LOGFILE "steamidsofpeople.log"

// Just to be more easy...
#define ADMIN 1
#define MOD 2
#define VIP 3

public plugin_init( )
{
    register_plugin( "trie example", "1.0.2", "guipatinador" )
    
    g_tAuthIdOfPeople = TrieCreate( )
    
    TrieSetCell( g_tAuthIdOfPeople, "STEAM_2:3:123456", ADMIN ) // admin is 1
    TrieSetCell( g_tAuthIdOfPeople, "STEAM_2:3:654321", ADMIN )
    TrieSetCell( g_tAuthIdOfPeople, "STEAM_2:3:123654", MOD ) // mod is 2
    TrieSetCell( g_tAuthIdOfPeople, "STEAM_2:3:456123", VIP ) // vip is 3
}

public plugin_end( )
{
    TrieDestroy( g_tAuthIdOfPeople )
}

public client_authorized( id )
{
    new szAuthID[ 35 ]; get_user_authid( id, szAuthID, charsmax( szAuthID ) )
    
    new iNum
    
    // This will save our integer (ADMIN, MOD or VIP) in iNum, if the key is on the trie
    if( TrieGetCell( g_tAuthIdOfPeople, szAuthID, iNum ) )
    {
        switch( iNum )
        {
            case ADMIN:
                log_to_file( LOGFILE, "A Admin entered the server" )
            
            case MOD:
                log_to_file( LOGFILE, "A Mod entered the server" )
            
            case VIP:
                log_to_file( LOGFILE, "A Vip entered the server" )
        }
    }
    
    else // The key (Steam ID) is not in the trie
    {
        log_to_file( LOGFILE, "A normal player entered the server" )
    }
} 
If you understand this, you know how to work with TrieCreate, TrieDestroy, TrieGetCell, TrieSetCell and TrieKeyExists.
Now let’s work with a few more natives.
For the third example, we will use admin commands to block some nicknames.

Code: Select all

#include < amxmodx >
#include < amxmisc >

new Trie:g_tNicks

#define ADMIN_FLAG ADMIN_RCON

public plugin_init( )
{
    register_plugin( "trie example", "1.0.3", "guipatinador" )
    
    register_concmd( "amx_blocknick", "BlockNickCmd", ADMIN_FLAG, "<name to block>" )
    register_concmd( "amx_unblocknick", "UnBlockNickCmd", ADMIN_FLAG, "<name to unblock>" )
    register_concmd( "amx_removenicks", "RemoveNicksCmd", ADMIN_FLAG )
    
    g_tNicks = TrieCreate( )
}

public plugin_end( )
{
    TrieDestroy( g_tNicks )
}

public BlockNickCmd( id, level, cid )
{
    if( !cmd_access( id, level, cid, 2 ) )
        return PLUGIN_HANDLED
    
    new szName[ 32 ]    
    read_argv( 1, szName, charsmax( szName ) )
    
    // First we’ll check if the username is in the trie
    // If it is, we won't do anything
    // If it isn’t, we’ll add the nick
    
    // I will show two methods
    
    // METHOD ONE
    if( TrieKeyExists( g_tNicks, szName ) )
    {
        client_print( id, print_console, "Username already added before" )    
    }
    
    else
    {
        TrieSetCell( g_tNicks, szName, 1 )
        client_print( id, print_console, "Username added with success" )
    }
    
    /*
    // METHOD TWO
    
    new iNum
    new bool:bNameAlreadyAdded = TrieGetCell( g_tNicks, szName, iNum )
    
    if( bNameAlreadyAdded )
    {
        client_print( id, print_console, "Username already added before" )    
    }
    
    else
    {
        TrieSetCell( g_tNicks, szName, 1 )
        client_print( id, print_console, "Username added with success" )
    }
    */
    
    return PLUGIN_HANDLED
}

public UnBlockNickCmd( id, level, cid )
{
    if( !cmd_access( id, level, cid, 2 ) )
        return PLUGIN_HANDLED
    
    new szName[ 32 ]    
    read_argv( 1, szName, charsmax( szName ) )
    
    if( TrieDeleteKey( g_tNicks, szName ) )
    {
        client_print( id, print_console, "Username deleted with success" )
    }
    
    else
    {
        client_print( id, print_console, "Username already deleted before" )
    }

    /*
    // If you want to delete, without print a message just do this
    TrieDeleteKey( g_tNicks, szName )
    */
    
    return PLUGIN_HANDLED
}

public RemoveNicksCmd( id, level, cid )
{
    if( !cmd_access( id, level, cid, 1 ) )
        return PLUGIN_HANDLED
    
    TrieClear( g_tNicks ) // This one clears all the keys of the trie and the data
    client_print( id, print_console, "All the blocked usernames were deleted" )
    
    return PLUGIN_HANDLED
}

public client_authorized( id )
{
    new szName[ 32 ]; get_user_name( id, szName, charsmax( szName ) )
    
    if( TrieKeyExists( g_tNicks, szName ) )
    {
        server_cmd( "kick #%i ^"Your nickname is blocked^"", get_user_userid( id ) )
    }
    
    /*
    // Note that this will also work
    new iNum
    if( TrieGetCell( g_tNicks, szName, iNum ) )
    {
        server_cmd( "kick #%i ^"Your nickname is blocked^"", get_user_userid( id ) )
    }
    */
} 
For the TrieGetString and TrieSetString we will associate some VIP dates to Steam IDs.

Code: Select all

#include < amxmodx >
#include < amxmisc >

new Trie:g_tVipDate

new const VIPDATE[ ][ ][ ] = {
    { "STEAM_2:3:123456", "01-04-2013" },
    { "STEAM_2:3:654321", "25-02-2013" },
    { "STEAM_2:3:123654", "13-03-2013" },
    { "STEAM_2:3:456123", "09-01-2013" }
}

public plugin_init( )
{
    register_plugin( "trie example", "1.0.4", "guipatinador" )
    
    register_clcmd( "say /vipdate", "VipDate" )
    
    g_tVipDate = TrieCreate( )
    
    new i
    for( i = 0; i < sizeof VIPDATE; i++ )
    {
        TrieSetString( g_tVipDate, VIPDATE[ i ][ 0 ], VIPDATE[ i ][ 1 ] )
        // First param = name of the trie
        // Second param = Steam ID (key)
        // Third param = date when the vip ends (string)
    }
}

public plugin_end( )
{
    TrieDestroy( g_tVipDate )
}

public VipDate( id )
{
    new szAuthID[ 35 ]; get_user_authid( id, szAuthID, charsmax( szAuthID ) )
    
    // First we’ll check if the Steam ID of the player is in the trie
    new szVipDate[ 12 ]
    if( TrieGetString( g_tVipDate, szAuthID, szVipDate, charsmax( szVipDate ) ) )
    {
        // The user is vip because the Steam ID is one of the keys of the trie
        client_print( id, print_chat, "Your VIP will end in: %s", szVipDate )
    }
    
    else
    {
        // User is not vip
        client_print( id, print_chat, "You are not VIP" )
    }
} 
And for the last... let's work with TrieSetArray and TrieGetArray.
This plugin read the stats of a file and save it by Steam ID.

Code: Select all

#include < amxmodx >
#include < amxmisc >

new Trie:g_StatsOfPlayers

new g_szAuthID[ 33 ][ 35 ] // This will save the Steam ID of any player

enum _:Stats {
    SteamID[ 35 ],
    Kills,
    Deaths,
    KillsKnife,
    KillsHE
}
// Those are the stats we want to save. Note that the Steam ID is a string, the rest are integers

public plugin_init( )
{
    register_plugin( "trie example", "1.0.5", "guipatinador" )
    
    register_clcmd( "say /mystats", "PrintStats" ) // We will use this command to see the stats
    
    g_StatsOfPlayers = TrieCreate( )
    ReadStatsFromFile( ) // Reads stats from the file when the server starts or the map changes    
}

public plugin_end( )
{
    TrieDestroy( g_StatsOfPlayers )
}

public ReadStatsFromFile( )
{
    new szFilePath[ 128 ]
    get_configsdir( szFilePath, charsmax( szFilePath ) )
    add( szFilePath, charsmax( szFilePath ), "/stats_of_players.ini" ) // File where the stats are saved
    
    new f = fopen( szFilePath, "rt" )
    
    if( !f )
    {
        new szMessage[ 256 ]
        formatex( szMessage, charsmax( szMessage ), "Unable to open %s", szFilePath )
        set_fail_state( szMessage )
    }
    
    new szDataFromFile[ 128 ]
    new Data[ Stats ]
    new szKills[ 5 ]
    new szDeaths[ 5 ]
    new szKnife[ 5 ]
    new szHE[ 5 ]
    
    while( !feof( f ) ) // Opens the file
    {
        fgets( f, szDataFromFile, charsmax( szDataFromFile ) )
        
        if( !szDataFromFile[ 0 ] || szDataFromFile[ 0 ] == ';' || szDataFromFile[ 0 ] == '/' && szDataFromFile[ 1 ] == '/' )
            continue
        
        trim( szDataFromFile )
        parse( szDataFromFile,
        Data[ SteamID ], charsmax( Data[ SteamID ] ), // This is our key
        szKills, charsmax( szKills ),
        szDeaths, charsmax( szDeaths ),
        szKnife, charsmax( szKnife ),
        szHE, charsmax( szHE ) )
        
        // All those things are strings. Let's convert to integers
        Data[ Kills ] = str_to_num( szKills )
        Data[ Deaths ] = str_to_num( szDeaths )
        Data[ KillsKnife ] = str_to_num( szKnife )
        Data[ KillsHE ] = str_to_num( szHE )
        
        // This will save the Steam ID and the stats in the trie
        TrieSetArray( g_StatsOfPlayers, Data[ SteamID ], Data, sizeof Data )
        // First param = name of the trie
        // Second param = key (string) (in this case it's the Steam ID)
        // Third param = array that contains the stats
        // Fourth param = size of the array (note that 'sizeof Data - 1' will not work)
    }
    
    fclose( f ) // Closes the file
}

public client_authorized( id )
{
    get_user_authid( id, g_szAuthID[ id ], charsmax( g_szAuthID[ ] ) )
}

public PrintStats( id )
{
    new Data[ Stats ]
    
    if( TrieGetArray( g_StatsOfPlayers, g_szAuthID[ id ], Data, sizeof Data ) )
    {
        // The params are similar to TrieSetString
        // First = name of the array
        // Second = key. g_szAuthID[ id ] contains the Steam ID of the player that types /mystats
        // Third = array to retrieve the data
        // Fourth = size
        
        client_print( id, print_chat, "Steam ID: %s | Kills: %i | Deaths: %i | Knife: %i | HE: %i", Data[ SteamID ], Data[ Kills ], Data[ Deaths ], Data[ KillsKnife ], Data[ KillsHE ] )
    }
    
    //  If the Steam ID doesn't exist in the file, it is better to print another message
    else
    {
        client_print( id, print_chat, "Your Steam ID do not have stats saved" )
    }
} 
That's all