nVault Top 15
Over the years I've seen many scripters ask how to create a top 15 for data that is stored in nVault. Since the nVault module has no built-in sorting functionality this is not possible without the use of nVault Utility. While not required to complete this, I am providing an example using the nVault Array include since I think this is a cleaner approach than storing all data as a string and then parsing components of the string for the calculation.
High-level explanation for how nVault Utility works:
nVault Utility uses the file natives to read data from the vault file in binary format. All data is stored at an offset within the file, with 0 being the position of the first record, the next record is positioned based off of the size of the previous record, etc. This offset will be used as a reference to the player data to make this top 15 possible.
Steps for creating a Top 15:
- Read all records in the vault file and store the player-data offset and XP value to a 2-dimension array.
- Sort this array using the XP value as the sort value.
- Loop through this sorted array, retrieving the player data from vault at the offset stored in the array.
- Create the top 15 based off of this since it is in order of XP as a result of the sort.
You must delete your vault file or prune all data before implementing this since nvault_read_array() will fail if you try to read data that was not stored using nvault_set_array().
Plugin chat commands:
- top15 - Display the top 15.
- loadtestdata - Loads 100 test records to the vault in 'Player_# [XP]' format. The players name includes their stored XP value so you can confirm it is sorting accurately.
//Allocate additional memory to plugin to prevent stack error
#pragma dynamic 16384
new const Version = "0.1";
new const VaultName = "XP_Top15_Example";
//This determines the max number of players that will be included in your top 15 calculation. It is best to keep this at a
//value <= the max players that you expect to have data saved in the vault. If the number of player data saved exceeds this
//value then your top 15 will not be accurate since some players will be left out.
const Max_Player_Support = 3000;
//Components of data that will be saved for each player.
PlayerName[ 32 ],
new pdData[ MAX_PLAYERS + 1 ][ PlayerData ];
new g_AuthID[ MAX_PLAYERS + 1 ][ 34 ];
new bool:g_BotOrHLTV[ MAX_PLAYERS + 1 ];
//In your plugin, you set a players XP using the below:
//pdData[ id ][ XP ] = 12345;
register_plugin( "nVault Top 15" , Version , "bugsy" );
register_clcmd( "say top15" , "ShowTop15" );
register_clcmd( "say loadtestdata" , "LoadTestData" );
if ( ( g_Vault = nvault_open( VaultName ) ) == INVALID_HANDLE )
set_fail_state( "Failed to open vault" );
nvault_close( g_Vault );
public client_authorized( id )
if ( !( g_BotOrHLTV[ id ] = bool:( is_user_bot( id ) || is_user_hltv( id ) ) ) )
//Get players' steam-id so it can be used to retrieve their data from the vault.
get_user_authid( id , g_AuthID[ id ] , charsmax( g_AuthID ) );
//Retrieve player data from vault.
nvault_get_array( g_Vault , g_AuthID[ id ] , pdData[ id ][ PlayerData:0 ] , sizeof( pdData ) );
public client_disconnect( id )
if ( !g_BotOrHLTV[ id ] )
//To avoid having to monitor for name changes in-game, the players name is retrieved and saved when disconnecting.
get_user_name( id , pdData[ id ][ PlayerName ] , charsmax( pdData[ PlayerName ] ) );
//Save player data to vault.
nvault_set_array( g_Vault , g_AuthID[ id ] , pdData[ id ][ PlayerData:0 ] , sizeof( pdData ) );
public ShowTop15( id )
static iSortData[ Max_Player_Support ][ Top15Info ];
new iVault , iRow , iCount , iNextOffset , iCurrentOffset , szKey[ 45 ] , iAvailablePlayers , pdVal[ PlayerData ];
new szMOTD[ 1501 ] , iPos;
//Close and re-open vault so the journal writes to the vault so nvault_util gets most up to date data.
nvault_close( g_Vault );
g_Vault = nvault_open( VaultName );
//Open vault for nvault utility usage.
iVault = nvault_util_open( VaultName );
//Get count of total number of records in the vault.
iCount = nvault_util_count( iVault );
//Loop through all records in the vault.
for ( iRow = 0 ; iRow < iCount && iRow < Max_Player_Support ; iRow++ )
//Read record from vault. iNextOffset will hold the position of the next record in the vault.
iNextOffset = nvault_util_read_array( iVault , iNextOffset , szKey , charsmax( szKey ) , pdVal[ PlayerData:0 ] , sizeof( pdVal ) );
//Set nVault_Offset to the byte offset for this players data within the vault so it can additional data (name, etc) if he is within top 15.
//iPrevOffset is used since iOffset holds the position of the NEXT record, not current.
iSortData[ iRow ][ nVault_Offset ] = iCurrentOffset;
//Set Player_XP value in array to his XP value. This will be used for sorting.
iSortData[ iRow ][ XP_Value ] = pdVal[ XP ];
//Since nvault_util_read_array() holds the position of the next record, we have to hold the current offset separately.
iCurrentOffset = iNextOffset;
//Sort the array.
SortCustom2D( iSortData , min( iCount , Max_Player_Support ) , "CompareXP" );
//Prepare top 15 MOTD.
iPos = formatex( szMOTD , charsmax( szMOTD ) , "<body bgcolor=#000000><font color=#98f5ff><pre>" );
iPos += formatex( szMOTD[ iPos ] , charsmax( szMOTD ) - iPos , "%2s %-22.22s %3s^n" , "#" , "Name" , "XP" );
//This will account for if the vault has less than 15 player data records stored.
iAvailablePlayers = min( iCount , 15 );
//Build the top 15. iAvailablePlayers is set to the smaller of 15 or the total records in the vault.
for ( iRow = 0 ; iRow < iAvailablePlayers ; iRow++ )
//Get nVault player data offset value which was set in the above loop.
iCurrentOffset = iSortData[ iRow ][ nVault_Offset ];
//Read data at the players offset so we can retrieve their name to be displayed in the top 15.
nvault_util_read_array( iVault , iCurrentOffset , szKey , charsmax( szKey ) , pdVal[ PlayerData:0 ] , sizeof( pdVal ) );
//Format line in MOTD.
iPos += formatex( szMOTD[ iPos ] , charsmax( szMOTD ) - iPos ,"%2d %-22.22s %3d^n", ( iRow + 1 ) , pdVal[ PlayerName ] , pdVal[ XP ] );
//Close nvault utility file.
nvault_util_close( iVault );
formatex( szMOTD[ iPos ], charsmax( szMOTD ) - iPos , "</body></font></pre>" );
show_motd( id , szMOTD , "XP Top 15" );
new szAuthID[ 10 ] , pdData[ PlayerData ] , iRow , iXP;
for ( iRow = 0 ; iRow < 100 ; iRow++ )
formatex( szAuthID , charsmax( szAuthID ) , "STEAM_%d" , iRow );
iXP = random( 9999 );
formatex( pdData[ PlayerName ] , charsmax( pdData[ PlayerName ] ) , "Player_%d [%d]" , iRow , iXP );
pdData[ XP ] = iXP;
nvault_set_array( g_Vault , szAuthID , pdData[ PlayerData:0 ] , sizeof( pdData ) );
public CompareXP( elem1 , elem2 )
if ( elem1[ 1 ] > elem2[ 1 ] )
else if(elem1[ 1 ] < elem2[ 1 ] )