summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgumi <git@gumi.ca>2020-07-24 14:06:43 -0400
committergumi <git@gumi.ca>2020-07-27 08:47:44 -0400
commit97561caeca7e9518ff7e4961a57257c5298bdf25 (patch)
tree39f97f8453d1b9a14cbf17c20924184e6194d0b6
parentb899412192d02f5f36d693a92d97b8cf6863f72f (diff)
downloadserverdata-97561caeca7e9518ff7e4961a57257c5298bdf25.tar.gz
serverdata-97561caeca7e9518ff7e4961a57257c5298bdf25.tar.bz2
serverdata-97561caeca7e9518ff7e4961a57257c5298bdf25.tar.xz
serverdata-97561caeca7e9518ff7e4961a57257c5298bdf25.zip
add a reusable event dispatching systems20200729
-rw-r--r--npc/functions/event-listeners.txt169
-rw-r--r--npc/functions/player-cache.txt28
-rw-r--r--npc/scripts.conf1
3 files changed, 174 insertions, 24 deletions
diff --git a/npc/functions/event-listeners.txt b/npc/functions/event-listeners.txt
new file mode 100644
index 00000000..111b2737
--- /dev/null
+++ b/npc/functions/event-listeners.txt
@@ -0,0 +1,169 @@
+// Event listeners work similar to the JavaScript event dispatching system,
+// except that the event object is a hash table.
+//
+// Javascript reference:
+// https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
+//
+// Example usage:
+/*
+// Create a handler function:
+public function myHandler {
+ // ... do something with getarg(0)
+}
+
+// Create a new event:
+$@event = htnew();
+
+// Listen for the event:
+addEventListener($@event, "myHandler");
+
+// Dispatch the event:
+dispatchEvent($@event, "misc data");
+*/
+
+
+/**
+ * getEventHt(<hash table expression>) => ht id
+ *
+ * Parses a hash table "event" expression and creates one when necessary.
+ * The following are all valid:
+ *
+ * getEventHt(.@ht); // variable
+ * getEventHt(72); // arbitrary integer (global namespace)
+ * getEventHt("some event"); // arbitrary string (global namespace)
+ *
+ * NOTE: This is only called internally; you don't have to use this function
+ *
+ * @param 0 - a variable / string / integer
+ * @return the hash table id
+ */
+function script getEventHt {
+ .@ht = ((getdatatype(getarg(0)) & (DATATYPE_VAR | DATATYPE_INT)) != 0) ? getarg(0) : 0;
+
+ if (htexists(.@ht)) {
+ // this is already a valid hash table
+ return getarg(0);
+ } else if ((getdatatype(getarg(0)) & DATATYPE_VAR) != 0) {
+ // the hash table doesn't exist yet: create one and store it in the
+ // provided variable
+
+ if ((getdatatype(getarg(0)) & DATATYPE_STR) != 0) {
+ consolemes(CONSOLEMES_ERROR, "getEventHt: variable must be an integer variable (cannot use %s)", data_to_string(getarg(0)));
+ end;
+ }
+
+ if (getarg(0) > 0) {
+ consolemes(CONSOLEMES_WARNING, "getEventHt: variable is already assigned (overwriting %s)", data_to_string(getarg(0)));
+ }
+
+ return set(getarg(0), htnew());
+ } else {
+ // global event listener namespace
+ if ($@GLOBAL_EVTL_HT < 1) {
+ $@GLOBAL_EVTL_HT = htnew();
+
+ // the global scope is new: create a new one
+ .@ht = htnew();
+ htput($@GLOBAL_EVTL_HT, data_to_string(getarg(0)), "" + .@ht);
+ return .@ht;
+ } else if ((.@ht$ = htget($@GLOBAL_EVTL_HT, data_to_string(getarg(0)), "")) != "") {
+ // found in the global scope
+ return atoi(.@ht$);
+ } else {
+ // not found in the global scope: create new
+ .@ht = htnew();
+ htput($@GLOBAL_EVTL_HT, data_to_string(getarg(0)), "" + .@ht);
+ return .@ht;
+ }
+ }
+}
+
+/**
+ * addEventListener(<ht: event>, "<handler: public local function>") => void
+ *
+ * Register a public local function in the current NPC as an event handler for
+ * the given hash table. Can also register for another NPC using the syntax
+ * `npcName::function`
+ *
+ * NOTE: only one handler may be assigned per NPC per Event
+ *
+ * @param 0 - the event to listen to (string, variable, or hash table)
+ * @param 1 - the event handler (must be a public local function)
+ */
+function script addEventListener {
+ .@ht = getEventHt(getarg(0));
+ .@pos = strpos(getarg(1), "::");
+
+ if (.@pos > -1) {
+ .@id = getnpcid(substr(getarg(1), 0, .@pos - 1));
+ .@function$ = substr(getarg(1), .@pos + 2, getstrlen(getarg(1)) - 1);
+ } else {
+ .@id = getnpcid();
+ .@function$ = getarg(1);
+ }
+
+ if (.@id == 0) {
+ consolemes(CONSOLEMES_WARNING, "addEventListener: the target NPC doesn't exist");
+ return;
+ }
+
+ // TODO: check that the target function exists
+ return htput(.@ht, "" + .@id, .@function$);
+}
+
+/**
+ * dispatchEvent(<ht: event>{, ...<arg>}) => bool
+ *
+ * Calls every event handler for the given hash table and passes the given
+ * arguments (if any)
+ *
+ * @param 0 - the event to dispatch (string, variable, or hash table)
+ * @return true when handlers were called, else false
+ */
+function script dispatchEvent {
+ .@ht = getEventHt(getarg(0));
+
+ if (htsize(.@ht) < 1) {
+ // no handler to call
+ return false;
+ }
+
+ .@it = htiterator(.@ht);
+
+ freeloop(true);
+ for (.@npc$ = htinextkey(.@it); hticheck(.@it); .@npc$ = htinextkey(.@it)) {
+ if (.@npc$ == "") {
+ continue;
+ }
+
+ .@func$ = htget(.@ht, .@npc$, "");
+
+ // this is ugly but we don't have rest parameters (...args) to apply args to function calls
+ switch (getargcount()) {
+ case 1:
+ callfunctionofnpc(atoi(.@npc$), .@func$);
+ break;
+ case 2:
+ callfunctionofnpc(atoi(.@npc$), .@func$, getarg(1));
+ break;
+ case 3:
+ callfunctionofnpc(atoi(.@npc$), .@func$, getarg(1), getarg(2));
+ break;
+ case 4:
+ callfunctionofnpc(atoi(.@npc$), .@func$, getarg(1), getarg(2), getarg(3));
+ break;
+ case 5:
+ callfunctionofnpc(atoi(.@npc$), .@func$, getarg(1), getarg(2), getarg(3), getarg(4));
+ break;
+ default:
+ consolemes(CONSOLEMES_WARNING, "dispatchEvent: truncating to 5 arguments (%d given)", getargcount() - 1);
+ case 6:
+ callfunctionofnpc(atoi(.@npc$), .@func$, getarg(1), getarg(2), getarg(3), getarg(4), getarg(5));
+ break;
+ }
+ }
+ freeloop(false);
+
+ htidelete(.@it);
+ return true;
+}
diff --git a/npc/functions/player-cache.txt b/npc/functions/player-cache.txt
index 7c281af9..204d1fe1 100644
--- a/npc/functions/player-cache.txt
+++ b/npc/functions/player-cache.txt
@@ -216,8 +216,7 @@
* @param 0 - the public local function
*/
public function addNameHandler {
- htput(.name_handlers, "" + getnpcid(), getarg(0));
- return;
+ return addEventListener(.name_handlers, getarg(0));
}
/**
@@ -229,8 +228,7 @@
* @param 0 - the public local function
*/
public function addVaultHandler {
- htput(.vault_handlers, "" + getnpcid(), getarg(0));
- return;
+ return addEventListener(.vault_handlers, getarg(0));
}
@@ -253,16 +251,7 @@
if (.@old$ != "" && .@old$ != .account_to_name$[playerattached()]) {
// fire event handlers
- .@it = htiterator(.name_handlers);
- for (.@npc$ = htinextkey(.@it); hticheck(.@it); .@npc$ = htinextkey(.@it)) {
- if (.@npc$ == "") {
- continue;
- }
-
- .@func$ = htget(.name_handlers, .@npc$, "");
- callfunctionofnpc(atoi(.@npc$), .@func$, .@old$);
- }
- htidelete(.@it);
+ dispatchEvent(.name_handlers, .@old$);
return true;
}
@@ -285,16 +274,7 @@
if (.@old > 0 && .@old != .vault_to_account[getvaultid()]) {
// fire event handlers
- .@it = htiterator(.vault_handlers);
- for (.@npc$ = htinextkey(.@it); hticheck(.@it); .@npc$ = htinextkey(.@it)) {
- if (.@npc$ == "") {
- continue;
- }
-
- .@func$ = htget(.vault_handlers, .@npc$, "");
- callfunctionofnpc(atoi(.@npc$), .@func$, .@old, .@old$);
- }
- htidelete(.@it);
+ dispatchEvent(.vault_handlers, .@old, .@old$);
return true;
}
diff --git a/npc/scripts.conf b/npc/scripts.conf
index 9714b492..d86046ad 100644
--- a/npc/scripts.conf
+++ b/npc/scripts.conf
@@ -14,6 +14,7 @@
"npc/functions/warp.txt",
"npc/functions/gender.txt",
"npc/functions/bitwise.txt",
+"npc/functions/event-listeners.txt",
// Misc functions
"npc/functions/main.txt",