From 8a4bf716002a017de77fe7df301ef8e4aaf00a2e Mon Sep 17 00:00:00 2001 From: Jesusaves Date: Fri, 9 Apr 2021 11:00:49 -0300 Subject: Initial commit --- npc/functions/player-cache.txt | 501 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 npc/functions/player-cache.txt (limited to 'npc/functions/player-cache.txt') diff --git a/npc/functions/player-cache.txt b/npc/functions/player-cache.txt new file mode 100644 index 00000000..f134f953 --- /dev/null +++ b/npc/functions/player-cache.txt @@ -0,0 +1,501 @@ +// 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(); +} -- cgit v1.2.3-70-g09d2