Veteran Member
Join Date: Dec 2008
Location: Portugal
|
04-17-2009
, 01:41
Re: The advantages of a trie over an array for strings storage
|
#35
|
Here it goes the code. I didn't test it and i will not since you have to do it by yourself to understand.
(read first the explanation below)
PHP Code:
#include <amxmodx> #include <amxmisc>
enum _:AuthField { AuthField_Names, AuthField_NamesCount }
new Trie:AuthsData new Trie:AllNames
new g_szSteamFile[64] new cvNameChangeKick new cvConnectKick new cvRenameTries
new g_iChangeName[33] new g_iRegistered[33]
public plugin_cfg() { AuthsData = TrieCreate(); AllNames = TrieCreate(); }
public plugin_natives() { register_native("is_user_registered", "native_is_user_registered") }
public plugin_init() { get_configsdir(g_szSteamFile, charsmax(g_szSteamFile)) add(g_szSteamFile, charsmax(g_szSteamFile), "/steamidname.txt")
LoadNames(true)
register_concmd("nr_reload", "cmd_ReloadName", ADMIN_RCON) register_clcmd("nr_check", "cmd_CheckRegister")
cvNameChangeKick = register_cvar("nr_change_kick", "1") cvConnectKick = register_cvar("nr_connect_kick", "1") cvRenameTries = register_cvar("nr_rename_tries", "5") }
public cmd_ReloadName(id, level, cid) { if(cmd_access(id, level, cid, 1)) { console_print(id, "[AMXX] %d name reservations loaded", LoadNames())
// Recheck all players' names static iPlayers[32], iNum // get_players(iPlayers, iNum)
for(new i=0; i < iNum;i++) client_putinserver(iPlayers[i]) }
return PLUGIN_HANDLED }
public cmd_CheckRegister(id) { new szAuth[35] get_user_authid(id, szAuth, charsmax(szAuth))
client_print(id, print_console, "Registered name(s) under this SteamID") client_print(id, print_console, "SteamID: %s", szAuth) client_print(id, print_console, "================================================")
new iCount new Array:authData if(TrieGetCell(AuthsData,szAuth,authData)) { iCount = ArrayGetCell(authData,AuthField_NamesCount); }
client_print(id, print_console, "================================================") client_print(id, print_console, "Total registered name(s): %i", iCount) client_print(id, print_console, "================================================")
return PLUGIN_HANDLED }
public client_connect(id) { g_iChangeName[id] = 0 }
public client_disconnect(id) { g_iChangeName[id] = 0
g_iRegistered[id] = 0 }
public client_putinserver(id) { g_iChangeName[id] = 1
static szName[32], szAuth[35] // get_user_name(id, szName, charsmax(szName)) get_user_authid(id, szAuth, charsmax(szAuth))
if(!CheckName(id, szName, szAuth)) { switch(get_pcvar_num(cvConnectKick)) { case 0: { static szUsedName[32], szTempName[32] copy(szUsedName, 28, szName)
new iMaxTries = min(get_pcvar_num(cvRenameTries), 9)
for(new i=1; i <= iMaxTries; i++) { formatex(szTempName, charsmax(szTempName), "%s(%d)", szUsedName, i)
if(!find_player("a", szTempName) && CheckName(id, szTempName, szAuth)) { set_user_info(id, "name", szTempName) client_print(id, print_chat, "[AMXX] Your name was changed, because it was already registered to another SteamID.")
return } } } case 1: { server_cmd("kick #%d This name is registered to someone else's SteamID!", get_user_userid(id)) } }
log_to_file("steamidfail.txt", "On Join: [SteamID] %s [Name] %s", szAuth, szName) } }
public client_infochanged(id) { new szNewName[32], szOldName[32], szAuth[35]
get_user_info(id, "name", szNewName, charsmax(szNewName)) get_user_name(id, szOldName, charsmax(szOldName)) get_user_authid(id, szAuth, charsmax(szAuth))
if(!equali(szNewName, szOldName) && !CheckName(id, szNewName, szAuth) && g_iChangeName[id]) { switch(get_pcvar_num(cvNameChangeKick)) { case 0: { set_user_info(id, "name", szOldName) client_print(id, print_chat, "Your new name is reserved to another SteamID. Name change cancelled") } case 1: { server_cmd("kick #%d This name is registered to someone else's SteamID!", get_user_userid(id)) } }
log_to_file("steamidfail.txt", "Name Change: [SteamID] %s [OldName] %s [NewName] %s", szAuth, szOldName, szNewName) } }
public native_is_user_registered(iPluginID, iParamsNum) { new id = get_param(1) return is_user_connected(id) ? g_iRegistered[id] : 0 }
LoadNames(iFatal = false) { TrieClear(AuthsData); TrieClear(AllNames); new namesCount
new hFile = fopen(g_szSteamFile, "r") if(!hFile) hFile = fopen(g_szSteamFile, "w+")
if(!hFile) { if(iFatal) { new szFailMsg[64] formatex(szFailMsg, charsmax(szFailMsg), "Can't open/create file %s", g_szSteamFile)
set_fail_state(szFailMsg) } } else { static szName[32], szAuth[35], szBuffer[128] // while(!feof(hFile)) { fgets(hFile, szBuffer, charsmax(szBuffer))
trim(szBuffer)
if(!szBuffer[0] || szBuffer[0] == ';' || (szBuffer[0] == '/' && szBuffer[1] == '/' )) continue
parse(szBuffer, szAuth, charsmax(szAuth), szName, charsmax(szName))
if(szName[0] && szAuth[0]) { new Array:authData if(!TrieGetCell(AuthsData,szAuth,authData)) { authData = ArrayCreate(1,AuthField); ArraySetCell(authData,AuthField_Names,TrieCreate()); ArraySetCell(authData,AuthField_NamesCount,0); TrieSetCell(AuthsData,szAuth,authData); } new Trie:authNames = ArrayGetCell(authData,AuthField_Names) new namesNumber = ArrayGetCell(authData,AuthField_NamesCount); TrieSetCell(authNames,szName,true); ArraySetCell(authData,AuthField_NamesCount,++namesNumber); TrieSetCell(AllNames,szName,true); namesCount++; } }
fclose(hFile) }
return namesCount; }
CheckName(iClient, szName[], szAuth[]) { g_iRegistered[iClient] = 0; if(TrieKeyExists(AllNames,szName)) { new Array:authData if(TrieGetCell(AuthsData,szAuth,authData)) { new Trie:authNames = ArrayGetCell(authData,AuthField_Names); g_iRegistered[iClient] = TrieKeyExists(authNames,szName); } return g_iRegistered[iClient]; }
return true; }
You cannot retrieve the number of entries of a trie, and
Quote:
TrieSetArray and TrieGetArray are broken
|
so i have to mix
tries with cellarrays.
So you can understand it better this is the structure i used to handle the steamIds and names in your code with a fictitious situation:
Code:
STEAM ID | NAMES
STEAM:1 | joaquim , andrade
STEAM:2 | connor
STEAM:3 | Mlk27
___
Trie:AllNames
{
joaquim,
andrade,
connor,
Mlk27
}
Trie:AuthsData
{
STEAM:1 -> Cellarray
{
Trie:AuthField_Names
{
joaquim,
andrade
}
AuthField_NamesCount = 2
}
STEAM:2 -> Cellarray
{
Trie:AuthField_Names
{
connor
}
AuthField_NamesCount = 1
}
STEAM:3 -> Cellarray
{
Trie:AuthField_Names
{
Mlk27
}
AuthField_NamesCount = 1
}
}
With the structure above, CheckName(iClient, szName[], szAuth[]) will work like:
Check if szName is registered by trying to find it in the trie Allnames.
If it is registered, go get the Cellarray associated with with szAuth in AuthsData. Check if szName is associated to szAuth by searching if it is in the trie of names belonging to szAuth.
If it is associated return true. If it isn't return false.
If it isn't registered return true.
__________________
Last edited by joaquimandrade; 04-17-2009 at 02:05.
|
|