// Player cache system
//
// Holds a cache of online players in-memory even after logout. This greatly
// reduces the need to query the SQL database for data that is frequently
// accessed.
//
// NOTE: This NPC uses the new public/private function call system to avoid
// polluting the global function namespace
//
// Example usage:
// .@account_id = "playerCache"::name2account("Reid");
//
- script playerCache FAKE_NPC,{
/**
* "playerCache"::name2account("char name") => account id
*
* Searches through the player cache to convert a char name to a account ID.
* If the player is not found, checks the SQL table. If the player doesn't
* exist, returns false.
*
* @param 0 - the char name to search
* @return the account id of the char
*/
public function name2account {
if (getarg(0) == "") {
return false;
}
if (.@acc = getcharid(CHAR_ID_ACCOUNT, getarg(0))) {
// player is currently online
return .@acc;
}
if (.@acc = htget(.name_to_account, getarg(0), 0)) {
// player found in the hash table
return .@acc;
}
// player still not found: now we try SQL
.@name$ = escape_sql(getarg(0));
if (SERVER_USES_VAULT) {
query_sql(sprintf("SELECT c.account_id, c.char_id, r.value "
"FROM `char` c "
"JOIN `global_acc_reg_num_db` r ON r.account_id = c.account_id "
"WHERE r.key = '##VAULT' AND r.index = 0 AND c.name = '%s' "
"LIMIT 1;", .@name$),
.@acc, .@char, .@vault);
if (.@vault > 0) {
.vault_to_account[.@vault] = .@acc;
.account_to_vault[.@acc] = .@vault;
}
} else {
query_sql(sprintf("SELECT account_id, char_id FROM `char` WHERE `name`='%s' LIMIT 1;", .@name$), .@acc, .@char);
}
if (.@acc > 0) {
// player found: add to our cache
htput(.name_to_account, getarg(0), .@acc);
.account_to_name$[.@acc] = getarg(0);
htput(.name_to_char, getarg(0), .@char);
.char_to_name$[.@char] = getarg(0);
return .@acc;
}
// player doesn't exist
return false;
}
/**
* "playerCache"::name2char("char name") => char id
*
* Searches through the player cache to convert a char name to a char ID.
* If the player is not found, checks the SQL table. If the player doesn't
* exist, returns false.
*
* @param 0 - the char name to search
* @return the char id of the char
*/
public function name2char {
if ((.@acc = name2account(getarg(0))) != 0) {
return htget(.name_to_char, getarg(0), false);
}
return false;
}
/**
* "playerCache"::char2account(char id) => account id
*
* Searches through the player cache to convert a char ID to an account ID.
* If the player is not found, checks the SQL table. If the player doesn't
* exist, returns false.
*
* @param 0 - the char ID to search
* @return the account id of the char
*/
public function char2account {
if (getarg(0) == 0) {
return false;
}
.@name$ = .char_to_name$[getarg(0)];
if (.@name$ != "") {
if (.@acc = getcharid(CHAR_ID_ACCOUNT, .@name$)) {
// player is currently online
return .@acc;
}
if (.@acc = htget(.name_to_account, .@name$, 0)) {
// player found in the hash table
return .@acc;
}
}
// player still not found: now we try SQL
if (SERVER_USES_VAULT) {
query_sql(sprintf("SELECT c.account_id, c.name, r.value "
"FROM `char` c "
"JOIN `global_acc_reg_num_db` r ON r.account_id = c.account_id "
"WHERE r.key = '##VAULT' AND r.index = 0 AND c.char_id = '%d' "
"LIMIT 1;", getarg(0)),
.@acc, .@name$, .@vault);
if (.@vault > 0) {
.vault_to_account[.@vault] = .@acc;
.account_to_vault[.@acc] = .@vault;
}
} else {
query_sql(sprintf("SELECT account_id, name FROM `char` WHERE `char_id`='%d' LIMIT 1;", getarg(0)), .@acc, .@name$);
}
if (.@acc > 0) {
// player found: add to our cache
htput(.name_to_account, .@name$, .@acc);
.account_to_name$[.@acc] = .@name$;
htput(.name_to_char, .@name$, getarg(0));
.char_to_name$[getarg(0)] = .@name$;
return .@acc;
}
// player doesn't exist
return false;
}
/**
* "playerCache"::account2char(account id) => char id
*
* Searches through the player cache to convert an account ID into a
* char ID. If the player is not found, returns false.
*
* NOTE: this is a weak reference; an account ID does not uniquely identify
* a character
*
* @param 0 - the account ID to search for
* @return the char ID of the char
*/
public function account2char {
if (getarg(0) == 0) {
return false;
}
if ((.@name$ = strcharinfo(PC_NAME, "", getarg(0))) != "") {
// player is online
return getcharid(CHAR_ID_CHAR, .@name$);
} else if ((.@name$ = .account_to_name$[getarg(0)]) != "") {
// found in our cache
return htget(.name_to_char, .@name$, false);
}
// player still not found: now we try SQL
if (SERVER_USES_VAULT) {
query_sql(sprintf("SELECT c.char_id, c.name, r.value "
"FROM `char` c "
"JOIN `global_acc_reg_num_db` r ON r.account_id = c.account_id "
"WHERE r.key = '##VAULT' AND r.index = 0 AND c.account_id = '%d' "
"ORDER BY c.last_login DESC LIMIT 1;", getarg(0)),
.@char, .@name$, .@vault);
if (.@vault > 0) {
.vault_to_account[.@vault] = getarg(0);
.account_to_vault[getarg(0)] = .@vault;
}
} else {
query_sql(sprintf("SELECT char_id, name FROM `char` WHERE account_id='%d' ORDER BY last_login DESC LIMIT 1;", getarg(0)), .@char, .@name$);
}
if (.@char > 0) {
// player found: add to our cache
htput(.name_to_account, .@name$, getarg(0));
.account_to_name$[getarg(0)] = .@name$;
htput(.name_to_char, .@name$, .@char);
.char_to_name$[.@char] = .@name$;
return .@char;
}
// not found
return false;
}
/**
* "playerCache"::account2name(account id) => char name
*
* Searches through the player cache to convert an account ID into a
* char name. If the account is not found, returns an empty string.
*
* NOTE: this is a weak reference; an account ID does not uniquely identify
* a character
*
* @param 0 - the account ID to search for
* @return the name of the char
*/
public function account2name {
if ((.@char = account2char(getarg(0))) != false) {
return .char_to_name$[.@char];
}
return "";
}
/**
* "playerCache"::char2name(char id) => char name
*
* Searches through the player cache to convert a char ID to a char name.
* If the player is not found, checks the SQL table. If the player doesn't
* exist, returns an empty string.
*
* @param 0 - the char ID to search
* @return the name of the char
*/
public function char2name {
if ((.@acc = char2account(getarg(0))) != 0) {
return .account_to_name$[.@acc];
}
// player not found
return "";
}
/**
* "playerCache"::vault2account(vault id) => account id
*
* Searches through the player cache to convert a Vault account ID into a
* game account ID. If the account is not found, returns false
*
* NOTE: this is a weak reference; a Vault ID does not uniquely identify
* a game account
*
* @param 0 - the Vault ID to search for
* @return the account id of the char
*/
public function vault2account {
if (getarg(0) == 0) {
return false;
}
if (!SERVER_USES_VAULT) {
return getarg(0);
} else if (.@acc = .vault_to_account[getarg(0)]) {
// found in the cache
return .@acc;
}
// player still not found: now we try SQL
query_sql(sprintf("SELECT c.account_id, c.char_id, c.name "
"FROM `char` c "
"JOIN `global_acc_reg_num_db` r ON r.account_id = c.account_id "
"WHERE r.key = '##VAULT' AND r.index = 0 AND r.value = %d "
"ORDER BY c.`last_login` DESC LIMIT 1;", getarg(0)),
.@acc, .@char, .@name$);
if (.@char > 0) {
// player found: add to our cache
htput(.name_to_account, .@name$, .@acc);
.account_to_name$[.@acc] = .@name$;
htput(.name_to_char, .@name$, .@char);
.char_to_name$[.@char] = .@name$;
.vault_to_account[getarg(0)] = .@acc;
.account_to_vault[.@acc] = getarg(0);
return .@acc;
}
return false;
}
/**
* "playerCache"::vault2char(vault id) => char id
*
* Searches through the player cache to convert a Vault account ID into a
* char id. If the player is not found, returns false
*
* NOTE: this is a weak reference; a Vault ID does not uniquely identify
* a character
*
* @param 0 - the Vault ID to search for
* @return the char id
*/
public function vault2char {
if ((.@acc = vault2account(getarg(0))) != 0) {
return account2char(.@acc);
}
// player not found
return false;
}
/**
* "playerCache"::vault2name(vault id) => account id
*
* Searches through the player cache to convert a Vault account ID into a
* char name. If the player is not found, returns an empty string
*
* NOTE: this is a weak reference; a Vault ID does not uniquely identify
* a character
*
* @param 0 - the Vault ID to search for
* @return the name of the char
*/
public function vault2name {
if ((.@acc = vault2account(getarg(0))) != 0) {
return account2name(.@acc);
}
// player not found
return "";
}
/**
* "playerCache"::account2vault(account id) => vault id
*
* Searches through the player cache to convert a game account ID into a
* Vault account ID. If the account is not found, returns false
*
* @param 0 - the account ID to search for
* @return the Vault ID associated with the account
*/
public function account2vault {
if (!SERVER_USES_VAULT) {
return getarg(0);
}
account2char(getarg(0)); // will fetch vault id
return .account_to_vault[getarg(0)];
}
/**
* "playerCache"::name2vault(char name) => vault id
*
* Searches through the player cache to convert a character name into a
* Vault account ID. If the account is not found, returns false
*
* @param 0 - the char name to search for
* @return the Vault ID associated with the account
*/
public function name2vault {
return account2vault(name2account(getarg(0)));
}
/**
* "playerCache"::char2vault(char id) => vault id
*
* Searches through the player cache to convert a char id into a
* Vault account ID. If the account is not found, returns false
*
* @param 0 - the char id to search for
* @return the Vault ID associated with the account
*/
public function char2vault {
return account2vault(char2account(getarg(0)));
}
/**
* Registers a public local function that will be called when the char name
* associated with an account changes. The previous account ID will be
* passed as first argument to the provided function.
*
* @param 0 - the public local function
*/
public function addNameHandler {
return addEventListener(.name_handlers, getarg(0));
}
/**
* Registers a public local function that will be called when the account
* associated with a Vault account changes. The previous account id will be
* passed as first argument to the provided function and the previous
* char name will be passed as second argument.
*
* @param 0 - the public local function
*/
public function addVaultHandler {
return addEventListener(.vault_handlers, getarg(0));
}
////////////////////////////////////////////////////////////////////////////////
/**
* updates the char name cache and fires event handlers when the name
* associated with an account changes (such as when switching char)
*/
private function updateName {
// get the cached name:
.@old$ = .account_to_name$[playerattached()];
// now update the cache:
htput(.name_to_account, strcharinfo(PC_NAME), playerattached());
.account_to_name$[playerattached()] = strcharinfo(PC_NAME);
htput(.name_to_char, strcharinfo(PC_NAME), getcharid(CHAR_ID_CHAR));
.char_to_name$[getcharid(CHAR_ID_CHAR)] = strcharinfo(PC_NAME);
if (.@old$ != "" && .@old$ != .account_to_name$[playerattached()]) {
// fire event handlers
dispatchEvent(.name_handlers, .@old$);
return true;
}
return false;
}
/**
* updates the Vault account cache and fires event handlers when the account
* associated with a Vault account changes (different game account on same
* Vault account)
*/
private function updateVault {
// get the cached id:
.@old = .vault_to_account[getvaultid()];
.@old$ = .account_to_name$[.@old];
// now update the cache:
.vault_to_account[getvaultid()] = playerattached();
.account_to_vault[playerattached()] = getvaultid();
if (.@old > 0 && .@old != .vault_to_account[getvaultid()]) {
// fire event handlers
dispatchEvent(.vault_handlers, .@old, .@old$);
return true;
}
return false;
}
/**
* adds/updates the player to the player cache
*/
private function updateCache {
updateName();
if (SERVER_USES_VAULT) {
updateVault();
}
return;
}
/**
* force-add all online players to the cache (used on init and reload)
*/
private function forceCache {
.@count = getunits(BL_PC, .@units, false);
for (.@i = 0; .@i < .@count; ++.@i) {
attachrid(.@units[.@i]);
updateCache();
detachrid();
}
return;
}
OnPCLoginEvent:
updateCache();
end;
OnInit:
.name_to_account = htnew(); // Map<char name, account id>
.account_to_name$[0] = ""; // sparse Array<char name> indexed by account id
.name_to_char = htnew(); // Map<char name, char id>
.char_to_name$[0] = ""; // sparse Array<char name> indexed by char id
if (SERVER_USES_VAULT) {
.vault_to_account[0] = 0; // sparse Array<vault id> indexed by account id
.account_to_vault[0] = 0; // sparse Array<account id> indexed by vault id
}
// event handlers:
.vault_handlers = htnew();
.name_handlers = htnew();
// reloadscript handler:
forceCache();
}