summaryrefslogtreecommitdiff
path: root/npc/functions/player-cache.txt
diff options
context:
space:
mode:
Diffstat (limited to 'npc/functions/player-cache.txt')
-rw-r--r--npc/functions/player-cache.txt501
1 files changed, 501 insertions, 0 deletions
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<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();
+}