// 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 .account_to_name$[0] = ""; // sparse Array indexed by account id .name_to_char = htnew(); // Map .char_to_name$[0] = ""; // sparse Array indexed by char id if (SERVER_USES_VAULT) { .vault_to_account[0] = 0; // sparse Array indexed by account id .account_to_vault[0] = 0; // sparse Array indexed by vault id } // event handlers: .vault_handlers = htnew(); .name_handlers = htnew(); // reloadscript handler: forceCache(); }