chore(release): 0.4.0 v0.4.0
authorxangelo <me@xangelo.ca>
Fri, 29 Sep 2023 16:12:39 +0000 (12:12 -0400)
committerxangelo <me@xangelo.ca>
Fri, 29 Sep 2023 16:12:39 +0000 (12:12 -0400)
39 files changed:
CHANGELOG.md
data/dungeons/storage_cellar.json [new file with mode: 0644]
migrations/20230908160725_dungeons.ts [new file with mode: 0644]
migrations/20230915162829_events.ts [new file with mode: 0644]
package-lock.json
package.json
public/assets/bundle.js
public/assets/css/game.css
seeds/cities.ts
seeds/dungeons.ts [new file with mode: 0644]
src/client/htmx.ts
src/server/admin.ts [new file with mode: 0644]
src/server/api.ts
src/server/chat-commands.ts
src/server/chat-commands/base.ts [new file with mode: 0644]
src/server/chat-commands/give-permission.ts [new file with mode: 0644]
src/server/chat-commands/index.ts [new file with mode: 0644]
src/server/chat-commands/refresh-cities.ts [new file with mode: 0644]
src/server/chat-commands/refresh-monsters.ts [new file with mode: 0644]
src/server/chat-commands/refresh-shops.ts [new file with mode: 0644]
src/server/chat-commands/say.ts [new file with mode: 0644]
src/server/chat-commands/set-level.ts [new file with mode: 0644]
src/server/dungeon.ts [new file with mode: 0644]
src/server/events.ts [new file with mode: 0644]
src/server/fight.ts
src/server/lib/clickhouse.ts [new file with mode: 0644]
src/server/locations/dungeon.ts [new file with mode: 0644]
src/server/map.ts
src/server/player.ts
src/server/views/components/explore-pane.ts [new file with mode: 0644]
src/server/views/dungeons/room.ts [new file with mode: 0644]
src/server/views/fight.ts
src/server/views/map.ts
src/shared/constants.ts
src/shared/dungeon.ts [new file with mode: 0644]
src/shared/event.ts [new file with mode: 0644]
src/shared/map.ts
src/shared/monsters.ts
src/shared/player.ts

index c9a78dc3841d3c32a4f04b4494533e39a43f697d..5c6ff07a47b2e2d04a8ccc1a3427f8fc78f25af3 100644 (file)
@@ -2,6 +2,26 @@
 
 All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
 
 
 All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
 
+## [0.4.0](https://git.xangelo.ca/?p=risinglegends.git;a=commitdiff;h=v0.4.0;hp=v0.3.6;ds=sidebyside) (2023-09-29)
+
+
+### ⚠ BREAKING CHANGES
+
+* dungeon traversal
+
+### Features
+
+* cleanup chat commands 074d6ad
+* dungeon traversal 2ec43df
+* min level for all locations 2be0160
+* psql based event system 43f0bc3
+
+
+### Bug Fixes
+
+* auto-enter dungeon if you are in it dfa62a7
+* remove log of 0 events being flushed 552c744
+
 ### [0.3.6](https://git.xangelo.ca/?p=risinglegends.git;a=commitdiff;h=v0.3.6;hp=v0.3.5;ds=sidebyside) (2023-09-06)
 
 
 ### [0.3.6](https://git.xangelo.ca/?p=risinglegends.git;a=commitdiff;h=v0.3.6;hp=v0.3.5;ds=sidebyside) (2023-09-06)
 
 
diff --git a/data/dungeons/storage_cellar.json b/data/dungeons/storage_cellar.json
new file mode 100644 (file)
index 0000000..ece9763
--- /dev/null
@@ -0,0 +1,224 @@
+{
+  "passages": [
+    {
+      "text": "You stand at the bottom of the stairwell leading outside  the cellar. You can make out a faint square of light above indicating the hatch that doesn't sit quite as flush as it once did.\n\n<commands>\n[[Head toward the torch->w1]]\n[[Forward->n1]]\n[[Head away from the torch->e1]]\n{{visit}}\n{{1}}\nYou climb down old wooden stairs into the dark cellar. The only light seems to be from the hatch you just climbed through.\n\n<quote>\"I'll close this up, otherwise one of those darned rats might get out\"</quote> Varun says. \n\nYou hear him grunt as he lifts the hatch and drops it into place. \n\nIt takes a minute for your eyes to adjust but soon you notice the torches on the wall.. it's not as dark down here as you imagined...\n{{/1}}\n{{/visit}}",
+      "links": [
+        {
+          "name": "Head toward the torch",
+          "link": "w1",
+          "pid": "3"
+        },
+        {
+          "name": "Forward",
+          "link": "n1",
+          "pid": "2"
+        },
+        {
+          "name": "Head away from the torch",
+          "link": "e1",
+          "pid": "6"
+        }
+      ],
+      "props": {
+        "visit": {
+          "1": "You climb down old wooden stairs into the dark cellar. The only light seems to be from the hatch you just climbed through.<quote>\"I'll close this up, otherwise one of those darned rats might get out\"</quote> Varun says. You hear him grunt as he lifts the hatch and drops it into place. It takes a minute for your eyes to adjust but soon you notice the torches on the wall.. it's not as dark down here as you imagined..."
+        }
+      },
+      "name": "start",
+      "pid": "1",
+      "position": {
+        "x": "800",
+        "y": "600"
+      }
+    },
+    {
+      "text": " A few feet in front of the hatch the ground is fairly compacted... covered in footsteps upon footsteps. This must be the route Varun's helpers take to retrieve the grain. \n\n<commands>\n[[Step to the Left->w2n2]]\n[[Back toward Hatch->start]]\n[[Keep going forward->n2]]\n[[Head right->e2n2]]",
+      "links": [
+        {
+          "name": "Step to the Left",
+          "link": "w2n2",
+          "pid": "4"
+        },
+        {
+          "name": "Back toward Hatch",
+          "link": "start",
+          "pid": "1"
+        },
+        {
+          "name": "Keep going forward",
+          "link": "n2",
+          "pid": "5"
+        },
+        {
+          "name": "Head right",
+          "link": "e2n2",
+          "pid": "7"
+        }
+      ],
+      "name": "n1",
+      "pid": "2",
+      "position": {
+        "x": "800",
+        "y": "400"
+      }
+    },
+    {
+      "text": "You step toward the torch. The light isn't bright enough to light up much... but you can see that the walls look a bit damp. Doesn't seem like ideal food storage conditions...\n\n\n<commands>\n[[Step forward along the wall->w2n2]]\n[[Back toward the hatch->start]]",
+      "links": [
+        {
+          "name": "Step forward along the wall",
+          "link": "w2n2",
+          "pid": "4"
+        },
+        {
+          "name": "Back toward the hatch",
+          "link": "start",
+          "pid": "1"
+        }
+      ],
+      "name": "w1",
+      "pid": "3",
+      "position": {
+        "x": "600",
+        "y": "600"
+      }
+    },
+    {
+      "text": "You're not that far from the torch but already it feels like this area is getting no light. Your foot snags against something.\n\nInspecting closer you see the torn rags of some kind of sack...\n\nIt looks like it fell off the wooden crates in front of you\n<commands>\n[[Back toward the torch->w1]]\n[[Step to the right->n1]]\n{{fight}}\n{{monster_id}}2{{/monster_id}}\n{{one_time}}true{{/one_time}}\n{{/fight}}",
+      "links": [
+        {
+          "name": "Back toward the torch",
+          "link": "w1",
+          "pid": "3"
+        },
+        {
+          "name": "Step to the right",
+          "link": "n1",
+          "pid": "2"
+        }
+      ],
+      "props": {
+        "fight": {
+          "monster_id": "2",
+          "one_time": "true"
+        }
+      },
+      "name": "w2n2",
+      "pid": "4",
+      "position": {
+        "x": "600",
+        "y": "400"
+      }
+    },
+    {
+      "text": "You can see that a path has been cleared between the wooden crates. The crates have dug into the floor as they were pushed around. \n\nThe slain rat lies on the floor. \n<commands>\n[[Head forward->n3]]\n[[Head back->n1]]\n{{fight}}\n{{monster_id}}2{{/monster_id}}\n{{one_time}}true{{/one_time}}\n{{/fight}}",
+      "links": [
+        {
+          "name": "Head forward",
+          "link": "n3",
+          "pid": "8"
+        },
+        {
+          "name": "Head back",
+          "link": "n1",
+          "pid": "2"
+        }
+      ],
+      "props": {
+        "fight": {
+          "monster_id": "2",
+          "one_time": "true"
+        }
+      },
+      "name": "n2",
+      "pid": "5",
+      "position": {
+        "x": "800",
+        "y": "200"
+      }
+    },
+    {
+      "text": "As you step away from the torch, you're aware of how cold the cellar actually is. It's not that big, but the light barely reaches into this corner.\n\n<commands>\n[[Investigate the noise->e2n2]]\n[[Head back to the hatch->start]]\n{{visit}}\n\t{{1}}You hear some skittering up ahead{{/1}}\n{{/visit}}",
+      "links": [
+        {
+          "name": "Investigate the noise",
+          "link": "e2n2",
+          "pid": "7"
+        },
+        {
+          "name": "Head back to the hatch",
+          "link": "start",
+          "pid": "1"
+        }
+      ],
+      "props": {
+        "visit": {
+          "1": "You hear some skittering up ahead"
+        }
+      },
+      "name": "e1",
+      "pid": "6",
+      "position": {
+        "x": "1000",
+        "y": "600"
+      }
+    },
+    {
+      "text": "You see some bags in the corner.. and also the corpse of that Rat you dealt wtih.\n\nIn front of you you can see stacks of wooden crates, often topped by a burlap sack of some kind.\n\n<commands>\n[[Head back the way you came->e1]]\n[[Head toward the hatch->n1]]\n{{fight}}\n{{monster_id}}2{{/monster_id}}\n{{one_time}}true{{/one_time}}\n{{/fight}}\n{{visit}}\n{{1}}\nThat rat was way too big to be a regular rat... \n{{/1}}\n{{/visit}}",
+      "links": [
+        {
+          "name": "Head back the way you came",
+          "link": "e1",
+          "pid": "6"
+        },
+        {
+          "name": "Head toward the hatch",
+          "link": "n1",
+          "pid": "2"
+        }
+      ],
+      "props": {
+        "fight": {
+          "monster_id": "2",
+          "one_time": "true"
+        },
+        "visit": {
+          "1": "That rat was way too big to be a regular rat... "
+        }
+      },
+      "name": "e2n2",
+      "pid": "7",
+      "position": {
+        "x": "1000",
+        "y": "400"
+      }
+    },
+    {
+      "text": "You reach the wall at the other end of the cellar.. you don't hear anything further.. maybe it's been cleared out?\n\n<commands>\n\n{{end}}true{{/end}}\n{{rewards}}\n\t{{base}}\n    \t{{exp}}20{{/exp}}\n    \t{{gold}}60{{/gold}}\n    {{/base}}\n\t{{per_kill_bonus}}\n    \t{{exp}}10{{/exp}}\n        {{gold}}10{{/gold}}\n    {{/per_kill_bonus}}\n{{/rewards}}",
+      "props": {
+        "end": "true",
+        "rewards": {
+          "base": {
+            "exp": "20",
+            "gold": "60"
+          },
+          "per_kill_bonus": {
+            "exp": "10",
+            "gold": "10"
+          }
+        }
+      },
+      "name": "n3",
+      "pid": "8",
+      "position": {
+        "x": "800",
+        "y": "0"
+      }
+    }
+  ],
+  "name": "Storage Cellar",
+  "startnode": "1",
+  "creator": "Twine",
+  "creator-version": "2.7.1",
+  "ifid": "0F34ADD6-2DC1-4A8C-9464-008386845215"
+}
diff --git a/migrations/20230908160725_dungeons.ts b/migrations/20230908160725_dungeons.ts
new file mode 100644 (file)
index 0000000..73da240
--- /dev/null
@@ -0,0 +1,44 @@
+import { Knex } from "knex";
+
+
+export async function up(knex: Knex): Promise<void> {
+  return knex.schema.createTable('dungeons', function(table) {
+    table.string('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
+    table.string('name');
+    table.uuid('starting_room');
+  }).createTable('dungeon_rooms', function(table) {
+      table.uuid('id').defaultTo(knex.raw('uuid_generate_v4()')).primary();
+      table.uuid('dungeon_id');
+      table.text('description');
+      table.json('exits').defaultTo(knex.raw("'[]'::json"));
+      table.json('settings').defaultTo('{}');
+    }).createTable('dungeon_things', function(table) {
+      table.uuid('room_id');
+      table.uuid('id').defaultTo(knex.raw('uuid_generate_v4()')).primary();
+      table.string('name');
+      table.json('properties').defaultTo({})
+    }).createTable('dungeon_players', function(table) {
+      table.uuid('player_id').primary();
+      table.uuid('dungeon_id');
+      table.uuid('current_room_id');
+      table.uuid('target_room_id'); // only used when movement is interrupted by a fight
+    }).createTable('dungeon_state', function(table) {
+      table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
+      table.uuid('player_id');
+      table.uuid('dungeon_id');
+      table.string('event_name');
+      table.json('event_props');
+      table.timestamp('create_date').defaultTo(knex.raw('NOW()'))
+      table.index(['player_id', 'dungeon_id']);
+      table.index(['player_id', 'dungeon_id', 'event_name']);
+    })
+}
+
+
+export async function down(knex: Knex): Promise<void> {
+  return knex.schema.dropTable('dungeon_rooms')
+                    .dropTable('dungeon_things')
+                    .dropTable('dungeon_players')
+                    .dropTable('dungeon_state')
+                    .dropTable('dungeons');
+}
diff --git a/migrations/20230915162829_events.ts b/migrations/20230915162829_events.ts
new file mode 100644 (file)
index 0000000..b34437b
--- /dev/null
@@ -0,0 +1,20 @@
+import { Knex } from "knex";
+
+
+export async function up(knex: Knex): Promise<void> {
+  return knex.schema.createTable('events', function(table) {
+    table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
+    table.string('event_name');
+    table.uuid('player_id');
+    table.integer('value').defaultTo(1);
+    table.string('app_version');
+    table.json('props').defaultTo('{}');
+    table.timestamp('created').defaultTo(knex.raw('NOW()'))
+  });
+}
+
+
+export async function down(knex: Knex): Promise<void> {
+  return knex.schema.dropTable('events');
+}
+
index 312fb12066ea0cf68406492e38cd199cee3a3745..512516027013a4687f72349636b3a3b6780be19b 100644 (file)
@@ -1,12 +1,12 @@
 {
   "name": "rising-legends",
 {
   "name": "rising-legends",
-  "version": "0.3.6",
+  "version": "0.4.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "rising-legends",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "rising-legends",
-      "version": "0.3.6",
+      "version": "0.4.0",
       "dependencies": {
         "@honeycombio/opentelemetry-node": "^0.4.0",
         "@opentelemetry/auto-instrumentations-node": "^0.37.0",
       "dependencies": {
         "@honeycombio/opentelemetry-node": "^0.4.0",
         "@opentelemetry/auto-instrumentations-node": "^0.37.0",
         "@types/jest": "^29.5.3",
         "@types/jquery": "^3.5.16",
         "@types/lodash": "^4.14.195",
         "@types/jest": "^29.5.3",
         "@types/jquery": "^3.5.16",
         "@types/lodash": "^4.14.195",
+        "@types/marked": "^5.0.1",
         "husky": "^8.0.0",
         "jest": "^29.6.2",
         "jquery": "^3.7.0",
         "husky": "^8.0.0",
         "jest": "^29.6.2",
         "jquery": "^3.7.0",
+        "marked": "^9.0.0",
         "nodemon": "^2.0.20",
         "standard-version": "^9.5.0",
         "ts-jest": "^29.1.1",
         "nodemon": "^2.0.20",
         "standard-version": "^9.5.0",
         "ts-jest": "^29.1.1",
       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
       "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
     },
       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
       "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
     },
+    "node_modules/@types/marked": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.1.tgz",
+      "integrity": "sha512-Y3pAUzHKh605fN6fvASsz5FDSWbZcs/65Q6xYRmnIP9ZIYz27T4IOmXfH9gWJV1dpi7f1e7z7nBGUTx/a0ptpA==",
+      "dev": true
+    },
     "node_modules/@types/memcached": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.7.tgz",
     "node_modules/@types/memcached": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.7.tgz",
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/marked": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-9.0.0.tgz",
+      "integrity": "sha512-37yoTpjU+TSXb9OBYY5n78z/CqXh76KiQj9xsKxEdztzU9fRLmbWO5YqKxgCVGKlNdexppnbKTkwB3RipVri8w==",
+      "dev": true,
+      "bin": {
+        "marked": "bin/marked.js"
+      },
+      "engines": {
+        "node": ">= 16"
+      }
+    },
     "node_modules/media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
     "node_modules/media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
       "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
     },
       "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
       "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
     },
+    "@types/marked": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@types/marked/-/marked-5.0.1.tgz",
+      "integrity": "sha512-Y3pAUzHKh605fN6fvASsz5FDSWbZcs/65Q6xYRmnIP9ZIYz27T4IOmXfH9gWJV1dpi7f1e7z7nBGUTx/a0ptpA==",
+      "dev": true
+    },
     "@types/memcached": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.7.tgz",
     "@types/memcached": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/@types/memcached/-/memcached-2.2.7.tgz",
       "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
       "dev": true
     },
       "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
       "dev": true
     },
+    "marked": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-9.0.0.tgz",
+      "integrity": "sha512-37yoTpjU+TSXb9OBYY5n78z/CqXh76KiQj9xsKxEdztzU9fRLmbWO5YqKxgCVGKlNdexppnbKTkwB3RipVri8w==",
+      "dev": true
+    },
     "media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
     "media-typer": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
index 3c242fc8858dfa49433c734d2ca1ec520ce13f02..7c8a2eb1603d6c620f7fea880492835be3411d44 100644 (file)
@@ -1,7 +1,7 @@
 {
   "name": "rising-legends",
   "private": true,
 {
   "name": "rising-legends",
   "private": true,
-  "version": "0.3.6",
+  "version": "0.4.0",
   "scripts": {
     "up": "npx prisma migrate dev --name \"init\"",
     "start": "pm2 start dist/server/api.js",
   "scripts": {
     "up": "npx prisma migrate dev --name \"init\"",
     "start": "pm2 start dist/server/api.js",
@@ -12,7 +12,7 @@
     "seed": "npx ts-node ./node_modules/knex/bin/cli.js seed:run",
     "seed:prod": "NODE_ENV=production npm run seed",
     "dev:client": "npx webpack -w",
     "seed": "npx ts-node ./node_modules/knex/bin/cli.js seed:run",
     "seed:prod": "NODE_ENV=production npm run seed",
     "dev:client": "npx webpack -w",
-    "dev:server": "npx nodemon src/server/api.ts",
+    "dev": "npx nodemon src/server/api.ts",
     "prepare": "husky install",
     "release": "npx standard-version && npm run copy-changelog",
     "copy-changelog": "cp ./CHANGELOG.md ~/repos/xangelo.ca/static/",
     "prepare": "husky install",
     "release": "npx standard-version && npm run copy-changelog",
     "copy-changelog": "cp ./CHANGELOG.md ~/repos/xangelo.ca/static/",
     "@types/jest": "^29.5.3",
     "@types/jquery": "^3.5.16",
     "@types/lodash": "^4.14.195",
     "@types/jest": "^29.5.3",
     "@types/jquery": "^3.5.16",
     "@types/lodash": "^4.14.195",
+    "@types/marked": "^5.0.1",
     "husky": "^8.0.0",
     "jest": "^29.6.2",
     "jquery": "^3.7.0",
     "husky": "^8.0.0",
     "jest": "^29.6.2",
     "jquery": "^3.7.0",
+    "marked": "^9.0.0",
     "nodemon": "^2.0.20",
     "standard-version": "^9.5.0",
     "ts-jest": "^29.1.1",
     "nodemon": "^2.0.20",
     "standard-version": "^9.5.0",
     "ts-jest": "^29.1.1",
index 696b7fa5bf9d95a8450d779c3daf5eefb0ba676e..f2ca3ad2233175474a99b635b150d513b3dc52e7 100644 (file)
@@ -1 +1 @@
-(()=>{var e={802:(e,t,s)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const s="color: "+this.color;t.splice(1,0,s,"color: inherit");let n=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(n++,"%c"===e&&(r=n))})),t.splice(r,0,s)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=s(804)(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},804:(e,t,s)=>{e.exports=function(e){function t(e){let s,r,o,i=null;function a(...e){if(!a.enabled)return;const n=a,r=Number(new Date),o=r-(s||r);n.diff=o,n.prev=s,n.curr=r,s=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((s,r)=>{if("%%"===s)return"%";i++;const o=t.formatters[r];if("function"==typeof o){const t=e[i];s=o.call(n,t),e.splice(i,1),i--}return s})),t.formatArgs.call(n,e),(n.log||t.log).apply(n,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=n,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(r!==t.namespaces&&(r=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(a),a}function n(e,s){const n=t(this.namespace+(void 0===s?":":s)+e);return n.log=this.log,n}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let s;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const n=("string"==typeof e?e:"").split(/[\s,]+/),r=n.length;for(s=0;s<r;s++)n[s]&&("-"===(e=n[s].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.slice(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let s,n;for(s=0,n=t.skips.length;s<n;s++)if(t.skips[s].test(e))return!1;for(s=0,n=t.names.length;s<n;s++)if(t.names[s].test(e))return!0;return!1},t.humanize=s(810),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((s=>{t[s]=e[s]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t),s|=0;return t.colors[Math.abs(s)%t.colors.length]},t.enable(t.load()),t}},810:e=>{var t=1e3,s=60*t,n=60*s,r=24*n;function o(e,t,s,n){var r=t>=1.5*s;return Math.round(e/s)+" "+n+(r?"s":"")}e.exports=function(e,i){i=i||{};var a,c,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var o=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(o){var i=parseFloat(o[1]);switch((o[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*i;case"weeks":case"week":case"w":return 6048e5*i;case"days":case"day":case"d":return i*r;case"hours":case"hour":case"hrs":case"hr":case"h":return i*n;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}(e);if("number"===u&&isFinite(e))return i.long?(a=e,(c=Math.abs(a))>=r?o(a,c,r,"day"):c>=n?o(a,c,n,"hour"):c>=s?o(a,c,s,"minute"):c>=t?o(a,c,t,"second"):a+" ms"):function(e){var o=Math.abs(e);return o>=r?Math.round(e/r)+"d":o>=n?Math.round(e/n)+"h":o>=s?Math.round(e/s)+"m":o>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},669:(e,t,s)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const s="color: "+this.color;t.splice(1,0,s,"color: inherit");let n=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(n++,"%c"===e&&(r=n))})),t.splice(r,0,s)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=s(231)(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},231:(e,t,s)=>{e.exports=function(e){function t(e){let s,r,o,i=null;function a(...e){if(!a.enabled)return;const n=a,r=Number(new Date),o=r-(s||r);n.diff=o,n.prev=s,n.curr=r,s=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((s,r)=>{if("%%"===s)return"%";i++;const o=t.formatters[r];if("function"==typeof o){const t=e[i];s=o.call(n,t),e.splice(i,1),i--}return s})),t.formatArgs.call(n,e),(n.log||t.log).apply(n,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=n,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(r!==t.namespaces&&(r=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(a),a}function n(e,s){const n=t(this.namespace+(void 0===s?":":s)+e);return n.log=this.log,n}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let s;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const n=("string"==typeof e?e:"").split(/[\s,]+/),r=n.length;for(s=0;s<r;s++)n[s]&&("-"===(e=n[s].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.slice(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let s,n;for(s=0,n=t.skips.length;s<n;s++)if(t.skips[s].test(e))return!1;for(s=0,n=t.names.length;s<n;s++)if(t.names[s].test(e))return!0;return!1},t.humanize=s(241),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((s=>{t[s]=e[s]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t),s|=0;return t.colors[Math.abs(s)%t.colors.length]},t.enable(t.load()),t}},241:e=>{var t=1e3,s=60*t,n=60*s,r=24*n;function o(e,t,s,n){var r=t>=1.5*s;return Math.round(e/s)+" "+n+(r?"s":"")}e.exports=function(e,i){i=i||{};var a,c,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var o=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(o){var i=parseFloat(o[1]);switch((o[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*i;case"weeks":case"week":case"w":return 6048e5*i;case"days":case"day":case"d":return i*r;case"hours":case"hour":case"hrs":case"hr":case"h":return i*n;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}(e);if("number"===u&&isFinite(e))return i.long?(a=e,(c=Math.abs(a))>=r?o(a,c,r,"day"):c>=n?o(a,c,n,"hour"):c>=s?o(a,c,s,"minute"):c>=t?o(a,c,t,"second"):a+" ms"):function(e){var o=Math.abs(e);return o>=r?Math.round(e/r)+"d":o>=n?Math.round(e/n)+"h":o>=s?Math.round(e/s)+"m":o>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},618:(e,t,s)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const s="color: "+this.color;t.splice(1,0,s,"color: inherit");let n=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(n++,"%c"===e&&(r=n))})),t.splice(r,0,s)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=s(224)(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},224:(e,t,s)=>{e.exports=function(e){function t(e){let s,r,o,i=null;function a(...e){if(!a.enabled)return;const n=a,r=Number(new Date),o=r-(s||r);n.diff=o,n.prev=s,n.curr=r,s=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((s,r)=>{if("%%"===s)return"%";i++;const o=t.formatters[r];if("function"==typeof o){const t=e[i];s=o.call(n,t),e.splice(i,1),i--}return s})),t.formatArgs.call(n,e),(n.log||t.log).apply(n,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=n,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(r!==t.namespaces&&(r=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(a),a}function n(e,s){const n=t(this.namespace+(void 0===s?":":s)+e);return n.log=this.log,n}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let s;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const n=("string"==typeof e?e:"").split(/[\s,]+/),r=n.length;for(s=0;s<r;s++)n[s]&&("-"===(e=n[s].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.slice(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let s,n;for(s=0,n=t.skips.length;s<n;s++)if(t.skips[s].test(e))return!1;for(s=0,n=t.names.length;s<n;s++)if(t.names[s].test(e))return!0;return!1},t.humanize=s(896),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((s=>{t[s]=e[s]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t),s|=0;return t.colors[Math.abs(s)%t.colors.length]},t.enable(t.load()),t}},896:e=>{var t=1e3,s=60*t,n=60*s,r=24*n;function o(e,t,s,n){var r=t>=1.5*s;return Math.round(e/s)+" "+n+(r?"s":"")}e.exports=function(e,i){i=i||{};var a,c,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var o=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(o){var i=parseFloat(o[1]);switch((o[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*i;case"weeks":case"week":case"w":return 6048e5*i;case"days":case"day":case"d":return i*r;case"hours":case"hour":case"hrs":case"hr":case"h":return i*n;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}(e);if("number"===u&&isFinite(e))return i.long?(a=e,(c=Math.abs(a))>=r?o(a,c,r,"day"):c>=n?o(a,c,n,"hour"):c>=s?o(a,c,s,"minute"):c>=t?o(a,c,t,"second"):a+" ms"):function(e){var o=Math.abs(e);return o>=r?Math.round(e/r)+"d":o>=n?Math.round(e/n)+"h":o>=s?Math.round(e/s)+"m":o>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},628:function(e,t,s){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s);var r=Object.getOwnPropertyDescriptor(t,s);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[s]}}),Object.defineProperty(e,n,r)}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var s in e)"default"!==s&&Object.prototype.hasOwnProperty.call(e,s)&&n(t,e,s);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.authToken=void 0;const i=s(46),a=s(182),c=o(s(454));function u(e,t=document){return t.querySelector(e)}function h(e,t=document){return Array.from(t.querySelectorAll(e))}function l(){return localStorage.getItem("authToken")||""}function d(){const e=p.gradientName(),t=u("body");let s;t.classList.value=e,t.classList.add(p.getTimePeriod()),s=p.get24Hour()>=5&&p.get24Hour()<9?"morning":p.get24Hour()>=9&&p.get24Hour()<17?"afternoon":p.get24Hour()>=17&&p.get24Hour()<20?"evening":"night",u("#time-of-day").innerHTML=`<img src="/assets/img/icons/time-of-day/${s}.png"> ${p.getHour()}${p.getAmPm()}`}t.authToken=l;const p=new a.TimeManager,f=(0,i.io)({extraHeaders:{"x-authtoken":l()}});d(),setInterval(d,6e4),f.on("connect",(()=>{console.log(`Connected: ${f.id}`)})),f.on("authToken",(e=>{console.log(`recv auth token ${e}`),localStorage.getItem("authToken")!==e&&(localStorage.setItem("authToken",e),window.location.reload())})),f.on("status",(e=>u("#server-stats").innerHTML=e)),f.on("chat",(function(e){u("#chat-messages").insertAdjacentHTML("beforeend",e),u("#chat-messages").scrollTop=u("#chat-messages").scrollHeight})),f.on("ready",(function(){console.log("Server connection verified"),h("nav a")[3].click()})),h("nav a").forEach((e=>{e.addEventListener("click",(e=>{const t=e.target;h("a",t.closest("nav")).forEach((e=>{e.classList.remove("active")})),t.classList.add("active");const s=u(t.getAttribute("hx-target").toString());Array.from(s.parentElement.children).forEach((e=>{e.classList.remove("active")})),s.classList.add("active")}))})),u("body").addEventListener("click",(e=>{var t;const s=e.target;if(s.parentElement.classList.contains("filter")){Array.from(s.parentElement.children).forEach((e=>e.classList.remove("active"))),s.classList.add("active");const e=s.getAttribute("data-filter"),t=u(`.filter-result[data-filter="${e}"]`,s.closest(".filter-container"));t&&Array.from(t.parentElement.children).forEach((t=>{t.getAttribute("data-filter")===e?(t.classList.remove("hidden"),t.classList.add("active")):(t.classList.add("hidden"),t.classList.remove("active"))}))}"dialog"===s.getAttribute("formmethod")&&"cancel"===s.getAttribute("value")&&(null===(t=s.closest("dialog"))||void 0===t||t.close())}));const g=new MutationObserver(((e,t)=>{const s={modal:!1,alert:!1};e.forEach((e=>{switch(e.target.id){case"modal-wrapper":s.modal=!0;break;case"alerts":s.alert=!0}})),s.modal&&u("#modal-wrapper").children.length&&h("#modal-wrapper dialog").forEach((e=>{e.open||e.showModal()})),s.alert&&u("#alerts").children.length&&h("#alerts .alert").forEach((e=>{if(!e.getAttribute("data-dismiss-at")){const t=Date.now()+c.ALERT_DISPLAY_LENGTH;e.setAttribute("data-dismiss-at",t.toString()),setTimeout((()=>{e.remove()}),c.ALERT_DISPLAY_LENGTH)}}))}));g.observe(u("#modal-wrapper"),{childList:!0}),g.observe(u("#alerts"),{childList:!0}),document.body.addEventListener("htmx:configRequest",(function(e){e.detail.headers["x-authtoken"]=l()})),document.body.addEventListener("htmx:load",(function(e){h(".disabled[data-block]").forEach((e=>{const t=parseInt(e.getAttribute("data-block")||"0");if(t>Date.now()){const s=t-Date.now();setTimeout((()=>{e.removeAttribute("disabled")}),s)}else e.removeAttribute("disabled")}))})),document.body.addEventListener("htmx:beforeSwap",(function(e){"chat-form"===e.target.id?u("#message").value="":"logout"===e.detail.serverResponse&&(localStorage.removeItem("authToken"),window.location.reload())}))},454:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ALERT_DISPLAY_LENGTH=t.STEP_DELAY=t.FIGHT_ATTACK_DELAY=void 0,t.FIGHT_ATTACK_DELAY=1500,t.STEP_DELAY=2e3,t.ALERT_DISPLAY_LENGTH=3e3},182:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TimeManager=void 0,t.TimeManager=class{constructor(e=120){this.dayLength=e,this.scaleFactor=30,this.dayLengthAsMS=60*e*1e3}dayScaleFactor(){return this.dayLength/30}getTimePeriod(){return this.isMorning()?"morning":this.isAfternoon()?"afternoon":this.isEvening()?"evening":this.isNight()?"night":void 0}getAmPm(){return this.get24Hour()<12?"am":"pm"}get24Hour(){const e=new Date,t=(e.getMinutes()+e.getHours()%2*(this.dayLength/2))/this.dayLength;return Math.floor(24*t)}getHour(){const e=this.get24Hour(),t=e>12?e-12:e;return 0===t?12:t}gradientName(){const e=Math.floor(this.get24Hour()/24*this.scaleFactor);return`sky-gradient-${e<10?"0":""}${e}`}isNight(){const e=this.get24Hour();return e>=0&&e<5||e>=21&&e<24}isMorning(){const e=this.get24Hour();return e>=5&&e<12}isAfternoon(){const e=this.get24Hour();return e>=12&&e<18}isEvening(){const e=this.get24Hour();return e>=18&&e<21}}},419:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.hasCORS=void 0;let s=!1;try{s="undefined"!=typeof XMLHttpRequest&&"withCredentials"in new XMLHttpRequest}catch(e){}t.hasCORS=s},754:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decode=t.encode=void 0,t.encode=function(e){let t="";for(let s in e)e.hasOwnProperty(s)&&(t.length&&(t+="&"),t+=encodeURIComponent(s)+"="+encodeURIComponent(e[s]));return t},t.decode=function(e){let t={},s=e.split("&");for(let e=0,n=s.length;e<n;e++){let n=s[e].split("=");t[decodeURIComponent(n[0])]=decodeURIComponent(n[1])}return t}},222:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parse=void 0;const s=/^(?:(?![^:@\/?#]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@\/?#]*)(?::([^:@\/?#]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,n=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.parse=function(e){const t=e,r=e.indexOf("["),o=e.indexOf("]");-1!=r&&-1!=o&&(e=e.substring(0,r)+e.substring(r,o).replace(/:/g,";")+e.substring(o,e.length));let i=s.exec(e||""),a={},c=14;for(;c--;)a[n[c]]=i[c]||"";return-1!=r&&-1!=o&&(a.source=t,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a.pathNames=function(e,t){const s=t.replace(/\/{2,9}/g,"/").split("/");return"/"!=t.slice(0,1)&&0!==t.length||s.splice(0,1),"/"==t.slice(-1)&&s.splice(s.length-1,1),s}(0,a.path),a.queryKey=function(e,t){const s={};return t.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(e,t,n){t&&(s[t]=n)})),s}(0,a.query),a}},726:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.yeast=t.decode=t.encode=void 0;const s="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),n={};let r,o=0,i=0;function a(e){let t="";do{t=s[e%64]+t,e=Math.floor(e/64)}while(e>0);return t}for(t.encode=a,t.decode=function(e){let t=0;for(i=0;i<e.length;i++)t=64*t+n[e.charAt(i)];return t},t.yeast=function(){const e=a(+new Date);return e!==r?(o=0,r=e):e+"."+a(o++)};i<64;i++)n[s[i]]=i},242:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.globalThisShim=void 0,t.globalThisShim="undefined"!=typeof self?self:"undefined"!=typeof window?window:Function("return this")()},679:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.nextTick=t.parse=t.installTimerFunctions=t.transports=t.Transport=t.protocol=t.Socket=void 0;const n=s(481);Object.defineProperty(t,"Socket",{enumerable:!0,get:function(){return n.Socket}}),t.protocol=n.Socket.protocol;var r=s(870);Object.defineProperty(t,"Transport",{enumerable:!0,get:function(){return r.Transport}});var o=s(385);Object.defineProperty(t,"transports",{enumerable:!0,get:function(){return o.transports}});var i=s(622);Object.defineProperty(t,"installTimerFunctions",{enumerable:!0,get:function(){return i.installTimerFunctions}});var a=s(222);Object.defineProperty(t,"parse",{enumerable:!0,get:function(){return a.parse}});var c=s(552);Object.defineProperty(t,"nextTick",{enumerable:!0,get:function(){return c.nextTick}})},481:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Socket=void 0;const r=s(385),o=s(622),i=s(754),a=s(222),c=n(s(802)),u=s(260),h=s(373),l=(0,c.default)("engine.io-client:socket");class d extends u.Emitter{constructor(e,t={}){super(),this.writeBuffer=[],e&&"object"==typeof e&&(t=e,e=null),e?(e=(0,a.parse)(e),t.hostname=e.host,t.secure="https"===e.protocol||"wss"===e.protocol,t.port=e.port,e.query&&(t.query=e.query)):t.host&&(t.hostname=(0,a.parse)(t.host).host),(0,o.installTimerFunctions)(this,t),this.secure=null!=t.secure?t.secure:"undefined"!=typeof location&&"https:"===location.protocol,t.hostname&&!t.port&&(t.port=this.secure?"443":"80"),this.hostname=t.hostname||("undefined"!=typeof location?location.hostname:"localhost"),this.port=t.port||("undefined"!=typeof location&&location.port?location.port:this.secure?"443":"80"),this.transports=t.transports||["polling","websocket"],this.writeBuffer=[],this.prevBufferLen=0,this.opts=Object.assign({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!0},t),this.opts.path=this.opts.path.replace(/\/$/,"")+(this.opts.addTrailingSlash?"/":""),"string"==typeof this.opts.query&&(this.opts.query=(0,i.decode)(this.opts.query)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingTimeoutTimer=null,"function"==typeof addEventListener&&(this.opts.closeOnBeforeunload&&(this.beforeunloadEventListener=()=>{this.transport&&(this.transport.removeAllListeners(),this.transport.close())},addEventListener("beforeunload",this.beforeunloadEventListener,!1)),"localhost"!==this.hostname&&(this.offlineEventListener=()=>{this.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",this.offlineEventListener,!1))),this.open()}createTransport(e){l('creating transport "%s"',e);const t=Object.assign({},this.opts.query);t.EIO=h.protocol,t.transport=e,this.id&&(t.sid=this.id);const s=Object.assign({},this.opts.transportOptions[e],this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return l("options: %j",s),new r.transports[e](s)}open(){let e;if(this.opts.rememberUpgrade&&d.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))e="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((()=>{this.emitReserved("error","No transports available")}),0);e=this.transports[0]}this.readyState="opening";try{e=this.createTransport(e)}catch(e){return l("error while creating transport: %s",e),this.transports.shift(),void this.open()}e.open(),this.setTransport(e)}setTransport(e){l("setting transport %s",e.name),this.transport&&(l("clearing existing transport %s",this.transport.name),this.transport.removeAllListeners()),this.transport=e,e.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(e=>this.onClose("transport close",e)))}probe(e){l('probing transport "%s"',e);let t=this.createTransport(e),s=!1;d.priorWebsocketSuccess=!1;const n=()=>{s||(l('probe transport "%s" opened',e),t.send([{type:"ping",data:"probe"}]),t.once("packet",(n=>{if(!s)if("pong"===n.type&&"probe"===n.data){if(l('probe transport "%s" pong',e),this.upgrading=!0,this.emitReserved("upgrading",t),!t)return;d.priorWebsocketSuccess="websocket"===t.name,l('pausing current transport "%s"',this.transport.name),this.transport.pause((()=>{s||"closed"!==this.readyState&&(l("changing transport and sending upgrade packet"),u(),this.setTransport(t),t.send([{type:"upgrade"}]),this.emitReserved("upgrade",t),t=null,this.upgrading=!1,this.flush())}))}else{l('probe transport "%s" failed',e);const s=new Error("probe error");s.transport=t.name,this.emitReserved("upgradeError",s)}})))};function r(){s||(s=!0,u(),t.close(),t=null)}const o=s=>{const n=new Error("probe error: "+s);n.transport=t.name,r(),l('probe transport "%s" failed because of error: %s',e,s),this.emitReserved("upgradeError",n)};function i(){o("transport closed")}function a(){o("socket closed")}function c(e){t&&e.name!==t.name&&(l('"%s" works - aborting "%s"',e.name,t.name),r())}const u=()=>{t.removeListener("open",n),t.removeListener("error",o),t.removeListener("close",i),this.off("close",a),this.off("upgrading",c)};t.once("open",n),t.once("error",o),t.once("close",i),this.once("close",a),this.once("upgrading",c),t.open()}onOpen(){if(l("socket open"),this.readyState="open",d.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade){l("starting upgrade probes");let e=0;const t=this.upgrades.length;for(;e<t;e++)this.probe(this.upgrades[e])}}onPacket(e){if("opening"===this.readyState||"open"===this.readyState||"closing"===this.readyState)switch(l('socket receive: type "%s", data "%s"',e.type,e.data),this.emitReserved("packet",e),this.emitReserved("heartbeat"),e.type){case"open":this.onHandshake(JSON.parse(e.data));break;case"ping":this.resetPingTimeout(),this.sendPacket("pong"),this.emitReserved("ping"),this.emitReserved("pong");break;case"error":const t=new Error("server error");t.code=e.data,this.onError(t);break;case"message":this.emitReserved("data",e.data),this.emitReserved("message",e.data)}else l('packet received with socket readyState "%s"',this.readyState)}onHandshake(e){this.emitReserved("handshake",e),this.id=e.sid,this.transport.query.sid=e.sid,this.upgrades=this.filterUpgrades(e.upgrades),this.pingInterval=e.pingInterval,this.pingTimeout=e.pingTimeout,this.maxPayload=e.maxPayload,this.onOpen(),"closed"!==this.readyState&&this.resetPingTimeout()}resetPingTimeout(){this.clearTimeoutFn(this.pingTimeoutTimer),this.pingTimeoutTimer=this.setTimeoutFn((()=>{this.onClose("ping timeout")}),this.pingInterval+this.pingTimeout),this.opts.autoUnref&&this.pingTimeoutTimer.unref()}onDrain(){this.writeBuffer.splice(0,this.prevBufferLen),this.prevBufferLen=0,0===this.writeBuffer.length?this.emitReserved("drain"):this.flush()}flush(){if("closed"!==this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){const e=this.getWritablePackets();l("flushing %d packets in socket",e.length),this.transport.send(e),this.prevBufferLen=e.length,this.emitReserved("flush")}}getWritablePackets(){if(!(this.maxPayload&&"polling"===this.transport.name&&this.writeBuffer.length>1))return this.writeBuffer;let e=1;for(let t=0;t<this.writeBuffer.length;t++){const s=this.writeBuffer[t].data;if(s&&(e+=(0,o.byteLength)(s)),t>0&&e>this.maxPayload)return l("only send %d out of %d packets",t,this.writeBuffer.length),this.writeBuffer.slice(0,t);e+=2}return l("payload size is %d (max: %d)",e,this.maxPayload),this.writeBuffer}write(e,t,s){return this.sendPacket("message",e,t,s),this}send(e,t,s){return this.sendPacket("message",e,t,s),this}sendPacket(e,t,s,n){if("function"==typeof t&&(n=t,t=void 0),"function"==typeof s&&(n=s,s=null),"closing"===this.readyState||"closed"===this.readyState)return;(s=s||{}).compress=!1!==s.compress;const r={type:e,data:t,options:s};this.emitReserved("packetCreate",r),this.writeBuffer.push(r),n&&this.once("flush",n),this.flush()}close(){const e=()=>{this.onClose("forced close"),l("socket closing - telling transport to close"),this.transport.close()},t=()=>{this.off("upgrade",t),this.off("upgradeError",t),e()},s=()=>{this.once("upgrade",t),this.once("upgradeError",t)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(()=>{this.upgrading?s():e()})):this.upgrading?s():e()),this}onError(e){l("socket error %j",e),d.priorWebsocketSuccess=!1,this.emitReserved("error",e),this.onClose("transport error",e)}onClose(e,t){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(l('socket close with reason: "%s"',e),this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&(removeEventListener("beforeunload",this.beforeunloadEventListener,!1),removeEventListener("offline",this.offlineEventListener,!1)),this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this.prevBufferLen=0)}filterUpgrades(e){const t=[];let s=0;const n=e.length;for(;s<n;s++)~this.transports.indexOf(e[s])&&t.push(e[s]);return t}}t.Socket=d,d.protocol=h.protocol},870:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Transport=void 0;const r=s(373),o=s(260),i=s(622),a=(0,n(s(802)).default)("engine.io-client:transport");class c extends Error{constructor(e,t,s){super(e),this.description=t,this.context=s,this.type="TransportError"}}class u extends o.Emitter{constructor(e){super(),this.writable=!1,(0,i.installTimerFunctions)(this,e),this.opts=e,this.query=e.query,this.socket=e.socket}onError(e,t,s){return super.emitReserved("error",new c(e,t,s)),this}open(){return this.readyState="opening",this.doOpen(),this}close(){return"opening"!==this.readyState&&"open"!==this.readyState||(this.doClose(),this.onClose()),this}send(e){"open"===this.readyState?this.write(e):a("transport is not open, discarding packets")}onOpen(){this.readyState="open",this.writable=!0,super.emitReserved("open")}onData(e){const t=(0,r.decodePacket)(e,this.socket.binaryType);this.onPacket(t)}onPacket(e){super.emitReserved("packet",e)}onClose(e){this.readyState="closed",super.emitReserved("close",e)}pause(e){}}t.Transport=u},385:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transports=void 0;const n=s(484),r=s(308);t.transports={websocket:r.WS,polling:n.Polling}},484:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Request=t.Polling=void 0;const r=s(870),o=n(s(802)),i=s(726),a=s(754),c=s(373),u=s(666),h=s(260),l=s(622),d=s(242),p=(0,o.default)("engine.io-client:polling");function f(){}const g=null!=new u.XHR({xdomain:!1}).responseType;class m extends r.Transport{constructor(e){if(super(e),this.polling=!1,"undefined"!=typeof location){const t="https:"===location.protocol;let s=location.port;s||(s=t?"443":"80"),this.xd="undefined"!=typeof location&&e.hostname!==location.hostname||s!==e.port,this.xs=e.secure!==t}const t=e&&e.forceBase64;this.supportsBinary=g&&!t}get name(){return"polling"}doOpen(){this.poll()}pause(e){this.readyState="pausing";const t=()=>{p("paused"),this.readyState="paused",e()};if(this.polling||!this.writable){let e=0;this.polling&&(p("we are currently polling - waiting to pause"),e++,this.once("pollComplete",(function(){p("pre-pause polling complete"),--e||t()}))),this.writable||(p("we are currently writing - waiting to pause"),e++,this.once("drain",(function(){p("pre-pause writing complete"),--e||t()})))}else t()}poll(){p("polling"),this.polling=!0,this.doPoll(),this.emitReserved("poll")}onData(e){p("polling got data %s",e),(0,c.decodePayload)(e,this.socket.binaryType).forEach((e=>{if("opening"===this.readyState&&"open"===e.type&&this.onOpen(),"close"===e.type)return this.onClose({description:"transport closed by the server"}),!1;this.onPacket(e)})),"closed"!==this.readyState&&(this.polling=!1,this.emitReserved("pollComplete"),"open"===this.readyState?this.poll():p('ignoring poll - transport state "%s"',this.readyState))}doClose(){const e=()=>{p("writing close packet"),this.write([{type:"close"}])};"open"===this.readyState?(p("transport open - closing"),e()):(p("transport not open - deferring close"),this.once("open",e))}write(e){this.writable=!1,(0,c.encodePayload)(e,(e=>{this.doWrite(e,(()=>{this.writable=!0,this.emitReserved("drain")}))}))}uri(){let e=this.query||{};const t=this.opts.secure?"https":"http";let s="";!1!==this.opts.timestampRequests&&(e[this.opts.timestampParam]=(0,i.yeast)()),this.supportsBinary||e.sid||(e.b64=1),this.opts.port&&("https"===t&&443!==Number(this.opts.port)||"http"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port);const n=(0,a.encode)(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(n.length?"?"+n:"")}request(e={}){return Object.assign(e,{xd:this.xd,xs:this.xs},this.opts),new y(this.uri(),e)}doWrite(e,t){const s=this.request({method:"POST",data:e});s.on("success",t),s.on("error",((e,t)=>{this.onError("xhr post error",e,t)}))}doPoll(){p("xhr poll");const e=this.request();e.on("data",this.onData.bind(this)),e.on("error",((e,t)=>{this.onError("xhr poll error",e,t)})),this.pollXhr=e}}t.Polling=m;class y extends h.Emitter{constructor(e,t){super(),(0,l.installTimerFunctions)(this,t),this.opts=t,this.method=t.method||"GET",this.uri=e,this.async=!1!==t.async,this.data=void 0!==t.data?t.data:null,this.create()}create(){const e=(0,l.pick)(this.opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");e.xdomain=!!this.opts.xd,e.xscheme=!!this.opts.xs;const t=this.xhr=new u.XHR(e);try{p("xhr open %s: %s",this.method,this.uri),t.open(this.method,this.uri,this.async);try{if(this.opts.extraHeaders){t.setDisableHeaderCheck&&t.setDisableHeaderCheck(!0);for(let e in this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(e)&&t.setRequestHeader(e,this.opts.extraHeaders[e])}}catch(e){}if("POST"===this.method)try{t.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(e){}try{t.setRequestHeader("Accept","*/*")}catch(e){}"withCredentials"in t&&(t.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(t.timeout=this.opts.requestTimeout),t.onreadystatechange=()=>{4===t.readyState&&(200===t.status||1223===t.status?this.onLoad():this.setTimeoutFn((()=>{this.onError("number"==typeof t.status?t.status:0)}),0))},p("xhr data %s",this.data),t.send(this.data)}catch(e){return void this.setTimeoutFn((()=>{this.onError(e)}),0)}"undefined"!=typeof document&&(this.index=y.requestsCount++,y.requests[this.index]=this)}onError(e){this.emitReserved("error",e,this.xhr),this.cleanup(!0)}cleanup(e){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=f,e)try{this.xhr.abort()}catch(e){}"undefined"!=typeof document&&delete y.requests[this.index],this.xhr=null}}onLoad(){const e=this.xhr.responseText;null!==e&&(this.emitReserved("data",e),this.emitReserved("success"),this.cleanup())}abort(){this.cleanup()}}if(t.Request=y,y.requestsCount=0,y.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",C);else if("function"==typeof addEventListener){const e="onpagehide"in d.globalThisShim?"pagehide":"unload";addEventListener(e,C,!1)}function C(){for(let e in y.requests)y.requests.hasOwnProperty(e)&&y.requests[e].abort()}},552:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultBinaryType=t.usingBrowserWebSocket=t.WebSocket=t.nextTick=void 0;const n=s(242);t.nextTick="function"==typeof Promise&&"function"==typeof Promise.resolve?e=>Promise.resolve().then(e):(e,t)=>t(e,0),t.WebSocket=n.globalThisShim.WebSocket||n.globalThisShim.MozWebSocket,t.usingBrowserWebSocket=!0,t.defaultBinaryType="arraybuffer"},308:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.WS=void 0;const r=s(870),o=s(754),i=s(726),a=s(622),c=s(552),u=n(s(802)),h=s(373),l=(0,u.default)("engine.io-client:websocket"),d="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase();class p extends r.Transport{constructor(e){super(e),this.supportsBinary=!e.forceBase64}get name(){return"websocket"}doOpen(){if(!this.check())return;const e=this.uri(),t=this.opts.protocols,s=d?{}:(0,a.pick)(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(s.headers=this.opts.extraHeaders);try{this.ws=c.usingBrowserWebSocket&&!d?t?new c.WebSocket(e,t):new c.WebSocket(e):new c.WebSocket(e,t,s)}catch(e){return this.emitReserved("error",e)}this.ws.binaryType=this.socket.binaryType||c.defaultBinaryType,this.addEventListeners()}addEventListeners(){this.ws.onopen=()=>{this.opts.autoUnref&&this.ws._socket.unref(),this.onOpen()},this.ws.onclose=e=>this.onClose({description:"websocket connection closed",context:e}),this.ws.onmessage=e=>this.onData(e.data),this.ws.onerror=e=>this.onError("websocket error",e)}write(e){this.writable=!1;for(let t=0;t<e.length;t++){const s=e[t],n=t===e.length-1;(0,h.encodePacket)(s,this.supportsBinary,(e=>{const t={};!c.usingBrowserWebSocket&&(s.options&&(t.compress=s.options.compress),this.opts.perMessageDeflate)&&("string"==typeof e?Buffer.byteLength(e):e.length)<this.opts.perMessageDeflate.threshold&&(t.compress=!1);try{c.usingBrowserWebSocket?this.ws.send(e):this.ws.send(e,t)}catch(e){l("websocket closed before onclose event")}n&&(0,c.nextTick)((()=>{this.writable=!0,this.emitReserved("drain")}),this.setTimeoutFn)}))}}doClose(){void 0!==this.ws&&(this.ws.close(),this.ws=null)}uri(){let e=this.query||{};const t=this.opts.secure?"wss":"ws";let s="";this.opts.port&&("wss"===t&&443!==Number(this.opts.port)||"ws"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port),this.opts.timestampRequests&&(e[this.opts.timestampParam]=(0,i.yeast)()),this.supportsBinary||(e.b64=1);const n=(0,o.encode)(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(n.length?"?"+n:"")}check(){return!!c.WebSocket}}t.WS=p},666:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.XHR=void 0;const n=s(419),r=s(242);t.XHR=function(e){const t=e.xdomain;try{if("undefined"!=typeof XMLHttpRequest&&(!t||n.hasCORS))return new XMLHttpRequest}catch(e){}if(!t)try{return new(r.globalThisShim[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(e){}}},622:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.byteLength=t.installTimerFunctions=t.pick=void 0;const n=s(242);t.pick=function(e,...t){return t.reduce(((t,s)=>(e.hasOwnProperty(s)&&(t[s]=e[s]),t)),{})};const r=n.globalThisShim.setTimeout,o=n.globalThisShim.clearTimeout;t.installTimerFunctions=function(e,t){t.useNativeTimers?(e.setTimeoutFn=r.bind(n.globalThisShim),e.clearTimeoutFn=o.bind(n.globalThisShim)):(e.setTimeoutFn=n.globalThisShim.setTimeout.bind(n.globalThisShim),e.clearTimeoutFn=n.globalThisShim.clearTimeout.bind(n.globalThisShim))},t.byteLength=function(e){return"string"==typeof e?function(e){let t=0,s=0;for(let n=0,r=e.length;n<r;n++)t=e.charCodeAt(n),t<128?s+=1:t<2048?s+=2:t<55296||t>=57344?s+=3:(n++,s+=4);return s}(e):Math.ceil(1.33*(e.byteLength||e.size))}},87:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ERROR_PACKET=t.PACKET_TYPES_REVERSE=t.PACKET_TYPES=void 0;const s=Object.create(null);t.PACKET_TYPES=s,s.open="0",s.close="1",s.ping="2",s.pong="3",s.message="4",s.upgrade="5",s.noop="6";const n=Object.create(null);t.PACKET_TYPES_REVERSE=n,Object.keys(s).forEach((e=>{n[s[e]]=e})),t.ERROR_PACKET={type:"error",data:"parser error"}},469:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decode=t.encode=void 0;const s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n="undefined"==typeof Uint8Array?[]:new Uint8Array(256);for(let e=0;e<64;e++)n[s.charCodeAt(e)]=e;t.encode=e=>{let t,n=new Uint8Array(e),r=n.length,o="";for(t=0;t<r;t+=3)o+=s[n[t]>>2],o+=s[(3&n[t])<<4|n[t+1]>>4],o+=s[(15&n[t+1])<<2|n[t+2]>>6],o+=s[63&n[t+2]];return r%3==2?o=o.substring(0,o.length-1)+"=":r%3==1&&(o=o.substring(0,o.length-2)+"=="),o},t.decode=e=>{let t,s,r,o,i,a=.75*e.length,c=e.length,u=0;"="===e[e.length-1]&&(a--,"="===e[e.length-2]&&a--);const h=new ArrayBuffer(a),l=new Uint8Array(h);for(t=0;t<c;t+=4)s=n[e.charCodeAt(t)],r=n[e.charCodeAt(t+1)],o=n[e.charCodeAt(t+2)],i=n[e.charCodeAt(t+3)],l[u++]=s<<2|r>>4,l[u++]=(15&r)<<4|o>>2,l[u++]=(3&o)<<6|63&i;return h}},572:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=s(87),r=s(469),o="function"==typeof ArrayBuffer,i=(e,t)=>{if(o){const s=(0,r.decode)(e);return a(s,t)}return{base64:!0,data:e}},a=(e,t)=>"blob"===t&&e instanceof ArrayBuffer?new Blob([e]):e;t.default=(e,t)=>{if("string"!=typeof e)return{type:"message",data:a(e,t)};const s=e.charAt(0);return"b"===s?{type:"message",data:i(e.substring(1),t)}:n.PACKET_TYPES_REVERSE[s]?e.length>1?{type:n.PACKET_TYPES_REVERSE[s],data:e.substring(1)}:{type:n.PACKET_TYPES_REVERSE[s]}:n.ERROR_PACKET}},908:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=s(87),r="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),o="function"==typeof ArrayBuffer,i=(e,t)=>{const s=new FileReader;return s.onload=function(){const e=s.result.split(",")[1];t("b"+(e||""))},s.readAsDataURL(e)};t.default=({type:e,data:t},s,a)=>{return r&&t instanceof Blob?s?a(t):i(t,a):o&&(t instanceof ArrayBuffer||(c=t,"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(c):c&&c.buffer instanceof ArrayBuffer))?s?a(t):i(new Blob([t]),a):a(n.PACKET_TYPES[e]+(t||""));var c}},373:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decodePayload=t.decodePacket=t.encodePayload=t.encodePacket=t.protocol=void 0;const n=s(908);t.encodePacket=n.default;const r=s(572);t.decodePacket=r.default;const o=String.fromCharCode(30);t.encodePayload=(e,t)=>{const s=e.length,r=new Array(s);let i=0;e.forEach(((e,a)=>{(0,n.default)(e,!1,(e=>{r[a]=e,++i===s&&t(r.join(o))}))}))},t.decodePayload=(e,t)=>{const s=e.split(o),n=[];for(let e=0;e<s.length;e++){const o=(0,r.default)(s[e],t);if(n.push(o),"error"===o.type)break}return n},t.protocol=4},159:(e,t)=>{"use strict";function s(e){e=e||{},this.ms=e.min||100,this.max=e.max||1e4,this.factor=e.factor||2,this.jitter=e.jitter>0&&e.jitter<=1?e.jitter:0,this.attempts=0}Object.defineProperty(t,"__esModule",{value:!0}),t.Backoff=void 0,t.Backoff=s,s.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),s=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-s:e+s}return 0|Math.min(e,this.max)},s.prototype.reset=function(){this.attempts=0},s.prototype.setMin=function(e){this.ms=e},s.prototype.setMax=function(e){this.max=e},s.prototype.setJitter=function(e){this.jitter=e}},46:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.connect=t.io=t.Socket=t.Manager=t.protocol=void 0;const r=s(84),o=s(168);Object.defineProperty(t,"Manager",{enumerable:!0,get:function(){return o.Manager}});const i=s(312);Object.defineProperty(t,"Socket",{enumerable:!0,get:function(){return i.Socket}});const a=n(s(669)).default("socket.io-client"),c={};function u(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};const s=r.url(e,t.path||"/socket.io"),n=s.source,i=s.id,u=s.path,h=c[i]&&u in c[i].nsps;let l;return t.forceNew||t["force new connection"]||!1===t.multiplex||h?(a("ignoring socket cache for %s",n),l=new o.Manager(n,t)):(c[i]||(a("new io instance for %s",n),c[i]=new o.Manager(n,t)),l=c[i]),s.query&&!t.query&&(t.query=s.queryKey),l.socket(s.path,t)}t.io=u,t.connect=u,t.default=u,Object.assign(u,{Manager:o.Manager,Socket:i.Socket,io:u,connect:u});var h=s(514);Object.defineProperty(t,"protocol",{enumerable:!0,get:function(){return h.protocol}}),e.exports=u},168:function(e,t,s){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[s]}})}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var s in e)"default"!==s&&Object.prototype.hasOwnProperty.call(e,s)&&n(t,e,s);return r(t,e),t},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Manager=void 0;const a=s(679),c=s(312),u=o(s(514)),h=s(149),l=s(159),d=s(260),p=i(s(669)).default("socket.io-client:manager");class f extends d.Emitter{constructor(e,t){var s;super(),this.nsps={},this.subs=[],e&&"object"==typeof e&&(t=e,e=void 0),(t=t||{}).path=t.path||"/socket.io",this.opts=t,a.installTimerFunctions(this,t),this.reconnection(!1!==t.reconnection),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor(null!==(s=t.randomizationFactor)&&void 0!==s?s:.5),this.backoff=new l.Backoff({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(null==t.timeout?2e4:t.timeout),this._readyState="closed",this.uri=e;const n=t.parser||u;this.encoder=new n.Encoder,this.decoder=new n.Decoder,this._autoConnect=!1!==t.autoConnect,this._autoConnect&&this.open()}reconnection(e){return arguments.length?(this._reconnection=!!e,this):this._reconnection}reconnectionAttempts(e){return void 0===e?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}reconnectionDelay(e){var t;return void 0===e?this._reconnectionDelay:(this._reconnectionDelay=e,null===(t=this.backoff)||void 0===t||t.setMin(e),this)}randomizationFactor(e){var t;return void 0===e?this._randomizationFactor:(this._randomizationFactor=e,null===(t=this.backoff)||void 0===t||t.setJitter(e),this)}reconnectionDelayMax(e){var t;return void 0===e?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,null===(t=this.backoff)||void 0===t||t.setMax(e),this)}timeout(e){return arguments.length?(this._timeout=e,this):this._timeout}maybeReconnectOnOpen(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}open(e){if(p("readyState %s",this._readyState),~this._readyState.indexOf("open"))return this;p("opening %s",this.uri),this.engine=new a.Socket(this.uri,this.opts);const t=this.engine,s=this;this._readyState="opening",this.skipReconnect=!1;const n=h.on(t,"open",(function(){s.onopen(),e&&e()})),r=h.on(t,"error",(t=>{p("error"),s.cleanup(),s._readyState="closed",this.emitReserved("error",t),e?e(t):s.maybeReconnectOnOpen()}));if(!1!==this._timeout){const e=this._timeout;p("connect attempt will timeout after %d",e),0===e&&n();const s=this.setTimeoutFn((()=>{p("connect attempt timed out after %d",e),n(),t.close(),t.emit("error",new Error("timeout"))}),e);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}return this.subs.push(n),this.subs.push(r),this}connect(e){return this.open(e)}onopen(){p("open"),this.cleanup(),this._readyState="open",this.emitReserved("open");const e=this.engine;this.subs.push(h.on(e,"ping",this.onping.bind(this)),h.on(e,"data",this.ondata.bind(this)),h.on(e,"error",this.onerror.bind(this)),h.on(e,"close",this.onclose.bind(this)),h.on(this.decoder,"decoded",this.ondecoded.bind(this)))}onping(){this.emitReserved("ping")}ondata(e){try{this.decoder.add(e)}catch(e){this.onclose("parse error",e)}}ondecoded(e){a.nextTick((()=>{this.emitReserved("packet",e)}),this.setTimeoutFn)}onerror(e){p("error",e),this.emitReserved("error",e)}socket(e,t){let s=this.nsps[e];return s?this._autoConnect&&!s.active&&s.connect():(s=new c.Socket(this,e,t),this.nsps[e]=s),s}_destroy(e){const t=Object.keys(this.nsps);for(const e of t)if(this.nsps[e].active)return void p("socket %s is still active, skipping close",e);this._close()}_packet(e){p("writing packet %j",e);const t=this.encoder.encode(e);for(let s=0;s<t.length;s++)this.engine.write(t[s],e.options)}cleanup(){p("cleanup"),this.subs.forEach((e=>e())),this.subs.length=0,this.decoder.destroy()}_close(){p("disconnect"),this.skipReconnect=!0,this._reconnecting=!1,this.onclose("forced close"),this.engine&&this.engine.close()}disconnect(){return this._close()}onclose(e,t){p("closed due to %s",e),this.cleanup(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect()}reconnect(){if(this._reconnecting||this.skipReconnect)return this;const e=this;if(this.backoff.attempts>=this._reconnectionAttempts)p("reconnect failed"),this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{const t=this.backoff.duration();p("will wait %dms before reconnect attempt",t),this._reconnecting=!0;const s=this.setTimeoutFn((()=>{e.skipReconnect||(p("attempting reconnect"),this.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((t=>{t?(p("reconnect attempt error"),e._reconnecting=!1,e.reconnect(),this.emitReserved("reconnect_error",t)):(p("reconnect success"),e.onreconnect())})))}),t);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}}onreconnect(){const e=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",e)}}t.Manager=f},149:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.on=void 0,t.on=function(e,t,s){return e.on(t,s),function(){e.off(t,s)}}},312:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Socket=void 0;const r=s(514),o=s(149),i=s(260),a=n(s(669)).default("socket.io-client:socket"),c=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1});class u extends i.Emitter{constructor(e,t,s){super(),this.connected=!1,this.recovered=!1,this.receiveBuffer=[],this.sendBuffer=[],this._queue=[],this._queueSeq=0,this.ids=0,this.acks={},this.flags={},this.io=e,this.nsp=t,s&&s.auth&&(this.auth=s.auth),this._opts=Object.assign({},s),this.io._autoConnect&&this.open()}get disconnected(){return!this.connected}subEvents(){if(this.subs)return;const e=this.io;this.subs=[o.on(e,"open",this.onopen.bind(this)),o.on(e,"packet",this.onpacket.bind(this)),o.on(e,"error",this.onerror.bind(this)),o.on(e,"close",this.onclose.bind(this))]}get active(){return!!this.subs}connect(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}open(){return this.connect()}send(...e){return e.unshift("message"),this.emit.apply(this,e),this}emit(e,...t){if(c.hasOwnProperty(e))throw new Error('"'+e.toString()+'" is a reserved event name');if(t.unshift(e),this._opts.retries&&!this.flags.fromQueue&&!this.flags.volatile)return this._addToQueue(t),this;const s={type:r.PacketType.EVENT,data:t,options:{}};if(s.options.compress=!1!==this.flags.compress,"function"==typeof t[t.length-1]){const e=this.ids++;a("emitting packet with ack id %d",e);const n=t.pop();this._registerAckCallback(e,n),s.id=e}const n=this.io.engine&&this.io.engine.transport&&this.io.engine.transport.writable;return!this.flags.volatile||n&&this.connected?this.connected?(this.notifyOutgoingListeners(s),this.packet(s)):this.sendBuffer.push(s):a("discard packet as the transport is not currently writable"),this.flags={},this}_registerAckCallback(e,t){var s;const n=null!==(s=this.flags.timeout)&&void 0!==s?s:this._opts.ackTimeout;if(void 0===n)return void(this.acks[e]=t);const r=this.io.setTimeoutFn((()=>{delete this.acks[e];for(let t=0;t<this.sendBuffer.length;t++)this.sendBuffer[t].id===e&&(a("removing packet with ack id %d from the buffer",e),this.sendBuffer.splice(t,1));a("event with ack id %d has timed out after %d ms",e,n),t.call(this,new Error("operation has timed out"))}),n);this.acks[e]=(...e)=>{this.io.clearTimeoutFn(r),t.apply(this,[null,...e])}}emitWithAck(e,...t){const s=void 0!==this.flags.timeout||void 0!==this._opts.ackTimeout;return new Promise(((n,r)=>{t.push(((e,t)=>s?e?r(e):n(t):n(e))),this.emit(e,...t)}))}_addToQueue(e){let t;"function"==typeof e[e.length-1]&&(t=e.pop());const s={id:this._queueSeq++,tryCount:0,pending:!1,args:e,flags:Object.assign({fromQueue:!0},this.flags)};e.push(((e,...n)=>{if(s===this._queue[0])return null!==e?s.tryCount>this._opts.retries&&(a("packet [%d] is discarded after %d tries",s.id,s.tryCount),this._queue.shift(),t&&t(e)):(a("packet [%d] was successfully sent",s.id),this._queue.shift(),t&&t(null,...n)),s.pending=!1,this._drainQueue()})),this._queue.push(s),this._drainQueue()}_drainQueue(e=!1){if(a("draining queue"),!this.connected||0===this._queue.length)return;const t=this._queue[0];!t.pending||e?(t.pending=!0,t.tryCount++,a("sending packet [%d] (try n°%d)",t.id,t.tryCount),this.flags=t.flags,this.emit.apply(this,t.args)):a("packet [%d] has already been sent and is waiting for an ack",t.id)}packet(e){e.nsp=this.nsp,this.io._packet(e)}onopen(){a("transport is open - connecting"),"function"==typeof this.auth?this.auth((e=>{this._sendConnectPacket(e)})):this._sendConnectPacket(this.auth)}_sendConnectPacket(e){this.packet({type:r.PacketType.CONNECT,data:this._pid?Object.assign({pid:this._pid,offset:this._lastOffset},e):e})}onerror(e){this.connected||this.emitReserved("connect_error",e)}onclose(e,t){a("close (%s)",e),this.connected=!1,delete this.id,this.emitReserved("disconnect",e,t)}onpacket(e){if(e.nsp===this.nsp)switch(e.type){case r.PacketType.CONNECT:e.data&&e.data.sid?this.onconnect(e.data.sid,e.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case r.PacketType.EVENT:case r.PacketType.BINARY_EVENT:this.onevent(e);break;case r.PacketType.ACK:case r.PacketType.BINARY_ACK:this.onack(e);break;case r.PacketType.DISCONNECT:this.ondisconnect();break;case r.PacketType.CONNECT_ERROR:this.destroy();const t=new Error(e.data.message);t.data=e.data.data,this.emitReserved("connect_error",t)}}onevent(e){const t=e.data||[];a("emitting event %j",t),null!=e.id&&(a("attaching ack callback to event"),t.push(this.ack(e.id))),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t))}emitEvent(e){if(this._anyListeners&&this._anyListeners.length){const t=this._anyListeners.slice();for(const s of t)s.apply(this,e)}super.emit.apply(this,e),this._pid&&e.length&&"string"==typeof e[e.length-1]&&(this._lastOffset=e[e.length-1])}ack(e){const t=this;let s=!1;return function(...n){s||(s=!0,a("sending ack %j",n),t.packet({type:r.PacketType.ACK,id:e,data:n}))}}onack(e){const t=this.acks[e.id];"function"==typeof t?(a("calling ack %s with %j",e.id,e.data),t.apply(this,e.data),delete this.acks[e.id]):a("bad ack %s",e.id)}onconnect(e,t){a("socket connected with id %s",e),this.id=e,this.recovered=t&&this._pid===t,this._pid=t,this.connected=!0,this.emitBuffered(),this.emitReserved("connect"),this._drainQueue(!0)}emitBuffered(){this.receiveBuffer.forEach((e=>this.emitEvent(e))),this.receiveBuffer=[],this.sendBuffer.forEach((e=>{this.notifyOutgoingListeners(e),this.packet(e)})),this.sendBuffer=[]}ondisconnect(){a("server disconnect (%s)",this.nsp),this.destroy(),this.onclose("io server disconnect")}destroy(){this.subs&&(this.subs.forEach((e=>e())),this.subs=void 0),this.io._destroy(this)}disconnect(){return this.connected&&(a("performing disconnect (%s)",this.nsp),this.packet({type:r.PacketType.DISCONNECT})),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}close(){return this.disconnect()}compress(e){return this.flags.compress=e,this}get volatile(){return this.flags.volatile=!0,this}timeout(e){return this.flags.timeout=e,this}onAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}prependAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}offAny(e){if(!this._anyListeners)return this;if(e){const t=this._anyListeners;for(let s=0;s<t.length;s++)if(e===t[s])return t.splice(s,1),this}else this._anyListeners=[];return this}listenersAny(){return this._anyListeners||[]}onAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.push(e),this}prependAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.unshift(e),this}offAnyOutgoing(e){if(!this._anyOutgoingListeners)return this;if(e){const t=this._anyOutgoingListeners;for(let s=0;s<t.length;s++)if(e===t[s])return t.splice(s,1),this}else this._anyOutgoingListeners=[];return this}listenersAnyOutgoing(){return this._anyOutgoingListeners||[]}notifyOutgoingListeners(e){if(this._anyOutgoingListeners&&this._anyOutgoingListeners.length){const t=this._anyOutgoingListeners.slice();for(const s of t)s.apply(this,e.data)}}}t.Socket=u},84:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.url=void 0;const r=s(679),o=n(s(669)).default("socket.io-client:url");t.url=function(e,t="",s){let n=e;s=s||"undefined"!=typeof location&&location,null==e&&(e=s.protocol+"//"+s.host),"string"==typeof e&&("/"===e.charAt(0)&&(e="/"===e.charAt(1)?s.protocol+e:s.host+e),/^(https?|wss?):\/\//.test(e)||(o("protocol-less url %s",e),e=void 0!==s?s.protocol+"//"+e:"https://"+e),o("parse %s",e),n=r.parse(e)),n.port||(/^(http|ws)$/.test(n.protocol)?n.port="80":/^(http|ws)s$/.test(n.protocol)&&(n.port="443")),n.path=n.path||"/";const i=-1!==n.host.indexOf(":")?"["+n.host+"]":n.host;return n.id=n.protocol+"://"+i+":"+n.port+t,n.href=n.protocol+"://"+i+(s&&s.port===n.port?"":":"+n.port),n}},880:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reconstructPacket=t.deconstructPacket=void 0;const n=s(665);function r(e,t){if(!e)return e;if((0,n.isBinary)(e)){const s={_placeholder:!0,num:t.length};return t.push(e),s}if(Array.isArray(e)){const s=new Array(e.length);for(let n=0;n<e.length;n++)s[n]=r(e[n],t);return s}if("object"==typeof e&&!(e instanceof Date)){const s={};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&(s[n]=r(e[n],t));return s}return e}function o(e,t){if(!e)return e;if(e&&!0===e._placeholder){if("number"==typeof e.num&&e.num>=0&&e.num<t.length)return t[e.num];throw new Error("illegal attachments")}if(Array.isArray(e))for(let s=0;s<e.length;s++)e[s]=o(e[s],t);else if("object"==typeof e)for(const s in e)Object.prototype.hasOwnProperty.call(e,s)&&(e[s]=o(e[s],t));return e}t.deconstructPacket=function(e){const t=[],s=e.data,n=e;return n.data=r(s,t),n.attachments=t.length,{packet:n,buffers:t}},t.reconstructPacket=function(e,t){return e.data=o(e.data,t),delete e.attachments,e}},514:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Decoder=t.Encoder=t.PacketType=t.protocol=void 0;const n=s(260),r=s(880),o=s(665),i=(0,s(618).default)("socket.io-parser");var a;t.protocol=5,function(e){e[e.CONNECT=0]="CONNECT",e[e.DISCONNECT=1]="DISCONNECT",e[e.EVENT=2]="EVENT",e[e.ACK=3]="ACK",e[e.CONNECT_ERROR=4]="CONNECT_ERROR",e[e.BINARY_EVENT=5]="BINARY_EVENT",e[e.BINARY_ACK=6]="BINARY_ACK"}(a=t.PacketType||(t.PacketType={})),t.Encoder=class{constructor(e){this.replacer=e}encode(e){return i("encoding packet %j",e),e.type!==a.EVENT&&e.type!==a.ACK||!(0,o.hasBinary)(e)?[this.encodeAsString(e)]:this.encodeAsBinary({type:e.type===a.EVENT?a.BINARY_EVENT:a.BINARY_ACK,nsp:e.nsp,data:e.data,id:e.id})}encodeAsString(e){let t=""+e.type;return e.type!==a.BINARY_EVENT&&e.type!==a.BINARY_ACK||(t+=e.attachments+"-"),e.nsp&&"/"!==e.nsp&&(t+=e.nsp+","),null!=e.id&&(t+=e.id),null!=e.data&&(t+=JSON.stringify(e.data,this.replacer)),i("encoded %j as %s",e,t),t}encodeAsBinary(e){const t=(0,r.deconstructPacket)(e),s=this.encodeAsString(t.packet),n=t.buffers;return n.unshift(s),n}};class c extends n.Emitter{constructor(e){super(),this.reviver=e}add(e){let t;if("string"==typeof e){if(this.reconstructor)throw new Error("got plaintext data when reconstructing a packet");t=this.decodeString(e);const s=t.type===a.BINARY_EVENT;s||t.type===a.BINARY_ACK?(t.type=s?a.EVENT:a.ACK,this.reconstructor=new u(t),0===t.attachments&&super.emitReserved("decoded",t)):super.emitReserved("decoded",t)}else{if(!(0,o.isBinary)(e)&&!e.base64)throw new Error("Unknown type: "+e);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");t=this.reconstructor.takeBinaryData(e),t&&(this.reconstructor=null,super.emitReserved("decoded",t))}}decodeString(e){let t=0;const s={type:Number(e.charAt(0))};if(void 0===a[s.type])throw new Error("unknown packet type "+s.type);if(s.type===a.BINARY_EVENT||s.type===a.BINARY_ACK){const n=t+1;for(;"-"!==e.charAt(++t)&&t!=e.length;);const r=e.substring(n,t);if(r!=Number(r)||"-"!==e.charAt(t))throw new Error("Illegal attachments");s.attachments=Number(r)}if("/"===e.charAt(t+1)){const n=t+1;for(;++t&&","!==e.charAt(t)&&t!==e.length;);s.nsp=e.substring(n,t)}else s.nsp="/";const n=e.charAt(t+1);if(""!==n&&Number(n)==n){const n=t+1;for(;++t;){const s=e.charAt(t);if(null==s||Number(s)!=s){--t;break}if(t===e.length)break}s.id=Number(e.substring(n,t+1))}if(e.charAt(++t)){const n=this.tryParse(e.substr(t));if(!c.isPayloadValid(s.type,n))throw new Error("invalid payload");s.data=n}return i("decoded %s as %j",e,s),s}tryParse(e){try{return JSON.parse(e,this.reviver)}catch(e){return!1}}static isPayloadValid(e,t){switch(e){case a.CONNECT:return"object"==typeof t;case a.DISCONNECT:return void 0===t;case a.CONNECT_ERROR:return"string"==typeof t||"object"==typeof t;case a.EVENT:case a.BINARY_EVENT:return Array.isArray(t)&&("string"==typeof t[0]||"number"==typeof t[0]);case a.ACK:case a.BINARY_ACK:return Array.isArray(t)}}destroy(){this.reconstructor&&(this.reconstructor.finishedReconstruction(),this.reconstructor=null)}}t.Decoder=c;class u{constructor(e){this.packet=e,this.buffers=[],this.reconPack=e}takeBinaryData(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){const e=(0,r.reconstructPacket)(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}finishedReconstruction(){this.reconPack=null,this.buffers=[]}}},665:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.hasBinary=t.isBinary=void 0;const s="function"==typeof ArrayBuffer,n=Object.prototype.toString,r="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===n.call(Blob),o="function"==typeof File||"undefined"!=typeof File&&"[object FileConstructor]"===n.call(File);function i(e){return s&&(e instanceof ArrayBuffer||(e=>"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):e.buffer instanceof ArrayBuffer)(e))||r&&e instanceof Blob||o&&e instanceof File}t.isBinary=i,t.hasBinary=function e(t,s){if(!t||"object"!=typeof t)return!1;if(Array.isArray(t)){for(let s=0,n=t.length;s<n;s++)if(e(t[s]))return!0;return!1}if(i(t))return!0;if(t.toJSON&&"function"==typeof t.toJSON&&1===arguments.length)return e(t.toJSON(),!0);for(const s in t)if(Object.prototype.hasOwnProperty.call(t,s)&&e(t[s]))return!0;return!1}},260:(e,t,s)=>{"use strict";function n(e){if(e)return function(e){for(var t in n.prototype)e[t]=n.prototype[t];return e}(e)}s.r(t),s.d(t,{Emitter:()=>n}),n.prototype.on=n.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},n.prototype.once=function(e,t){function s(){this.off(e,s),t.apply(this,arguments)}return s.fn=t,this.on(e,s),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var s,n=this._callbacks["$"+e];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var r=0;r<n.length;r++)if((s=n[r])===t||s.fn===t){n.splice(r,1);break}return 0===n.length&&delete this._callbacks["$"+e],this},n.prototype.emit=function(e){this._callbacks=this._callbacks||{};for(var t=new Array(arguments.length-1),s=this._callbacks["$"+e],n=1;n<arguments.length;n++)t[n-1]=arguments[n];if(s){n=0;for(var r=(s=s.slice(0)).length;n<r;++n)s[n].apply(this,t)}return this},n.prototype.emitReserved=n.prototype.emit,n.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},n.prototype.hasListeners=function(e){return!!this.listeners(e).length}}},t={};function s(n){var r=t[n];if(void 0!==r)return r.exports;var o=t[n]={exports:{}};return e[n].call(o.exports,o,o.exports,s),o.exports}s.d=(e,t)=>{for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s(628)})();
\ No newline at end of file
+(()=>{var e={802:(e,t,s)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const s="color: "+this.color;t.splice(1,0,s,"color: inherit");let n=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(n++,"%c"===e&&(r=n))})),t.splice(r,0,s)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=s(804)(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},804:(e,t,s)=>{e.exports=function(e){function t(e){let s,r,o,i=null;function a(...e){if(!a.enabled)return;const n=a,r=Number(new Date),o=r-(s||r);n.diff=o,n.prev=s,n.curr=r,s=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((s,r)=>{if("%%"===s)return"%";i++;const o=t.formatters[r];if("function"==typeof o){const t=e[i];s=o.call(n,t),e.splice(i,1),i--}return s})),t.formatArgs.call(n,e),(n.log||t.log).apply(n,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=n,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(r!==t.namespaces&&(r=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(a),a}function n(e,s){const n=t(this.namespace+(void 0===s?":":s)+e);return n.log=this.log,n}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let s;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const n=("string"==typeof e?e:"").split(/[\s,]+/),r=n.length;for(s=0;s<r;s++)n[s]&&("-"===(e=n[s].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.slice(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let s,n;for(s=0,n=t.skips.length;s<n;s++)if(t.skips[s].test(e))return!1;for(s=0,n=t.names.length;s<n;s++)if(t.names[s].test(e))return!0;return!1},t.humanize=s(810),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((s=>{t[s]=e[s]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t),s|=0;return t.colors[Math.abs(s)%t.colors.length]},t.enable(t.load()),t}},810:e=>{var t=1e3,s=60*t,n=60*s,r=24*n;function o(e,t,s,n){var r=t>=1.5*s;return Math.round(e/s)+" "+n+(r?"s":"")}e.exports=function(e,i){i=i||{};var a,c,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var o=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(o){var i=parseFloat(o[1]);switch((o[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*i;case"weeks":case"week":case"w":return 6048e5*i;case"days":case"day":case"d":return i*r;case"hours":case"hour":case"hrs":case"hr":case"h":return i*n;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}(e);if("number"===u&&isFinite(e))return i.long?(a=e,(c=Math.abs(a))>=r?o(a,c,r,"day"):c>=n?o(a,c,n,"hour"):c>=s?o(a,c,s,"minute"):c>=t?o(a,c,t,"second"):a+" ms"):function(e){var o=Math.abs(e);return o>=r?Math.round(e/r)+"d":o>=n?Math.round(e/n)+"h":o>=s?Math.round(e/s)+"m":o>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},669:(e,t,s)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const s="color: "+this.color;t.splice(1,0,s,"color: inherit");let n=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(n++,"%c"===e&&(r=n))})),t.splice(r,0,s)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=s(231)(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},231:(e,t,s)=>{e.exports=function(e){function t(e){let s,r,o,i=null;function a(...e){if(!a.enabled)return;const n=a,r=Number(new Date),o=r-(s||r);n.diff=o,n.prev=s,n.curr=r,s=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((s,r)=>{if("%%"===s)return"%";i++;const o=t.formatters[r];if("function"==typeof o){const t=e[i];s=o.call(n,t),e.splice(i,1),i--}return s})),t.formatArgs.call(n,e),(n.log||t.log).apply(n,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=n,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(r!==t.namespaces&&(r=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(a),a}function n(e,s){const n=t(this.namespace+(void 0===s?":":s)+e);return n.log=this.log,n}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let s;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const n=("string"==typeof e?e:"").split(/[\s,]+/),r=n.length;for(s=0;s<r;s++)n[s]&&("-"===(e=n[s].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.slice(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let s,n;for(s=0,n=t.skips.length;s<n;s++)if(t.skips[s].test(e))return!1;for(s=0,n=t.names.length;s<n;s++)if(t.names[s].test(e))return!0;return!1},t.humanize=s(241),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((s=>{t[s]=e[s]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t),s|=0;return t.colors[Math.abs(s)%t.colors.length]},t.enable(t.load()),t}},241:e=>{var t=1e3,s=60*t,n=60*s,r=24*n;function o(e,t,s,n){var r=t>=1.5*s;return Math.round(e/s)+" "+n+(r?"s":"")}e.exports=function(e,i){i=i||{};var a,c,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var o=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(o){var i=parseFloat(o[1]);switch((o[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*i;case"weeks":case"week":case"w":return 6048e5*i;case"days":case"day":case"d":return i*r;case"hours":case"hour":case"hrs":case"hr":case"h":return i*n;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}(e);if("number"===u&&isFinite(e))return i.long?(a=e,(c=Math.abs(a))>=r?o(a,c,r,"day"):c>=n?o(a,c,n,"hour"):c>=s?o(a,c,s,"minute"):c>=t?o(a,c,t,"second"):a+" ms"):function(e){var o=Math.abs(e);return o>=r?Math.round(e/r)+"d":o>=n?Math.round(e/n)+"h":o>=s?Math.round(e/s)+"m":o>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},618:(e,t,s)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const s="color: "+this.color;t.splice(1,0,s,"color: inherit");let n=0,r=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(n++,"%c"===e&&(r=n))})),t.splice(r,0,s)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}return!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG),e},t.useColors=function(){return!("undefined"==typeof window||!window.process||"renderer"!==window.process.type&&!window.process.__nwjs)||("undefined"==typeof navigator||!navigator.userAgent||!navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&("undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=s(224)(t);const{formatters:n}=e.exports;n.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},224:(e,t,s)=>{e.exports=function(e){function t(e){let s,r,o,i=null;function a(...e){if(!a.enabled)return;const n=a,r=Number(new Date),o=r-(s||r);n.diff=o,n.prev=s,n.curr=r,s=r,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let i=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((s,r)=>{if("%%"===s)return"%";i++;const o=t.formatters[r];if("function"==typeof o){const t=e[i];s=o.call(n,t),e.splice(i,1),i--}return s})),t.formatArgs.call(n,e),(n.log||t.log).apply(n,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=n,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==i?i:(r!==t.namespaces&&(r=t.namespaces,o=t.enabled(e)),o),set:e=>{i=e}}),"function"==typeof t.init&&t.init(a),a}function n(e,s){const n=t(this.namespace+(void 0===s?":":s)+e);return n.log=this.log,n}function r(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){return e instanceof Error?e.stack||e.message:e},t.disable=function(){const e=[...t.names.map(r),...t.skips.map(r).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let s;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const n=("string"==typeof e?e:"").split(/[\s,]+/),r=n.length;for(s=0;s<r;s++)n[s]&&("-"===(e=n[s].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.slice(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let s,n;for(s=0,n=t.skips.length;s<n;s++)if(t.skips[s].test(e))return!1;for(s=0,n=t.names.length;s<n;s++)if(t.names[s].test(e))return!0;return!1},t.humanize=s(896),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((s=>{t[s]=e[s]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let s=0;for(let t=0;t<e.length;t++)s=(s<<5)-s+e.charCodeAt(t),s|=0;return t.colors[Math.abs(s)%t.colors.length]},t.enable(t.load()),t}},896:e=>{var t=1e3,s=60*t,n=60*s,r=24*n;function o(e,t,s,n){var r=t>=1.5*s;return Math.round(e/s)+" "+n+(r?"s":"")}e.exports=function(e,i){i=i||{};var a,c,u=typeof e;if("string"===u&&e.length>0)return function(e){if(!((e=String(e)).length>100)){var o=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(o){var i=parseFloat(o[1]);switch((o[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*i;case"weeks":case"week":case"w":return 6048e5*i;case"days":case"day":case"d":return i*r;case"hours":case"hour":case"hrs":case"hr":case"h":return i*n;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}(e);if("number"===u&&isFinite(e))return i.long?(a=e,(c=Math.abs(a))>=r?o(a,c,r,"day"):c>=n?o(a,c,n,"hour"):c>=s?o(a,c,s,"minute"):c>=t?o(a,c,t,"second"):a+" ms"):function(e){var o=Math.abs(e);return o>=r?Math.round(e/r)+"d":o>=n?Math.round(e/n)+"h":o>=s?Math.round(e/s)+"m":o>=t?Math.round(e/t)+"s":e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},628:function(e,t,s){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s);var r=Object.getOwnPropertyDescriptor(t,s);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[s]}}),Object.defineProperty(e,n,r)}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var s in e)"default"!==s&&Object.prototype.hasOwnProperty.call(e,s)&&n(t,e,s);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.authToken=void 0;const i=s(46),a=s(182),c=o(s(454));function u(e,t=document){return t.querySelector(e)}function h(e,t=document){return Array.from(t.querySelectorAll(e))}function l(){return localStorage.getItem("authToken")||""}function d(){const e=p.gradientName(),t=u("body");let s;t.classList.value=e,t.classList.add(p.getTimePeriod()),s=p.get24Hour()>=5&&p.get24Hour()<9?"morning":p.get24Hour()>=9&&p.get24Hour()<17?"afternoon":p.get24Hour()>=17&&p.get24Hour()<20?"evening":"night",u("#time-of-day").innerHTML=`<img src="/assets/img/icons/time-of-day/${s}.png"> ${p.getHour()}${p.getAmPm()}`}t.authToken=l;const p=new a.TimeManager,f=(0,i.io)({extraHeaders:{"x-authtoken":l()}});d(),setInterval(d,6e4),f.on("connect",(()=>{console.log(`Connected: ${f.id}`)})),f.on("authToken",(e=>{console.log(`recv auth token ${e}`),localStorage.getItem("authToken")!==e&&(localStorage.setItem("authToken",e),window.location.reload())})),f.on("status",(e=>u("#server-stats").innerHTML=e)),f.on("chat",(function(e){u("#chat-messages").insertAdjacentHTML("beforeend",e),u("#chat-messages").scrollTop=u("#chat-messages").scrollHeight})),f.on("ready",(function(){console.log("Server connection verified"),h("nav a")[3].click()})),h("nav a").forEach((e=>{e.addEventListener("click",(e=>{const t=e.target;h("a",t.closest("nav")).forEach((e=>{e.classList.remove("active")})),t.classList.add("active");const s=u(t.getAttribute("hx-target").toString());Array.from(s.parentElement.children).forEach((e=>{e.classList.remove("active")})),s.classList.add("active")}))})),u("body").addEventListener("click",(e=>{var t;const s=e.target;if(s.parentElement.classList.contains("filter")){Array.from(s.parentElement.children).forEach((e=>e.classList.remove("active"))),s.classList.add("active");const e=s.getAttribute("data-filter"),t=u(`.filter-result[data-filter="${e}"]`,s.closest(".filter-container"));t&&Array.from(t.parentElement.children).forEach((t=>{t.getAttribute("data-filter")===e?(t.classList.remove("hidden"),t.classList.add("active")):(t.classList.add("hidden"),t.classList.remove("active"))}))}if("dialog"===s.getAttribute("formmethod")&&"cancel"===s.getAttribute("value")){null===(t=s.closest("dialog"))||void 0===t||t.close();const e=s.getAttribute("nav-trigger");if(e){const[t,s]=e.split("|");h(t)[s].click()}}}));const g=new MutationObserver(((e,t)=>{const s={modal:!1,alert:!1};e.forEach((e=>{switch(e.target.id){case"modal-wrapper":s.modal=!0;break;case"alerts":s.alert=!0}})),s.modal&&u("#modal-wrapper").children.length&&h("#modal-wrapper dialog").forEach((e=>{e.open||e.showModal()})),s.alert&&u("#alerts").children.length&&h("#alerts .alert").forEach((e=>{if(!e.getAttribute("data-dismiss-at")){const t=Date.now()+c.ALERT_DISPLAY_LENGTH;e.setAttribute("data-dismiss-at",t.toString()),setTimeout((()=>{e.remove()}),c.ALERT_DISPLAY_LENGTH)}}))}));g.observe(u("#modal-wrapper"),{childList:!0}),g.observe(u("#alerts"),{childList:!0}),document.body.addEventListener("htmx:configRequest",(function(e){e.detail.headers["x-authtoken"]=l()})),document.body.addEventListener("htmx:load",(function(e){h(".disabled[data-block]").forEach((e=>{const t=parseInt(e.getAttribute("data-block")||"0");if(t>Date.now()){const s=t-Date.now();setTimeout((()=>{e.removeAttribute("disabled")}),s)}else e.removeAttribute("disabled")}))})),document.body.addEventListener("htmx:beforeSwap",(function(e){"chat-form"===e.target.id?u("#message").value="":"logout"===e.detail.serverResponse&&(localStorage.removeItem("authToken"),window.location.reload())}))},454:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.DUNGEON_TRAVEL_BLOCK=t.CHANCE_TO_FIGHT_SPECIAL=t.ALERT_DISPLAY_LENGTH=t.STEP_DELAY=t.FIGHT_ATTACK_DELAY=void 0,t.FIGHT_ATTACK_DELAY=1500,t.STEP_DELAY=2e3,t.ALERT_DISPLAY_LENGTH=3e3,t.CHANCE_TO_FIGHT_SPECIAL=10,t.DUNGEON_TRAVEL_BLOCK=3e3},182:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TimeManager=void 0,t.TimeManager=class{constructor(e=120){this.dayLength=e,this.scaleFactor=30,this.dayLengthAsMS=60*e*1e3}dayScaleFactor(){return this.dayLength/30}getTimePeriod(){return this.isMorning()?"morning":this.isAfternoon()?"afternoon":this.isEvening()?"evening":this.isNight()?"night":void 0}getAmPm(){return this.get24Hour()<12?"am":"pm"}get24Hour(){const e=new Date,t=(e.getMinutes()+e.getHours()%2*(this.dayLength/2))/this.dayLength;return Math.floor(24*t)}getHour(){const e=this.get24Hour(),t=e>12?e-12:e;return 0===t?12:t}gradientName(){const e=Math.floor(this.get24Hour()/24*this.scaleFactor);return`sky-gradient-${e<10?"0":""}${e}`}isNight(){const e=this.get24Hour();return e>=0&&e<5||e>=21&&e<24}isMorning(){const e=this.get24Hour();return e>=5&&e<12}isAfternoon(){const e=this.get24Hour();return e>=12&&e<18}isEvening(){const e=this.get24Hour();return e>=18&&e<21}}},419:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.hasCORS=void 0;let s=!1;try{s="undefined"!=typeof XMLHttpRequest&&"withCredentials"in new XMLHttpRequest}catch(e){}t.hasCORS=s},754:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decode=t.encode=void 0,t.encode=function(e){let t="";for(let s in e)e.hasOwnProperty(s)&&(t.length&&(t+="&"),t+=encodeURIComponent(s)+"="+encodeURIComponent(e[s]));return t},t.decode=function(e){let t={},s=e.split("&");for(let e=0,n=s.length;e<n;e++){let n=s[e].split("=");t[decodeURIComponent(n[0])]=decodeURIComponent(n[1])}return t}},222:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parse=void 0;const s=/^(?:(?![^:@\/?#]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@\/?#]*)(?::([^:@\/?#]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,n=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.parse=function(e){const t=e,r=e.indexOf("["),o=e.indexOf("]");-1!=r&&-1!=o&&(e=e.substring(0,r)+e.substring(r,o).replace(/:/g,";")+e.substring(o,e.length));let i=s.exec(e||""),a={},c=14;for(;c--;)a[n[c]]=i[c]||"";return-1!=r&&-1!=o&&(a.source=t,a.host=a.host.substring(1,a.host.length-1).replace(/;/g,":"),a.authority=a.authority.replace("[","").replace("]","").replace(/;/g,":"),a.ipv6uri=!0),a.pathNames=function(e,t){const s=t.replace(/\/{2,9}/g,"/").split("/");return"/"!=t.slice(0,1)&&0!==t.length||s.splice(0,1),"/"==t.slice(-1)&&s.splice(s.length-1,1),s}(0,a.path),a.queryKey=function(e,t){const s={};return t.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(e,t,n){t&&(s[t]=n)})),s}(0,a.query),a}},726:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.yeast=t.decode=t.encode=void 0;const s="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),n={};let r,o=0,i=0;function a(e){let t="";do{t=s[e%64]+t,e=Math.floor(e/64)}while(e>0);return t}for(t.encode=a,t.decode=function(e){let t=0;for(i=0;i<e.length;i++)t=64*t+n[e.charAt(i)];return t},t.yeast=function(){const e=a(+new Date);return e!==r?(o=0,r=e):e+"."+a(o++)};i<64;i++)n[s[i]]=i},242:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.globalThisShim=void 0,t.globalThisShim="undefined"!=typeof self?self:"undefined"!=typeof window?window:Function("return this")()},679:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.nextTick=t.parse=t.installTimerFunctions=t.transports=t.Transport=t.protocol=t.Socket=void 0;const n=s(481);Object.defineProperty(t,"Socket",{enumerable:!0,get:function(){return n.Socket}}),t.protocol=n.Socket.protocol;var r=s(870);Object.defineProperty(t,"Transport",{enumerable:!0,get:function(){return r.Transport}});var o=s(385);Object.defineProperty(t,"transports",{enumerable:!0,get:function(){return o.transports}});var i=s(622);Object.defineProperty(t,"installTimerFunctions",{enumerable:!0,get:function(){return i.installTimerFunctions}});var a=s(222);Object.defineProperty(t,"parse",{enumerable:!0,get:function(){return a.parse}});var c=s(552);Object.defineProperty(t,"nextTick",{enumerable:!0,get:function(){return c.nextTick}})},481:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Socket=void 0;const r=s(385),o=s(622),i=s(754),a=s(222),c=n(s(802)),u=s(260),h=s(373),l=(0,c.default)("engine.io-client:socket");class d extends u.Emitter{constructor(e,t={}){super(),this.writeBuffer=[],e&&"object"==typeof e&&(t=e,e=null),e?(e=(0,a.parse)(e),t.hostname=e.host,t.secure="https"===e.protocol||"wss"===e.protocol,t.port=e.port,e.query&&(t.query=e.query)):t.host&&(t.hostname=(0,a.parse)(t.host).host),(0,o.installTimerFunctions)(this,t),this.secure=null!=t.secure?t.secure:"undefined"!=typeof location&&"https:"===location.protocol,t.hostname&&!t.port&&(t.port=this.secure?"443":"80"),this.hostname=t.hostname||("undefined"!=typeof location?location.hostname:"localhost"),this.port=t.port||("undefined"!=typeof location&&location.port?location.port:this.secure?"443":"80"),this.transports=t.transports||["polling","websocket"],this.writeBuffer=[],this.prevBufferLen=0,this.opts=Object.assign({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,addTrailingSlash:!0,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!0},t),this.opts.path=this.opts.path.replace(/\/$/,"")+(this.opts.addTrailingSlash?"/":""),"string"==typeof this.opts.query&&(this.opts.query=(0,i.decode)(this.opts.query)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingTimeoutTimer=null,"function"==typeof addEventListener&&(this.opts.closeOnBeforeunload&&(this.beforeunloadEventListener=()=>{this.transport&&(this.transport.removeAllListeners(),this.transport.close())},addEventListener("beforeunload",this.beforeunloadEventListener,!1)),"localhost"!==this.hostname&&(this.offlineEventListener=()=>{this.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",this.offlineEventListener,!1))),this.open()}createTransport(e){l('creating transport "%s"',e);const t=Object.assign({},this.opts.query);t.EIO=h.protocol,t.transport=e,this.id&&(t.sid=this.id);const s=Object.assign({},this.opts.transportOptions[e],this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return l("options: %j",s),new r.transports[e](s)}open(){let e;if(this.opts.rememberUpgrade&&d.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))e="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((()=>{this.emitReserved("error","No transports available")}),0);e=this.transports[0]}this.readyState="opening";try{e=this.createTransport(e)}catch(e){return l("error while creating transport: %s",e),this.transports.shift(),void this.open()}e.open(),this.setTransport(e)}setTransport(e){l("setting transport %s",e.name),this.transport&&(l("clearing existing transport %s",this.transport.name),this.transport.removeAllListeners()),this.transport=e,e.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(e=>this.onClose("transport close",e)))}probe(e){l('probing transport "%s"',e);let t=this.createTransport(e),s=!1;d.priorWebsocketSuccess=!1;const n=()=>{s||(l('probe transport "%s" opened',e),t.send([{type:"ping",data:"probe"}]),t.once("packet",(n=>{if(!s)if("pong"===n.type&&"probe"===n.data){if(l('probe transport "%s" pong',e),this.upgrading=!0,this.emitReserved("upgrading",t),!t)return;d.priorWebsocketSuccess="websocket"===t.name,l('pausing current transport "%s"',this.transport.name),this.transport.pause((()=>{s||"closed"!==this.readyState&&(l("changing transport and sending upgrade packet"),u(),this.setTransport(t),t.send([{type:"upgrade"}]),this.emitReserved("upgrade",t),t=null,this.upgrading=!1,this.flush())}))}else{l('probe transport "%s" failed',e);const s=new Error("probe error");s.transport=t.name,this.emitReserved("upgradeError",s)}})))};function r(){s||(s=!0,u(),t.close(),t=null)}const o=s=>{const n=new Error("probe error: "+s);n.transport=t.name,r(),l('probe transport "%s" failed because of error: %s',e,s),this.emitReserved("upgradeError",n)};function i(){o("transport closed")}function a(){o("socket closed")}function c(e){t&&e.name!==t.name&&(l('"%s" works - aborting "%s"',e.name,t.name),r())}const u=()=>{t.removeListener("open",n),t.removeListener("error",o),t.removeListener("close",i),this.off("close",a),this.off("upgrading",c)};t.once("open",n),t.once("error",o),t.once("close",i),this.once("close",a),this.once("upgrading",c),t.open()}onOpen(){if(l("socket open"),this.readyState="open",d.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade){l("starting upgrade probes");let e=0;const t=this.upgrades.length;for(;e<t;e++)this.probe(this.upgrades[e])}}onPacket(e){if("opening"===this.readyState||"open"===this.readyState||"closing"===this.readyState)switch(l('socket receive: type "%s", data "%s"',e.type,e.data),this.emitReserved("packet",e),this.emitReserved("heartbeat"),e.type){case"open":this.onHandshake(JSON.parse(e.data));break;case"ping":this.resetPingTimeout(),this.sendPacket("pong"),this.emitReserved("ping"),this.emitReserved("pong");break;case"error":const t=new Error("server error");t.code=e.data,this.onError(t);break;case"message":this.emitReserved("data",e.data),this.emitReserved("message",e.data)}else l('packet received with socket readyState "%s"',this.readyState)}onHandshake(e){this.emitReserved("handshake",e),this.id=e.sid,this.transport.query.sid=e.sid,this.upgrades=this.filterUpgrades(e.upgrades),this.pingInterval=e.pingInterval,this.pingTimeout=e.pingTimeout,this.maxPayload=e.maxPayload,this.onOpen(),"closed"!==this.readyState&&this.resetPingTimeout()}resetPingTimeout(){this.clearTimeoutFn(this.pingTimeoutTimer),this.pingTimeoutTimer=this.setTimeoutFn((()=>{this.onClose("ping timeout")}),this.pingInterval+this.pingTimeout),this.opts.autoUnref&&this.pingTimeoutTimer.unref()}onDrain(){this.writeBuffer.splice(0,this.prevBufferLen),this.prevBufferLen=0,0===this.writeBuffer.length?this.emitReserved("drain"):this.flush()}flush(){if("closed"!==this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){const e=this.getWritablePackets();l("flushing %d packets in socket",e.length),this.transport.send(e),this.prevBufferLen=e.length,this.emitReserved("flush")}}getWritablePackets(){if(!(this.maxPayload&&"polling"===this.transport.name&&this.writeBuffer.length>1))return this.writeBuffer;let e=1;for(let t=0;t<this.writeBuffer.length;t++){const s=this.writeBuffer[t].data;if(s&&(e+=(0,o.byteLength)(s)),t>0&&e>this.maxPayload)return l("only send %d out of %d packets",t,this.writeBuffer.length),this.writeBuffer.slice(0,t);e+=2}return l("payload size is %d (max: %d)",e,this.maxPayload),this.writeBuffer}write(e,t,s){return this.sendPacket("message",e,t,s),this}send(e,t,s){return this.sendPacket("message",e,t,s),this}sendPacket(e,t,s,n){if("function"==typeof t&&(n=t,t=void 0),"function"==typeof s&&(n=s,s=null),"closing"===this.readyState||"closed"===this.readyState)return;(s=s||{}).compress=!1!==s.compress;const r={type:e,data:t,options:s};this.emitReserved("packetCreate",r),this.writeBuffer.push(r),n&&this.once("flush",n),this.flush()}close(){const e=()=>{this.onClose("forced close"),l("socket closing - telling transport to close"),this.transport.close()},t=()=>{this.off("upgrade",t),this.off("upgradeError",t),e()},s=()=>{this.once("upgrade",t),this.once("upgradeError",t)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(()=>{this.upgrading?s():e()})):this.upgrading?s():e()),this}onError(e){l("socket error %j",e),d.priorWebsocketSuccess=!1,this.emitReserved("error",e),this.onClose("transport error",e)}onClose(e,t){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(l('socket close with reason: "%s"',e),this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&(removeEventListener("beforeunload",this.beforeunloadEventListener,!1),removeEventListener("offline",this.offlineEventListener,!1)),this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this.prevBufferLen=0)}filterUpgrades(e){const t=[];let s=0;const n=e.length;for(;s<n;s++)~this.transports.indexOf(e[s])&&t.push(e[s]);return t}}t.Socket=d,d.protocol=h.protocol},870:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Transport=void 0;const r=s(373),o=s(260),i=s(622),a=(0,n(s(802)).default)("engine.io-client:transport");class c extends Error{constructor(e,t,s){super(e),this.description=t,this.context=s,this.type="TransportError"}}class u extends o.Emitter{constructor(e){super(),this.writable=!1,(0,i.installTimerFunctions)(this,e),this.opts=e,this.query=e.query,this.socket=e.socket}onError(e,t,s){return super.emitReserved("error",new c(e,t,s)),this}open(){return this.readyState="opening",this.doOpen(),this}close(){return"opening"!==this.readyState&&"open"!==this.readyState||(this.doClose(),this.onClose()),this}send(e){"open"===this.readyState?this.write(e):a("transport is not open, discarding packets")}onOpen(){this.readyState="open",this.writable=!0,super.emitReserved("open")}onData(e){const t=(0,r.decodePacket)(e,this.socket.binaryType);this.onPacket(t)}onPacket(e){super.emitReserved("packet",e)}onClose(e){this.readyState="closed",super.emitReserved("close",e)}pause(e){}}t.Transport=u},385:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transports=void 0;const n=s(484),r=s(308);t.transports={websocket:r.WS,polling:n.Polling}},484:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Request=t.Polling=void 0;const r=s(870),o=n(s(802)),i=s(726),a=s(754),c=s(373),u=s(666),h=s(260),l=s(622),d=s(242),p=(0,o.default)("engine.io-client:polling");function f(){}const g=null!=new u.XHR({xdomain:!1}).responseType;class m extends r.Transport{constructor(e){if(super(e),this.polling=!1,"undefined"!=typeof location){const t="https:"===location.protocol;let s=location.port;s||(s=t?"443":"80"),this.xd="undefined"!=typeof location&&e.hostname!==location.hostname||s!==e.port,this.xs=e.secure!==t}const t=e&&e.forceBase64;this.supportsBinary=g&&!t}get name(){return"polling"}doOpen(){this.poll()}pause(e){this.readyState="pausing";const t=()=>{p("paused"),this.readyState="paused",e()};if(this.polling||!this.writable){let e=0;this.polling&&(p("we are currently polling - waiting to pause"),e++,this.once("pollComplete",(function(){p("pre-pause polling complete"),--e||t()}))),this.writable||(p("we are currently writing - waiting to pause"),e++,this.once("drain",(function(){p("pre-pause writing complete"),--e||t()})))}else t()}poll(){p("polling"),this.polling=!0,this.doPoll(),this.emitReserved("poll")}onData(e){p("polling got data %s",e),(0,c.decodePayload)(e,this.socket.binaryType).forEach((e=>{if("opening"===this.readyState&&"open"===e.type&&this.onOpen(),"close"===e.type)return this.onClose({description:"transport closed by the server"}),!1;this.onPacket(e)})),"closed"!==this.readyState&&(this.polling=!1,this.emitReserved("pollComplete"),"open"===this.readyState?this.poll():p('ignoring poll - transport state "%s"',this.readyState))}doClose(){const e=()=>{p("writing close packet"),this.write([{type:"close"}])};"open"===this.readyState?(p("transport open - closing"),e()):(p("transport not open - deferring close"),this.once("open",e))}write(e){this.writable=!1,(0,c.encodePayload)(e,(e=>{this.doWrite(e,(()=>{this.writable=!0,this.emitReserved("drain")}))}))}uri(){let e=this.query||{};const t=this.opts.secure?"https":"http";let s="";!1!==this.opts.timestampRequests&&(e[this.opts.timestampParam]=(0,i.yeast)()),this.supportsBinary||e.sid||(e.b64=1),this.opts.port&&("https"===t&&443!==Number(this.opts.port)||"http"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port);const n=(0,a.encode)(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(n.length?"?"+n:"")}request(e={}){return Object.assign(e,{xd:this.xd,xs:this.xs},this.opts),new y(this.uri(),e)}doWrite(e,t){const s=this.request({method:"POST",data:e});s.on("success",t),s.on("error",((e,t)=>{this.onError("xhr post error",e,t)}))}doPoll(){p("xhr poll");const e=this.request();e.on("data",this.onData.bind(this)),e.on("error",((e,t)=>{this.onError("xhr poll error",e,t)})),this.pollXhr=e}}t.Polling=m;class y extends h.Emitter{constructor(e,t){super(),(0,l.installTimerFunctions)(this,t),this.opts=t,this.method=t.method||"GET",this.uri=e,this.async=!1!==t.async,this.data=void 0!==t.data?t.data:null,this.create()}create(){const e=(0,l.pick)(this.opts,"agent","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");e.xdomain=!!this.opts.xd,e.xscheme=!!this.opts.xs;const t=this.xhr=new u.XHR(e);try{p("xhr open %s: %s",this.method,this.uri),t.open(this.method,this.uri,this.async);try{if(this.opts.extraHeaders){t.setDisableHeaderCheck&&t.setDisableHeaderCheck(!0);for(let e in this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(e)&&t.setRequestHeader(e,this.opts.extraHeaders[e])}}catch(e){}if("POST"===this.method)try{t.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(e){}try{t.setRequestHeader("Accept","*/*")}catch(e){}"withCredentials"in t&&(t.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(t.timeout=this.opts.requestTimeout),t.onreadystatechange=()=>{4===t.readyState&&(200===t.status||1223===t.status?this.onLoad():this.setTimeoutFn((()=>{this.onError("number"==typeof t.status?t.status:0)}),0))},p("xhr data %s",this.data),t.send(this.data)}catch(e){return void this.setTimeoutFn((()=>{this.onError(e)}),0)}"undefined"!=typeof document&&(this.index=y.requestsCount++,y.requests[this.index]=this)}onError(e){this.emitReserved("error",e,this.xhr),this.cleanup(!0)}cleanup(e){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=f,e)try{this.xhr.abort()}catch(e){}"undefined"!=typeof document&&delete y.requests[this.index],this.xhr=null}}onLoad(){const e=this.xhr.responseText;null!==e&&(this.emitReserved("data",e),this.emitReserved("success"),this.cleanup())}abort(){this.cleanup()}}if(t.Request=y,y.requestsCount=0,y.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",C);else if("function"==typeof addEventListener){const e="onpagehide"in d.globalThisShim?"pagehide":"unload";addEventListener(e,C,!1)}function C(){for(let e in y.requests)y.requests.hasOwnProperty(e)&&y.requests[e].abort()}},552:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultBinaryType=t.usingBrowserWebSocket=t.WebSocket=t.nextTick=void 0;const n=s(242);t.nextTick="function"==typeof Promise&&"function"==typeof Promise.resolve?e=>Promise.resolve().then(e):(e,t)=>t(e,0),t.WebSocket=n.globalThisShim.WebSocket||n.globalThisShim.MozWebSocket,t.usingBrowserWebSocket=!0,t.defaultBinaryType="arraybuffer"},308:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.WS=void 0;const r=s(870),o=s(754),i=s(726),a=s(622),c=s(552),u=n(s(802)),h=s(373),l=(0,u.default)("engine.io-client:websocket"),d="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase();class p extends r.Transport{constructor(e){super(e),this.supportsBinary=!e.forceBase64}get name(){return"websocket"}doOpen(){if(!this.check())return;const e=this.uri(),t=this.opts.protocols,s=d?{}:(0,a.pick)(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(s.headers=this.opts.extraHeaders);try{this.ws=c.usingBrowserWebSocket&&!d?t?new c.WebSocket(e,t):new c.WebSocket(e):new c.WebSocket(e,t,s)}catch(e){return this.emitReserved("error",e)}this.ws.binaryType=this.socket.binaryType||c.defaultBinaryType,this.addEventListeners()}addEventListeners(){this.ws.onopen=()=>{this.opts.autoUnref&&this.ws._socket.unref(),this.onOpen()},this.ws.onclose=e=>this.onClose({description:"websocket connection closed",context:e}),this.ws.onmessage=e=>this.onData(e.data),this.ws.onerror=e=>this.onError("websocket error",e)}write(e){this.writable=!1;for(let t=0;t<e.length;t++){const s=e[t],n=t===e.length-1;(0,h.encodePacket)(s,this.supportsBinary,(e=>{const t={};!c.usingBrowserWebSocket&&(s.options&&(t.compress=s.options.compress),this.opts.perMessageDeflate)&&("string"==typeof e?Buffer.byteLength(e):e.length)<this.opts.perMessageDeflate.threshold&&(t.compress=!1);try{c.usingBrowserWebSocket?this.ws.send(e):this.ws.send(e,t)}catch(e){l("websocket closed before onclose event")}n&&(0,c.nextTick)((()=>{this.writable=!0,this.emitReserved("drain")}),this.setTimeoutFn)}))}}doClose(){void 0!==this.ws&&(this.ws.close(),this.ws=null)}uri(){let e=this.query||{};const t=this.opts.secure?"wss":"ws";let s="";this.opts.port&&("wss"===t&&443!==Number(this.opts.port)||"ws"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port),this.opts.timestampRequests&&(e[this.opts.timestampParam]=(0,i.yeast)()),this.supportsBinary||(e.b64=1);const n=(0,o.encode)(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(n.length?"?"+n:"")}check(){return!!c.WebSocket}}t.WS=p},666:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.XHR=void 0;const n=s(419),r=s(242);t.XHR=function(e){const t=e.xdomain;try{if("undefined"!=typeof XMLHttpRequest&&(!t||n.hasCORS))return new XMLHttpRequest}catch(e){}if(!t)try{return new(r.globalThisShim[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(e){}}},622:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.byteLength=t.installTimerFunctions=t.pick=void 0;const n=s(242);t.pick=function(e,...t){return t.reduce(((t,s)=>(e.hasOwnProperty(s)&&(t[s]=e[s]),t)),{})};const r=n.globalThisShim.setTimeout,o=n.globalThisShim.clearTimeout;t.installTimerFunctions=function(e,t){t.useNativeTimers?(e.setTimeoutFn=r.bind(n.globalThisShim),e.clearTimeoutFn=o.bind(n.globalThisShim)):(e.setTimeoutFn=n.globalThisShim.setTimeout.bind(n.globalThisShim),e.clearTimeoutFn=n.globalThisShim.clearTimeout.bind(n.globalThisShim))},t.byteLength=function(e){return"string"==typeof e?function(e){let t=0,s=0;for(let n=0,r=e.length;n<r;n++)t=e.charCodeAt(n),t<128?s+=1:t<2048?s+=2:t<55296||t>=57344?s+=3:(n++,s+=4);return s}(e):Math.ceil(1.33*(e.byteLength||e.size))}},87:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ERROR_PACKET=t.PACKET_TYPES_REVERSE=t.PACKET_TYPES=void 0;const s=Object.create(null);t.PACKET_TYPES=s,s.open="0",s.close="1",s.ping="2",s.pong="3",s.message="4",s.upgrade="5",s.noop="6";const n=Object.create(null);t.PACKET_TYPES_REVERSE=n,Object.keys(s).forEach((e=>{n[s[e]]=e})),t.ERROR_PACKET={type:"error",data:"parser error"}},469:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decode=t.encode=void 0;const s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",n="undefined"==typeof Uint8Array?[]:new Uint8Array(256);for(let e=0;e<64;e++)n[s.charCodeAt(e)]=e;t.encode=e=>{let t,n=new Uint8Array(e),r=n.length,o="";for(t=0;t<r;t+=3)o+=s[n[t]>>2],o+=s[(3&n[t])<<4|n[t+1]>>4],o+=s[(15&n[t+1])<<2|n[t+2]>>6],o+=s[63&n[t+2]];return r%3==2?o=o.substring(0,o.length-1)+"=":r%3==1&&(o=o.substring(0,o.length-2)+"=="),o},t.decode=e=>{let t,s,r,o,i,a=.75*e.length,c=e.length,u=0;"="===e[e.length-1]&&(a--,"="===e[e.length-2]&&a--);const h=new ArrayBuffer(a),l=new Uint8Array(h);for(t=0;t<c;t+=4)s=n[e.charCodeAt(t)],r=n[e.charCodeAt(t+1)],o=n[e.charCodeAt(t+2)],i=n[e.charCodeAt(t+3)],l[u++]=s<<2|r>>4,l[u++]=(15&r)<<4|o>>2,l[u++]=(3&o)<<6|63&i;return h}},572:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=s(87),r=s(469),o="function"==typeof ArrayBuffer,i=(e,t)=>{if(o){const s=(0,r.decode)(e);return a(s,t)}return{base64:!0,data:e}},a=(e,t)=>"blob"===t&&e instanceof ArrayBuffer?new Blob([e]):e;t.default=(e,t)=>{if("string"!=typeof e)return{type:"message",data:a(e,t)};const s=e.charAt(0);return"b"===s?{type:"message",data:i(e.substring(1),t)}:n.PACKET_TYPES_REVERSE[s]?e.length>1?{type:n.PACKET_TYPES_REVERSE[s],data:e.substring(1)}:{type:n.PACKET_TYPES_REVERSE[s]}:n.ERROR_PACKET}},908:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});const n=s(87),r="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),o="function"==typeof ArrayBuffer,i=(e,t)=>{const s=new FileReader;return s.onload=function(){const e=s.result.split(",")[1];t("b"+(e||""))},s.readAsDataURL(e)};t.default=({type:e,data:t},s,a)=>{return r&&t instanceof Blob?s?a(t):i(t,a):o&&(t instanceof ArrayBuffer||(c=t,"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(c):c&&c.buffer instanceof ArrayBuffer))?s?a(t):i(new Blob([t]),a):a(n.PACKET_TYPES[e]+(t||""));var c}},373:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decodePayload=t.decodePacket=t.encodePayload=t.encodePacket=t.protocol=void 0;const n=s(908);t.encodePacket=n.default;const r=s(572);t.decodePacket=r.default;const o=String.fromCharCode(30);t.encodePayload=(e,t)=>{const s=e.length,r=new Array(s);let i=0;e.forEach(((e,a)=>{(0,n.default)(e,!1,(e=>{r[a]=e,++i===s&&t(r.join(o))}))}))},t.decodePayload=(e,t)=>{const s=e.split(o),n=[];for(let e=0;e<s.length;e++){const o=(0,r.default)(s[e],t);if(n.push(o),"error"===o.type)break}return n},t.protocol=4},159:(e,t)=>{"use strict";function s(e){e=e||{},this.ms=e.min||100,this.max=e.max||1e4,this.factor=e.factor||2,this.jitter=e.jitter>0&&e.jitter<=1?e.jitter:0,this.attempts=0}Object.defineProperty(t,"__esModule",{value:!0}),t.Backoff=void 0,t.Backoff=s,s.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),s=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-s:e+s}return 0|Math.min(e,this.max)},s.prototype.reset=function(){this.attempts=0},s.prototype.setMin=function(e){this.ms=e},s.prototype.setMax=function(e){this.max=e},s.prototype.setJitter=function(e){this.jitter=e}},46:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.connect=t.io=t.Socket=t.Manager=t.protocol=void 0;const r=s(84),o=s(168);Object.defineProperty(t,"Manager",{enumerable:!0,get:function(){return o.Manager}});const i=s(312);Object.defineProperty(t,"Socket",{enumerable:!0,get:function(){return i.Socket}});const a=n(s(669)).default("socket.io-client"),c={};function u(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};const s=r.url(e,t.path||"/socket.io"),n=s.source,i=s.id,u=s.path,h=c[i]&&u in c[i].nsps;let l;return t.forceNew||t["force new connection"]||!1===t.multiplex||h?(a("ignoring socket cache for %s",n),l=new o.Manager(n,t)):(c[i]||(a("new io instance for %s",n),c[i]=new o.Manager(n,t)),l=c[i]),s.query&&!t.query&&(t.query=s.queryKey),l.socket(s.path,t)}t.io=u,t.connect=u,t.default=u,Object.assign(u,{Manager:o.Manager,Socket:i.Socket,io:u,connect:u});var h=s(514);Object.defineProperty(t,"protocol",{enumerable:!0,get:function(){return h.protocol}}),e.exports=u},168:function(e,t,s){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[s]}})}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var s in e)"default"!==s&&Object.prototype.hasOwnProperty.call(e,s)&&n(t,e,s);return r(t,e),t},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Manager=void 0;const a=s(679),c=s(312),u=o(s(514)),h=s(149),l=s(159),d=s(260),p=i(s(669)).default("socket.io-client:manager");class f extends d.Emitter{constructor(e,t){var s;super(),this.nsps={},this.subs=[],e&&"object"==typeof e&&(t=e,e=void 0),(t=t||{}).path=t.path||"/socket.io",this.opts=t,a.installTimerFunctions(this,t),this.reconnection(!1!==t.reconnection),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor(null!==(s=t.randomizationFactor)&&void 0!==s?s:.5),this.backoff=new l.Backoff({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(null==t.timeout?2e4:t.timeout),this._readyState="closed",this.uri=e;const n=t.parser||u;this.encoder=new n.Encoder,this.decoder=new n.Decoder,this._autoConnect=!1!==t.autoConnect,this._autoConnect&&this.open()}reconnection(e){return arguments.length?(this._reconnection=!!e,this):this._reconnection}reconnectionAttempts(e){return void 0===e?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}reconnectionDelay(e){var t;return void 0===e?this._reconnectionDelay:(this._reconnectionDelay=e,null===(t=this.backoff)||void 0===t||t.setMin(e),this)}randomizationFactor(e){var t;return void 0===e?this._randomizationFactor:(this._randomizationFactor=e,null===(t=this.backoff)||void 0===t||t.setJitter(e),this)}reconnectionDelayMax(e){var t;return void 0===e?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,null===(t=this.backoff)||void 0===t||t.setMax(e),this)}timeout(e){return arguments.length?(this._timeout=e,this):this._timeout}maybeReconnectOnOpen(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}open(e){if(p("readyState %s",this._readyState),~this._readyState.indexOf("open"))return this;p("opening %s",this.uri),this.engine=new a.Socket(this.uri,this.opts);const t=this.engine,s=this;this._readyState="opening",this.skipReconnect=!1;const n=h.on(t,"open",(function(){s.onopen(),e&&e()})),r=h.on(t,"error",(t=>{p("error"),s.cleanup(),s._readyState="closed",this.emitReserved("error",t),e?e(t):s.maybeReconnectOnOpen()}));if(!1!==this._timeout){const e=this._timeout;p("connect attempt will timeout after %d",e),0===e&&n();const s=this.setTimeoutFn((()=>{p("connect attempt timed out after %d",e),n(),t.close(),t.emit("error",new Error("timeout"))}),e);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}return this.subs.push(n),this.subs.push(r),this}connect(e){return this.open(e)}onopen(){p("open"),this.cleanup(),this._readyState="open",this.emitReserved("open");const e=this.engine;this.subs.push(h.on(e,"ping",this.onping.bind(this)),h.on(e,"data",this.ondata.bind(this)),h.on(e,"error",this.onerror.bind(this)),h.on(e,"close",this.onclose.bind(this)),h.on(this.decoder,"decoded",this.ondecoded.bind(this)))}onping(){this.emitReserved("ping")}ondata(e){try{this.decoder.add(e)}catch(e){this.onclose("parse error",e)}}ondecoded(e){a.nextTick((()=>{this.emitReserved("packet",e)}),this.setTimeoutFn)}onerror(e){p("error",e),this.emitReserved("error",e)}socket(e,t){let s=this.nsps[e];return s?this._autoConnect&&!s.active&&s.connect():(s=new c.Socket(this,e,t),this.nsps[e]=s),s}_destroy(e){const t=Object.keys(this.nsps);for(const e of t)if(this.nsps[e].active)return void p("socket %s is still active, skipping close",e);this._close()}_packet(e){p("writing packet %j",e);const t=this.encoder.encode(e);for(let s=0;s<t.length;s++)this.engine.write(t[s],e.options)}cleanup(){p("cleanup"),this.subs.forEach((e=>e())),this.subs.length=0,this.decoder.destroy()}_close(){p("disconnect"),this.skipReconnect=!0,this._reconnecting=!1,this.onclose("forced close"),this.engine&&this.engine.close()}disconnect(){return this._close()}onclose(e,t){p("closed due to %s",e),this.cleanup(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect()}reconnect(){if(this._reconnecting||this.skipReconnect)return this;const e=this;if(this.backoff.attempts>=this._reconnectionAttempts)p("reconnect failed"),this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{const t=this.backoff.duration();p("will wait %dms before reconnect attempt",t),this._reconnecting=!0;const s=this.setTimeoutFn((()=>{e.skipReconnect||(p("attempting reconnect"),this.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((t=>{t?(p("reconnect attempt error"),e._reconnecting=!1,e.reconnect(),this.emitReserved("reconnect_error",t)):(p("reconnect success"),e.onreconnect())})))}),t);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}}onreconnect(){const e=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",e)}}t.Manager=f},149:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.on=void 0,t.on=function(e,t,s){return e.on(t,s),function(){e.off(t,s)}}},312:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.Socket=void 0;const r=s(514),o=s(149),i=s(260),a=n(s(669)).default("socket.io-client:socket"),c=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1});class u extends i.Emitter{constructor(e,t,s){super(),this.connected=!1,this.recovered=!1,this.receiveBuffer=[],this.sendBuffer=[],this._queue=[],this._queueSeq=0,this.ids=0,this.acks={},this.flags={},this.io=e,this.nsp=t,s&&s.auth&&(this.auth=s.auth),this._opts=Object.assign({},s),this.io._autoConnect&&this.open()}get disconnected(){return!this.connected}subEvents(){if(this.subs)return;const e=this.io;this.subs=[o.on(e,"open",this.onopen.bind(this)),o.on(e,"packet",this.onpacket.bind(this)),o.on(e,"error",this.onerror.bind(this)),o.on(e,"close",this.onclose.bind(this))]}get active(){return!!this.subs}connect(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}open(){return this.connect()}send(...e){return e.unshift("message"),this.emit.apply(this,e),this}emit(e,...t){if(c.hasOwnProperty(e))throw new Error('"'+e.toString()+'" is a reserved event name');if(t.unshift(e),this._opts.retries&&!this.flags.fromQueue&&!this.flags.volatile)return this._addToQueue(t),this;const s={type:r.PacketType.EVENT,data:t,options:{}};if(s.options.compress=!1!==this.flags.compress,"function"==typeof t[t.length-1]){const e=this.ids++;a("emitting packet with ack id %d",e);const n=t.pop();this._registerAckCallback(e,n),s.id=e}const n=this.io.engine&&this.io.engine.transport&&this.io.engine.transport.writable;return!this.flags.volatile||n&&this.connected?this.connected?(this.notifyOutgoingListeners(s),this.packet(s)):this.sendBuffer.push(s):a("discard packet as the transport is not currently writable"),this.flags={},this}_registerAckCallback(e,t){var s;const n=null!==(s=this.flags.timeout)&&void 0!==s?s:this._opts.ackTimeout;if(void 0===n)return void(this.acks[e]=t);const r=this.io.setTimeoutFn((()=>{delete this.acks[e];for(let t=0;t<this.sendBuffer.length;t++)this.sendBuffer[t].id===e&&(a("removing packet with ack id %d from the buffer",e),this.sendBuffer.splice(t,1));a("event with ack id %d has timed out after %d ms",e,n),t.call(this,new Error("operation has timed out"))}),n);this.acks[e]=(...e)=>{this.io.clearTimeoutFn(r),t.apply(this,[null,...e])}}emitWithAck(e,...t){const s=void 0!==this.flags.timeout||void 0!==this._opts.ackTimeout;return new Promise(((n,r)=>{t.push(((e,t)=>s?e?r(e):n(t):n(e))),this.emit(e,...t)}))}_addToQueue(e){let t;"function"==typeof e[e.length-1]&&(t=e.pop());const s={id:this._queueSeq++,tryCount:0,pending:!1,args:e,flags:Object.assign({fromQueue:!0},this.flags)};e.push(((e,...n)=>{if(s===this._queue[0])return null!==e?s.tryCount>this._opts.retries&&(a("packet [%d] is discarded after %d tries",s.id,s.tryCount),this._queue.shift(),t&&t(e)):(a("packet [%d] was successfully sent",s.id),this._queue.shift(),t&&t(null,...n)),s.pending=!1,this._drainQueue()})),this._queue.push(s),this._drainQueue()}_drainQueue(e=!1){if(a("draining queue"),!this.connected||0===this._queue.length)return;const t=this._queue[0];!t.pending||e?(t.pending=!0,t.tryCount++,a("sending packet [%d] (try n°%d)",t.id,t.tryCount),this.flags=t.flags,this.emit.apply(this,t.args)):a("packet [%d] has already been sent and is waiting for an ack",t.id)}packet(e){e.nsp=this.nsp,this.io._packet(e)}onopen(){a("transport is open - connecting"),"function"==typeof this.auth?this.auth((e=>{this._sendConnectPacket(e)})):this._sendConnectPacket(this.auth)}_sendConnectPacket(e){this.packet({type:r.PacketType.CONNECT,data:this._pid?Object.assign({pid:this._pid,offset:this._lastOffset},e):e})}onerror(e){this.connected||this.emitReserved("connect_error",e)}onclose(e,t){a("close (%s)",e),this.connected=!1,delete this.id,this.emitReserved("disconnect",e,t)}onpacket(e){if(e.nsp===this.nsp)switch(e.type){case r.PacketType.CONNECT:e.data&&e.data.sid?this.onconnect(e.data.sid,e.data.pid):this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case r.PacketType.EVENT:case r.PacketType.BINARY_EVENT:this.onevent(e);break;case r.PacketType.ACK:case r.PacketType.BINARY_ACK:this.onack(e);break;case r.PacketType.DISCONNECT:this.ondisconnect();break;case r.PacketType.CONNECT_ERROR:this.destroy();const t=new Error(e.data.message);t.data=e.data.data,this.emitReserved("connect_error",t)}}onevent(e){const t=e.data||[];a("emitting event %j",t),null!=e.id&&(a("attaching ack callback to event"),t.push(this.ack(e.id))),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t))}emitEvent(e){if(this._anyListeners&&this._anyListeners.length){const t=this._anyListeners.slice();for(const s of t)s.apply(this,e)}super.emit.apply(this,e),this._pid&&e.length&&"string"==typeof e[e.length-1]&&(this._lastOffset=e[e.length-1])}ack(e){const t=this;let s=!1;return function(...n){s||(s=!0,a("sending ack %j",n),t.packet({type:r.PacketType.ACK,id:e,data:n}))}}onack(e){const t=this.acks[e.id];"function"==typeof t?(a("calling ack %s with %j",e.id,e.data),t.apply(this,e.data),delete this.acks[e.id]):a("bad ack %s",e.id)}onconnect(e,t){a("socket connected with id %s",e),this.id=e,this.recovered=t&&this._pid===t,this._pid=t,this.connected=!0,this.emitBuffered(),this.emitReserved("connect"),this._drainQueue(!0)}emitBuffered(){this.receiveBuffer.forEach((e=>this.emitEvent(e))),this.receiveBuffer=[],this.sendBuffer.forEach((e=>{this.notifyOutgoingListeners(e),this.packet(e)})),this.sendBuffer=[]}ondisconnect(){a("server disconnect (%s)",this.nsp),this.destroy(),this.onclose("io server disconnect")}destroy(){this.subs&&(this.subs.forEach((e=>e())),this.subs=void 0),this.io._destroy(this)}disconnect(){return this.connected&&(a("performing disconnect (%s)",this.nsp),this.packet({type:r.PacketType.DISCONNECT})),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}close(){return this.disconnect()}compress(e){return this.flags.compress=e,this}get volatile(){return this.flags.volatile=!0,this}timeout(e){return this.flags.timeout=e,this}onAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}prependAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}offAny(e){if(!this._anyListeners)return this;if(e){const t=this._anyListeners;for(let s=0;s<t.length;s++)if(e===t[s])return t.splice(s,1),this}else this._anyListeners=[];return this}listenersAny(){return this._anyListeners||[]}onAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.push(e),this}prependAnyOutgoing(e){return this._anyOutgoingListeners=this._anyOutgoingListeners||[],this._anyOutgoingListeners.unshift(e),this}offAnyOutgoing(e){if(!this._anyOutgoingListeners)return this;if(e){const t=this._anyOutgoingListeners;for(let s=0;s<t.length;s++)if(e===t[s])return t.splice(s,1),this}else this._anyOutgoingListeners=[];return this}listenersAnyOutgoing(){return this._anyOutgoingListeners||[]}notifyOutgoingListeners(e){if(this._anyOutgoingListeners&&this._anyOutgoingListeners.length){const t=this._anyOutgoingListeners.slice();for(const s of t)s.apply(this,e.data)}}}t.Socket=u},84:function(e,t,s){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.url=void 0;const r=s(679),o=n(s(669)).default("socket.io-client:url");t.url=function(e,t="",s){let n=e;s=s||"undefined"!=typeof location&&location,null==e&&(e=s.protocol+"//"+s.host),"string"==typeof e&&("/"===e.charAt(0)&&(e="/"===e.charAt(1)?s.protocol+e:s.host+e),/^(https?|wss?):\/\//.test(e)||(o("protocol-less url %s",e),e=void 0!==s?s.protocol+"//"+e:"https://"+e),o("parse %s",e),n=r.parse(e)),n.port||(/^(http|ws)$/.test(n.protocol)?n.port="80":/^(http|ws)s$/.test(n.protocol)&&(n.port="443")),n.path=n.path||"/";const i=-1!==n.host.indexOf(":")?"["+n.host+"]":n.host;return n.id=n.protocol+"://"+i+":"+n.port+t,n.href=n.protocol+"://"+i+(s&&s.port===n.port?"":":"+n.port),n}},880:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reconstructPacket=t.deconstructPacket=void 0;const n=s(665);function r(e,t){if(!e)return e;if((0,n.isBinary)(e)){const s={_placeholder:!0,num:t.length};return t.push(e),s}if(Array.isArray(e)){const s=new Array(e.length);for(let n=0;n<e.length;n++)s[n]=r(e[n],t);return s}if("object"==typeof e&&!(e instanceof Date)){const s={};for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&(s[n]=r(e[n],t));return s}return e}function o(e,t){if(!e)return e;if(e&&!0===e._placeholder){if("number"==typeof e.num&&e.num>=0&&e.num<t.length)return t[e.num];throw new Error("illegal attachments")}if(Array.isArray(e))for(let s=0;s<e.length;s++)e[s]=o(e[s],t);else if("object"==typeof e)for(const s in e)Object.prototype.hasOwnProperty.call(e,s)&&(e[s]=o(e[s],t));return e}t.deconstructPacket=function(e){const t=[],s=e.data,n=e;return n.data=r(s,t),n.attachments=t.length,{packet:n,buffers:t}},t.reconstructPacket=function(e,t){return e.data=o(e.data,t),delete e.attachments,e}},514:(e,t,s)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Decoder=t.Encoder=t.PacketType=t.protocol=void 0;const n=s(260),r=s(880),o=s(665),i=(0,s(618).default)("socket.io-parser");var a;t.protocol=5,function(e){e[e.CONNECT=0]="CONNECT",e[e.DISCONNECT=1]="DISCONNECT",e[e.EVENT=2]="EVENT",e[e.ACK=3]="ACK",e[e.CONNECT_ERROR=4]="CONNECT_ERROR",e[e.BINARY_EVENT=5]="BINARY_EVENT",e[e.BINARY_ACK=6]="BINARY_ACK"}(a=t.PacketType||(t.PacketType={})),t.Encoder=class{constructor(e){this.replacer=e}encode(e){return i("encoding packet %j",e),e.type!==a.EVENT&&e.type!==a.ACK||!(0,o.hasBinary)(e)?[this.encodeAsString(e)]:this.encodeAsBinary({type:e.type===a.EVENT?a.BINARY_EVENT:a.BINARY_ACK,nsp:e.nsp,data:e.data,id:e.id})}encodeAsString(e){let t=""+e.type;return e.type!==a.BINARY_EVENT&&e.type!==a.BINARY_ACK||(t+=e.attachments+"-"),e.nsp&&"/"!==e.nsp&&(t+=e.nsp+","),null!=e.id&&(t+=e.id),null!=e.data&&(t+=JSON.stringify(e.data,this.replacer)),i("encoded %j as %s",e,t),t}encodeAsBinary(e){const t=(0,r.deconstructPacket)(e),s=this.encodeAsString(t.packet),n=t.buffers;return n.unshift(s),n}};class c extends n.Emitter{constructor(e){super(),this.reviver=e}add(e){let t;if("string"==typeof e){if(this.reconstructor)throw new Error("got plaintext data when reconstructing a packet");t=this.decodeString(e);const s=t.type===a.BINARY_EVENT;s||t.type===a.BINARY_ACK?(t.type=s?a.EVENT:a.ACK,this.reconstructor=new u(t),0===t.attachments&&super.emitReserved("decoded",t)):super.emitReserved("decoded",t)}else{if(!(0,o.isBinary)(e)&&!e.base64)throw new Error("Unknown type: "+e);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");t=this.reconstructor.takeBinaryData(e),t&&(this.reconstructor=null,super.emitReserved("decoded",t))}}decodeString(e){let t=0;const s={type:Number(e.charAt(0))};if(void 0===a[s.type])throw new Error("unknown packet type "+s.type);if(s.type===a.BINARY_EVENT||s.type===a.BINARY_ACK){const n=t+1;for(;"-"!==e.charAt(++t)&&t!=e.length;);const r=e.substring(n,t);if(r!=Number(r)||"-"!==e.charAt(t))throw new Error("Illegal attachments");s.attachments=Number(r)}if("/"===e.charAt(t+1)){const n=t+1;for(;++t&&","!==e.charAt(t)&&t!==e.length;);s.nsp=e.substring(n,t)}else s.nsp="/";const n=e.charAt(t+1);if(""!==n&&Number(n)==n){const n=t+1;for(;++t;){const s=e.charAt(t);if(null==s||Number(s)!=s){--t;break}if(t===e.length)break}s.id=Number(e.substring(n,t+1))}if(e.charAt(++t)){const n=this.tryParse(e.substr(t));if(!c.isPayloadValid(s.type,n))throw new Error("invalid payload");s.data=n}return i("decoded %s as %j",e,s),s}tryParse(e){try{return JSON.parse(e,this.reviver)}catch(e){return!1}}static isPayloadValid(e,t){switch(e){case a.CONNECT:return"object"==typeof t;case a.DISCONNECT:return void 0===t;case a.CONNECT_ERROR:return"string"==typeof t||"object"==typeof t;case a.EVENT:case a.BINARY_EVENT:return Array.isArray(t)&&("string"==typeof t[0]||"number"==typeof t[0]);case a.ACK:case a.BINARY_ACK:return Array.isArray(t)}}destroy(){this.reconstructor&&(this.reconstructor.finishedReconstruction(),this.reconstructor=null)}}t.Decoder=c;class u{constructor(e){this.packet=e,this.buffers=[],this.reconPack=e}takeBinaryData(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){const e=(0,r.reconstructPacket)(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}finishedReconstruction(){this.reconPack=null,this.buffers=[]}}},665:(e,t)=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.hasBinary=t.isBinary=void 0;const s="function"==typeof ArrayBuffer,n=Object.prototype.toString,r="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===n.call(Blob),o="function"==typeof File||"undefined"!=typeof File&&"[object FileConstructor]"===n.call(File);function i(e){return s&&(e instanceof ArrayBuffer||(e=>"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):e.buffer instanceof ArrayBuffer)(e))||r&&e instanceof Blob||o&&e instanceof File}t.isBinary=i,t.hasBinary=function e(t,s){if(!t||"object"!=typeof t)return!1;if(Array.isArray(t)){for(let s=0,n=t.length;s<n;s++)if(e(t[s]))return!0;return!1}if(i(t))return!0;if(t.toJSON&&"function"==typeof t.toJSON&&1===arguments.length)return e(t.toJSON(),!0);for(const s in t)if(Object.prototype.hasOwnProperty.call(t,s)&&e(t[s]))return!0;return!1}},260:(e,t,s)=>{"use strict";function n(e){if(e)return function(e){for(var t in n.prototype)e[t]=n.prototype[t];return e}(e)}s.r(t),s.d(t,{Emitter:()=>n}),n.prototype.on=n.prototype.addEventListener=function(e,t){return this._callbacks=this._callbacks||{},(this._callbacks["$"+e]=this._callbacks["$"+e]||[]).push(t),this},n.prototype.once=function(e,t){function s(){this.off(e,s),t.apply(this,arguments)}return s.fn=t,this.on(e,s),this},n.prototype.off=n.prototype.removeListener=n.prototype.removeAllListeners=n.prototype.removeEventListener=function(e,t){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var s,n=this._callbacks["$"+e];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+e],this;for(var r=0;r<n.length;r++)if((s=n[r])===t||s.fn===t){n.splice(r,1);break}return 0===n.length&&delete this._callbacks["$"+e],this},n.prototype.emit=function(e){this._callbacks=this._callbacks||{};for(var t=new Array(arguments.length-1),s=this._callbacks["$"+e],n=1;n<arguments.length;n++)t[n-1]=arguments[n];if(s){n=0;for(var r=(s=s.slice(0)).length;n<r;++n)s[n].apply(this,t)}return this},n.prototype.emitReserved=n.prototype.emit,n.prototype.listeners=function(e){return this._callbacks=this._callbacks||{},this._callbacks["$"+e]||[]},n.prototype.hasListeners=function(e){return!!this.listeners(e).length}}},t={};function s(n){var r=t[n];if(void 0!==r)return r.exports;var o=t[n]={exports:{}};return e[n].call(o.exports,o,o.exports,s),o.exports}s.d=(e,t)=>{for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},s.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),s.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s(628)})();
\ No newline at end of file
index 898b78a43d311b0cd1a15146b291c2912b7eb7cf..f9104106395ef7f58eb990bc183e6d31b31e7660 100644 (file)
@@ -785,3 +785,7 @@ footer {
     overflow: auto;
   }
 }
     overflow: auto;
   }
 }
+
+.dungeon-room-description {
+  padding: 1rem;
+}
index 2e1bfbaed0c32d6761b7854d1b08bc01f84b8079..866907fa490d7c7e27cf970d42f38e548270258b 100644 (file)
@@ -73,7 +73,8 @@ export async function createLocations(): Promise<void> {
           type: r.fields.Type,
           city_id: r.fields.city_id[0],
           display_order: r.fields["Display Order"],
           type: r.fields.Type,
           city_id: r.fields.city_id[0],
           display_order: r.fields["Display Order"],
-          event_name: r.fields['event_name']
+          event_name: r.fields['event_name'],
+          min_level: Math.max(parseInt(r.fields['Min Level'].toString()), 1)
         }
       })).onConflict('id').merge();
 
         }
       })).onConflict('id').merge();
 
diff --git a/seeds/dungeons.ts b/seeds/dungeons.ts
new file mode 100644 (file)
index 0000000..eb210d1
--- /dev/null
@@ -0,0 +1,176 @@
+import { config as dotenv } from 'dotenv';
+import { db } from "../src/server/lib/db";
+import { join } from 'path';
+import { Dungeon, DungeonRoom, RoomExit } from '../src/shared/dungeon';
+import * as fs from 'fs';
+import * as marked from 'marked';
+import { map, max } from 'lodash';
+
+dotenv();
+
+type TwisonLink = {
+  name: string;
+  ink: string;
+  pid: string;
+}
+
+type TwisonPassage = {
+  text: string;
+  name: string;
+  pid: string;
+  props?: {
+    fight?: {
+      monster_id: string;
+      one_time: string;
+    },
+    visit?: Record<string, string>,
+    end?: string,
+    rewards?: {
+      base: {
+        exp?: string,
+        gold?: string
+      },
+      per_kill_bonus?: {
+        exp?: string,
+        gold?: string
+      }
+    }
+  },
+  position: {
+    x: string;
+    y: string;
+  };
+  links:TwisonLink[]
+}
+
+type TwisonDungeon = {
+  passages: TwisonPassage[];
+  name: string;
+  startnode: string;
+  creator: string;
+  'creator-version': string;
+  ifid: string;
+}
+
+async function getFileData(filename: string): Promise<TwisonDungeon>  {
+  const data = fs.readFileSync(join(__dirname, '..', 'data', 'dungeons', filename), 'utf8');
+
+  console.log(data);
+
+  return JSON.parse(data) as TwisonDungeon;
+}
+
+export async function main(filename: string, done?: any) {
+  await db('dungeon_players').delete();
+
+  const raw = await getFileData(filename);
+
+  // attempt to create base dungeon!
+  const dungeon: Dungeon = (await db('dungeons').insert({
+    id: raw.ifid.toLowerCase(),
+    name: raw.name
+  }).onConflict('id').merge().returning('*')).pop();
+
+  // clear the rooms for the created dungeon
+  await db('dungeon_rooms').where({dungeon_id: dungeon.id}).delete();
+
+  // create the rooms first!
+  const roomsToCreate = raw.passages.map(passage => {
+    let data: Omit<DungeonRoom, 'id'> = {
+      dungeon_id: dungeon.id,
+      description: marked.parse(passage.text.split('<commands>')[0]),
+      exits: [],
+      settings: {}
+    };
+
+    if(passage.props) {
+      if(passage.props.fight) {
+        data.settings.fight = {
+          monster_id: parseInt(passage.props.fight.monster_id),
+          one_time: passage.props.fight.one_time.toLowerCase() === 'true'
+        }
+      }
+      if(passage.props.visit) {
+        data.settings.visit = map(passage.props.visit, (str: string, index: string) => {
+          return {
+            visit_number: parseInt(index),
+            description: marked.parse(str)
+          }
+        });
+      }
+      if(passage.props.end) {
+        data.settings.end = true;
+      }
+      if(passage.props.rewards) {
+        data.settings.rewards = {
+          base: {
+            exp: max([parseInt(passage.props.rewards.base.exp || '0'), 0]),
+            gold: max([parseInt(passage.props.rewards.base.gold || '0'), 0]),
+          }
+        };
+
+        if(passage.props.rewards.per_kill_bonus) {
+          data.settings.rewards.per_kill_bonus = {
+            exp: max([parseInt(passage.props.rewards.per_kill_bonus.exp || '0'), 0]),
+            gold: max([parseInt(passage.props.rewards.per_kill_bonus.gold || '0'), 0]),
+          }
+        }
+      }
+    }
+
+    return data;
+  });
+
+  const createdRooms: DungeonRoom[] = await db('dungeon_rooms').insert(roomsToCreate,).returning('*');
+
+  console.log(createdRooms);
+
+  // set the starting room!
+  await db('dungeons').update({starting_room: createdRooms[pidToIndex(raw.startnode)].id.toLowerCase()}).where({
+    id: dungeon.id
+  });
+
+  const exits = raw.passages.map(passage => {
+    const roomIndex = pidToIndex(passage.pid);
+    let exits: RoomExit[] = [];
+
+    // ending nodes don't have any links
+    if(passage.links) {
+      exits = passage.links.map(link => {
+        const pid = pidToIndex(link.pid);
+        return {
+          name: link.name,
+          target_room_id: createdRooms[pid].id
+        } 
+      });
+    }
+
+    return {
+      id: createdRooms[roomIndex].id,
+      exits
+    };
+  });
+
+  let i = 0;
+  exits.map(async exit => {
+    await db('dungeon_rooms').update({exits: JSON.stringify(exit.exits)}).where({
+      id: exit.id
+    });
+    console.log(`${++i}/${raw.passages.length} rooms created`);
+
+    if(i === raw.passages.length) {
+      done();
+    }
+  });
+
+  console.log(JSON.stringify(exits, null, 2));
+}
+
+function pidToIndex(str: string): number {
+  return parseInt(str)-1;
+}
+
+main('storage_cellar.json', () => {
+  console.log('Dungeons created!');
+  process.exit(0);
+});
index 2dc56080732463482a69a9d8334e0bbc5da9992d..5b14ef375b89f0a024338e74a16948aeeed0a4e1 100644 (file)
@@ -119,6 +119,12 @@ $<HTMLElement>('body').addEventListener('click', e => {
 
   if(target.getAttribute('formmethod') === 'dialog' && target.getAttribute('value') === 'cancel') {
     target.closest('dialog')?.close();
 
   if(target.getAttribute('formmethod') === 'dialog' && target.getAttribute('value') === 'cancel') {
     target.closest('dialog')?.close();
+    // sometimes dialog buttons have a "nav direct"
+    const attr = target.getAttribute('nav-trigger');
+    if(attr) {
+      const [selector, click_num] = attr.split('|');
+      $$<HTMLElement>(selector)[click_num].click();
+    }
   }
 });
 
   }
 });
 
diff --git a/src/server/admin.ts b/src/server/admin.ts
new file mode 100644 (file)
index 0000000..e45466d
--- /dev/null
@@ -0,0 +1,6 @@
+import { Permission } from "../shared/player";
+import { db } from './lib/db';
+
+export async function givePlayerPermission(player_id: string, permission: Permission) {
+  return db('player_permissions').insert({player_id, permission});
+}
index 0aa2689c95a6e594999ccffc22309771648ee358..c476f770041a2c927a4bfd8910077dd367a71657 100644 (file)
@@ -18,10 +18,10 @@ import {maxHp, maxVigor, Player} from '../shared/player';
 import {createFight, getMonsterList, getMonsterLocation, getRandomMonster, loadMonster, loadMonsterFromFight, loadMonsterWithFaction} from './monster';
 import {addInventoryItem, getEquippedItems, getInventory, getInventoryItem} from './inventory';
 import { getItemFromPlayer, getItemFromShop, getPlayersItems, getShopItems, givePlayerItem, updateItemCount } from './items';
 import {createFight, getMonsterList, getMonsterLocation, getRandomMonster, loadMonster, loadMonsterFromFight, loadMonsterWithFaction} from './monster';
 import {addInventoryItem, getEquippedItems, getInventory, getInventoryItem} from './inventory';
 import { getItemFromPlayer, getItemFromShop, getPlayersItems, getShopItems, givePlayerItem, updateItemCount } from './items';
-import {FightTrigger, Monster, MonsterForFight} from '../shared/monsters';
+import {FightTrigger, Monster} from '../shared/monsters';
 import {getShopEquipment, listShopItems } from './shopEquipment';
 import {EquipmentSlot} from '../shared/inventory';
 import {getShopEquipment, listShopItems } from './shopEquipment';
 import {EquipmentSlot} from '../shared/inventory';
-import { clearTravelPlan, completeTravel, getAllPaths, getAllServices, getCityDetails, getService, getTravelPlan, stepForward, travel } from './map';
+import { clearTravelPlan, completeTravel, getAllPaths, getAllServices, getCityDetails, getService, getTravelPlan, stepForward, travel, getDungeon } from './map';
 import { signup, login, authEndpoint } from './auth';
 import {db} from './lib/db';
 import { getPlayerSkills} from './skills';
 import { signup, login, authEndpoint } from './auth';
 import {db} from './lib/db';
 import { getPlayerSkills} from './skills';
@@ -32,8 +32,10 @@ import { fightRound, blockPlayerInFight } from './fight';
 import { router as healerRouter } from './locations/healer';
 import { router as professionRouter } from './locations/recruiter';
 import { router as repairRouter } from './locations/repair';
 import { router as healerRouter } from './locations/healer';
 import { router as professionRouter } from './locations/recruiter';
 import { router as repairRouter } from './locations/repair';
+import { router as dungeonRouter } from './locations/dungeon';
 
 import * as Alert from './views/alert';
 
 import * as Alert from './views/alert';
+import { ExplorePane } from './views/components/explore-pane';
 import { renderPlayerBar } from './views/player-bar'
 import { renderEquipmentDetails, renderStore } from './views/stores';
 import { renderMap } from './views/map';
 import { renderPlayerBar } from './views/player-bar'
 import { renderEquipmentDetails, renderStore } from './views/stores';
 import { renderMap } from './views/map';
@@ -49,11 +51,15 @@ import { renderChatMessage } from './views/chat';
 import { Item, PlayerItem, ShopItem } from 'shared/items';
 import { equip, unequip } from './equipment';
 import { HealthPotionSmall } from '../shared/items/health_potion';
 import { Item, PlayerItem, ShopItem } from 'shared/items';
 import { equip, unequip } from './equipment';
 import { HealthPotionSmall } from '../shared/items/health_potion';
+import { completeDungeonFight, getActiveDungeon, getRoomVists, loadRoom, blockPlayerInDungeon } from './dungeon';
+import { renderDungeon, renderDungeonRoom } from './views/dungeons/room';
+import { flushBuffer, addEvent } from './events';
 
 dotenv();
 
 otel.s();
 
 
 dotenv();
 
 otel.s();
 
+flushBuffer();
 const app = express();
 const server = http.createServer(app);
 
 const app = express();
 const server = http.createServer(app);
 
@@ -140,12 +146,15 @@ io.on('connection', async socket => {
   // this is a special event to let the client know it can start 
   // requesting data
   socket.emit('ready');
   // this is a special event to let the client know it can start 
   // requesting data
   socket.emit('ready');
+
+  addEvent('LOGIN', player.id);
 });
 
 
 app.use(healerRouter);
 app.use(professionRouter);
 app.use(repairRouter);
 });
 
 
 app.use(healerRouter);
 app.use(professionRouter);
 app.use(repairRouter);
+app.use(dungeonRouter);
 
 
 app.get('/chat/history', authEndpoint, async (req: Request, res: Response) => {
 
 
 app.get('/chat/history', authEndpoint, async (req: Request, res: Response) => {
@@ -163,14 +172,12 @@ app.post('/chat', authEndpoint, async (req: Request, res: Response) => {
   }
 
   if(msg.startsWith('/server') && req.player.permissions.includes('admin')) {
   }
 
   if(msg.startsWith('/server') && req.player.permissions.includes('admin')) {
+    const sender = io.sockets.sockets.get(cache.get(`socket:${req.player.id}`));
     try {
     try {
-      const output = await handleChatCommands(msg, req.player, io);
-      if(output) {
-        io.to(cache.get(`socket:${req.player.id}`)).emit('chat', renderChatMessage(output));
-      }
+      await handleChatCommands(msg, req.player, io, sender);
     }
     catch(e) {
     }
     catch(e) {
-      io.to(cache.get(`socket:${req.player.id}`)).emit('chat', renderChatMessage(broadcastMessage('server', e.message)));
+      sender.emit('chat', renderChatMessage(broadcastMessage('server', e.message)));
     }
   }
   else if(msg === '/online') {
     }
   }
   else if(msg === '/online') {
@@ -235,7 +242,7 @@ app.get('/player/inventory', authEndpoint, async (req: Request, res: Response) =
   res.send(renderInventoryPage(inventory, items));
 });
 
   res.send(renderInventoryPage(inventory, items));
 });
 
-app.post('/player/equip/:item_id/:slot', authEndpoint, blockPlayerInFight, async (req: Request, res: Response) => {
+app.post('/player/equip/:item_id/:slot', authEndpoint, blockPlayerInFight, blockPlayerInDungeon, async (req: Request, res: Response) => {
   const inventoryItem = await getInventoryItem(req.player.id, req.params.item_id);
   const equippedItems = await getEquippedItems(req.player.id);
   const requestedSlot = req.params.slot;
   const inventoryItem = await getInventoryItem(req.player.id, req.params.item_id);
   const equippedItems = await getEquippedItems(req.player.id);
   const requestedSlot = req.params.slot;
@@ -287,7 +294,7 @@ app.post('/player/equip/:item_id/:slot', authEndpoint, blockPlayerInFight, async
   res.send(renderInventoryPage(inventory, items, inventoryItem.type) + renderPlayerBar(req.player));
 });
 
   res.send(renderInventoryPage(inventory, items, inventoryItem.type) + renderPlayerBar(req.player));
 });
 
-app.post('/player/unequip/:item_id', authEndpoint, blockPlayerInFight, async (req: Request, res: Response) => {
+app.post('/player/unequip/:item_id', authEndpoint, blockPlayerInFight, blockPlayerInDungeon, async (req: Request, res: Response) => {
   const [item, ] = await Promise.all([
     getInventoryItem(req.player.id, req.params.item_id),
     unequip(req.player.id, req.params.item_id)
   const [item, ] = await Promise.all([
     getInventoryItem(req.player.id, req.params.item_id),
     unequip(req.player.id, req.params.item_id)
@@ -310,44 +317,53 @@ app.get('/player/explore', authEndpoint, async (req: Request, res: Response) =>
       closestTown = (travelPlan.current_position / travelPlan.total_distance) > 0.5 ? travelPlan.destination_id : travelPlan.source_id;
   }
 
       closestTown = (travelPlan.current_position / travelPlan.total_distance) > 0.5 ? travelPlan.destination_id : travelPlan.source_id;
   }
 
-  if(fight) {
+  if(fight && req.player.hp > 0) {
     const location = await getMonsterLocation(fight.ref_id);
 
     res.send(renderPlayerBar(req.player) + renderFightPreRound(fight, true, location, closestTown));
     const location = await getMonsterLocation(fight.ref_id);
 
     res.send(renderPlayerBar(req.player) + renderFightPreRound(fight, true, location, closestTown));
+    return;
   }
   }
-  else {
-    if(travelPlan) {
-      // traveling!
-      const chanceToSeeMonster = random(0, 100);
-      const things: any[] = [];
-      if(chanceToSeeMonster <= 30) {
-        const monster = await getRandomMonster([closestTown]);
-        things.push(monster);
-      }
 
 
-      // STEP_DELAY
-      const nextAction = cache[`step:${req.player.id}`] || 0;
+  const dungeon = await getActiveDungeon(req.player.id);
+  if(dungeon && req.player.hp > 0) {
+    const service = await getDungeon(dungeon.dungeon_id);
+    const room = await loadRoom(dungeon.current_room_id);
+    const visits = await getRoomVists(req.player.id, service.event_name);
 
 
-      res.send(renderPlayerBar(req.player) + renderTravel({
-        things,
-        nextAction,
-        closestTown: closestTown,
-        walkingText: '',
-        travelPlan
-      }));
-    }
-    else {
-      // display the city info!
-      const [city, locations, paths] = await Promise.all([
-        getCityDetails(req.player.city_id),
-        getAllServices(req.player.city_id),
-        getAllPaths(req.player.city_id)
-      ]);
-
-      res.send(renderPlayerBar(req.player) + await renderMap({city, locations, paths}, closestTown));
+    res.send(ExplorePane(service.city_id, renderDungeon(service.city_name, service.name, room, visits)));
+    return;
+  }
+
+  if(travelPlan && req.player.hp > 0) {
+    // traveling!
+    const chanceToSeeMonster = random(0, 100);
+    const things: any[] = [];
+    if(chanceToSeeMonster <= 30) {
+      const monster = await getRandomMonster([closestTown]);
+      things.push(monster);
     }
 
     }
 
+    // STEP_DELAY
+    const nextAction = cache[`step:${req.player.id}`] || 0;
+
+    res.send(renderPlayerBar(req.player) + renderTravel({
+      things,
+      nextAction,
+      closestTown: closestTown,
+      walkingText: '',
+      travelPlan
+    }));
+    return;
   }
   }
+
+  // display the default explore view
+  const [city, locations, paths] = await Promise.all([
+    getCityDetails(req.player.city_id),
+    getAllServices(req.player.city_id, req.player.level),
+    getAllPaths(req.player.city_id)
+  ]);
+
+  res.send(renderPlayerBar(req.player) + await renderMap({city, locations, paths}, closestTown));
 });
 
 // used to purchase equipment from a particular shop
 });
 
 // used to purchase equipment from a particular shop
@@ -593,6 +609,40 @@ app.post('/fight/turn', authEndpoint, async (req: Request, res: Response) => {
   });
 
 
   });
 
 
+  if(fightData.roundData.winner !== 'in-progress') {
+    delete cache[fightBlockKey];
+  }
+
+  if(fightData.roundData.winner === 'player') {
+    //@TODO: Add equipped weapons
+    addEvent('MONSTER_KILLED', req.player.id, {
+      monster_id: monster.ref_id,
+      monster_name: monster.name,
+      monster_level: monster.level,
+      fight_trigger: monster.fight_trigger,
+    });
+  }
+  else if(fightData.roundData.winner === 'monster') {
+    addEvent('PLAYER_KILLED', req.player.id, {
+      monster_id: monster.ref_id,
+      monster_name: monster.name,
+      monster_level: monster.level,
+      fight_trigger: monster.fight_trigger
+    });
+  }
+
+
+  if(monster.fight_trigger === 'dungeon-forced' && fightData.roundData.winner === 'player') {
+    // ok the player was in a dungeon, lets make sure 
+    // that they complete whatever dungeon room they are in
+    const dungeonState = await completeDungeonFight(req.player.id, monster);
+    const room = await loadRoom(dungeonState.current_room_id);
+    const visits = await getRoomVists(req.player.id, room.dungeon_id);
+
+    res.send(renderDungeonRoom(room, visits));
+    return;
+  }
+
   let html = renderFight(
     monster,
     renderRoundDetails(fightData.roundData),
   let html = renderFight(
     monster,
     renderRoundDetails(fightData.roundData),
@@ -600,10 +650,6 @@ app.post('/fight/turn', authEndpoint, async (req: Request, res: Response) => {
     cache[fightBlockKey]
   );
 
     cache[fightBlockKey]
   );
 
-  if(fightData.roundData.winner !== 'in-progress') {
-    delete cache[fightBlockKey];
-  }
-
   if(fightData.monsters.length && monster.fight_trigger === 'explore') {
     html += renderMonsterSelector(fightData.monsters, monster.ref_id);
   }
   if(fightData.monsters.length && monster.fight_trigger === 'explore') {
     html += renderMonsterSelector(fightData.monsters, monster.ref_id);
   }
@@ -679,7 +725,7 @@ app.post('/travel/step', authEndpoint, async (req: Request, res: Response) => {
 
     const [city, locations, paths] = await Promise.all([
       getCityDetails(travel.destination_id),
 
     const [city, locations, paths] = await Promise.all([
       getCityDetails(travel.destination_id),
-      getAllServices(travel.destination_id),
+      getAllServices(travel.destination_id, req.player.level),
       getAllPaths(travel.destination_id)
     ]);
 
       getAllPaths(travel.destination_id)
     ]);
 
@@ -736,7 +782,7 @@ app.post('/travel/return-to-source', authEndpoint, async (req: Request, res: Res
   else {
     const [city, locations, paths] = await Promise.all([
       getCityDetails(req.player.city_id),
   else {
     const [city, locations, paths] = await Promise.all([
       getCityDetails(req.player.city_id),
-      getAllServices(req.player.city_id),
+      getAllServices(req.player.city_id, req.player.level),
       getAllPaths(req.player.city_id)
     ]);
 
       getAllPaths(req.player.city_id)
     ]);
 
index 5e43627095d6ec5a19b5ec5395fd74ea2fa466ca..527e77e30a1a4636a60d06835d8bb1efa7e84cc1 100644 (file)
@@ -1,52 +1,24 @@
-import { Server } from 'socket.io';
-import { maxHp, maxVigor, Player } from '../shared/player';
-import { createMonsters } from '../../seeds/monsters';
-import { createAllCitiesAndLocations } from '../../seeds/cities';
-import { createShopItems, createShopEquipment } from '../../seeds/shop_items';
-import { broadcastMessage, Message } from '../shared/message';
-import { updatePlayer } from './player';
+import { Server, Socket } from 'socket.io';
+import { Player } from '../shared/player';
+import { broadcastMessage } from '../shared/message';
+import { renderChatMessage } from './views/chat';
+import { Commands } from './chat-commands/';
 
 
-export async function handleChatCommands(msg: string, player: Player, io: Server): Promise<Message> {
-  let message: Message;
-  if(msg === '/server refresh-monsters') {
-    await createMonsters();
-    message = broadcastMessage('server', 'Monster refresh!');
-  }
-  else if(msg === '/server refresh-cities') {
-    await createAllCitiesAndLocations();
-    message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!');
-  }
-  else if(msg === '/server refresh-shops') {
-    await createShopItems();
-    await createShopEquipment();
-    message = broadcastMessage('server', 'Refresh shop items');
-  }
-  else if(msg.startsWith('/server set-level')) {
-    const level = parseInt(msg.split(' ').pop());
-    if(level < 1) {
-      message = broadcastMessage('server', 'Needs to be at least level 1');
-    }
-    else {
-      message = broadcastMessage('server', `Set player level: ${level}`);
 
 
-      player.level = level;
-      player.strength = 4;
-      player.constitution = 4;
-      player.dexterity = 4;
-      player.intelligence = 4;
-      player.hp = maxHp(player.constitution, player.level);
-      player.vigor = maxVigor(player.constitution, player.level);
-      player.stat_points = level-1;
+export async function handleChatCommands(msg: string, player: Player, io: Server, sender: Socket): Promise<void> {
+  const rawCommand = msg.split('/server ')[1];
 
 
-      await updatePlayer(player);
+  let matched = false;
+  Commands.forEach(async command => {
+    if(command.regex.test(rawCommand)) {
+      matched = true;
+      console.log(`${player.username} running command: [${rawCommand}]`);
+      await command.handler(rawCommand, sender, player, io);
     }
     }
-  }
-  else {
-    const str = msg.split('/server ')[1];
-    if(str) {
-      message = broadcastMessage('server', str);
-    }
-  }
+  });
 
 
-  return message;
+  if(!matched) {
+    const message = broadcastMessage('server', `Invalid command: [${rawCommand}]`);
+    sender.emit('chat', renderChatMessage(message));
+  }
 }
 }
diff --git a/src/server/chat-commands/base.ts b/src/server/chat-commands/base.ts
new file mode 100644 (file)
index 0000000..8241067
--- /dev/null
@@ -0,0 +1,14 @@
+import type { Player } from '../../shared/player';
+import type { Server, Socket } from 'socket.io';
+
+type ChatCommandHandler = (command: string, sender: Socket, player: Player, io: Server) => Promise<void>;
+
+export class ChatCommand {
+  constructor(
+    public name: string,
+    public regex: RegExp,
+    public handler: ChatCommandHandler
+  ) {
+
+  }
+}
diff --git a/src/server/chat-commands/give-permission.ts b/src/server/chat-commands/give-permission.ts
new file mode 100644 (file)
index 0000000..2586f8c
--- /dev/null
@@ -0,0 +1,31 @@
+import { Socket } from 'socket.io';
+import { Permission, PermissionGuard, Player } from '../../shared/player';
+import { ChatCommand } from './base';
+import { givePlayerPermission } from '../admin';
+import { findPlayerByUsername } from '../player';
+import { broadcastMessage, Message } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+  if(player.permissions.includes('admin')) {
+    const pieces = rawCommand.split(' ');
+    const username = pieces[1];
+    const permission = pieces[2];
+
+    if(PermissionGuard(permission)) {
+      const recipient = await findPlayerByUsername(username);
+      if(player) {
+        await givePlayerPermission(recipient.id, permission as Permission);
+
+        const message: Message = broadcastMessage('server', `Granted ${permission} to ${recipient.username}`);
+        sender.emit('chat', renderChatMessage(message));
+      }
+      else {
+        const message: Message = broadcastMessage('server', `Cant find user [${username}]`);
+        sender.emit('chat', renderChatMessage(message));
+      }
+    }
+  }
+}
+
+export const setPermission = new ChatCommand('give-permission', new RegExp(/^give-permission (.*)+/), handler);
diff --git a/src/server/chat-commands/index.ts b/src/server/chat-commands/index.ts
new file mode 100644 (file)
index 0000000..2ea13bc
--- /dev/null
@@ -0,0 +1,16 @@
+import { ChatCommand } from "./base";
+import { refreshMonsters } from "./refresh-monsters";
+import { refreshCities } from './refresh-cities';
+import { refreshShops } from './refresh-shops';
+import { setLevel } from './set-level';
+import { say } from './say';
+import { setPermission } from './give-permission';
+
+export const Commands = new Set<ChatCommand>();
+
+Commands.add(refreshMonsters);
+Commands.add(refreshCities);
+Commands.add(refreshShops);
+Commands.add(setLevel);
+Commands.add(say);
+Commands.add(setPermission);
diff --git a/src/server/chat-commands/refresh-cities.ts b/src/server/chat-commands/refresh-cities.ts
new file mode 100644 (file)
index 0000000..25c94cb
--- /dev/null
@@ -0,0 +1,16 @@
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { createAllCitiesAndLocations } from '../../../seeds/cities';
+import { broadcastMessage } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+  if(player.permissions.includes('admin')) {
+    await createAllCitiesAndLocations();
+    const message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!');
+    sender.emit('chat', renderChatMessage(message));
+  }
+}
+
+export const refreshCities = new ChatCommand('refresh-cities', new RegExp(/^refresh-cities/), handler);
diff --git a/src/server/chat-commands/refresh-monsters.ts b/src/server/chat-commands/refresh-monsters.ts
new file mode 100644 (file)
index 0000000..e0c0624
--- /dev/null
@@ -0,0 +1,16 @@
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { createMonsters } from '../../../seeds/monsters';
+import { broadcastMessage } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+  if(player.permissions.includes('admin')) {
+    await createMonsters();
+    const message = broadcastMessage('server', 'Monsters refreshed!');
+    sender.emit('chat', renderChatMessage(message));
+  }
+}
+
+export const refreshMonsters = new ChatCommand('refresh-monsters', new RegExp(/^refresh-monsters$/), handler);
diff --git a/src/server/chat-commands/refresh-shops.ts b/src/server/chat-commands/refresh-shops.ts
new file mode 100644 (file)
index 0000000..2f90c3b
--- /dev/null
@@ -0,0 +1,18 @@
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { createShopItems, createShopEquipment } from '../../../seeds/shop_items';
+import { broadcastMessage } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+  if(player.permissions.includes('admin')) {
+    await createShopItems();
+    await createShopEquipment();
+    const message = broadcastMessage('server', 'Shop items refreshed!');
+    sender.emit('chat', renderChatMessage(message));
+
+  }
+}
+
+export const refreshShops = new ChatCommand('refresh-shops', new RegExp(/^refresh-shops/), handler);
diff --git a/src/server/chat-commands/say.ts b/src/server/chat-commands/say.ts
new file mode 100644 (file)
index 0000000..1c1104e
--- /dev/null
@@ -0,0 +1,14 @@
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { broadcastMessage, Message } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+  if(player.permissions.includes('moderator') || player.permissions.includes('admin')) {
+    let message: Message = broadcastMessage('server', rawCommand.split('say ')[1].trim());
+    sender.emit('chat', renderChatMessage(message));
+  }
+}
+
+export const say = new ChatCommand('say', new RegExp(/^say (.*)+/), handler);
diff --git a/src/server/chat-commands/set-level.ts b/src/server/chat-commands/set-level.ts
new file mode 100644 (file)
index 0000000..640a194
--- /dev/null
@@ -0,0 +1,42 @@
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { broadcastMessage, Message } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { updatePlayer } from '../player';
+import { maxHp, maxVigor, Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+  if(player.permissions.includes('admin') || player.permissions.includes('tester')) {
+    let message: Message;
+    // command in set-level username level
+    const pieces = rawCommand.split(' ');
+    if(pieces.length !== 2) {
+      message = broadcastMessage('server', 'format: /set-level level');
+    }
+    else {
+      const level = parseInt(pieces.pop() || '0');
+      if(level < 1) {
+        message = broadcastMessage('server', 'format: /set-level [level >= 1]');
+      }
+      else {
+        message = broadcastMessage('server', `Set to level ${level}. Please reload.`);
+
+        player.level = level;
+        player.strength = 4;
+        player.constitution = 4;
+        player.dexterity = 4;
+        player.intelligence = 4;
+        player.hp = maxHp(player.constitution, player.level);
+        player.vigor = maxVigor(player.constitution, player.level);
+        player.stat_points = level-1;
+
+        await updatePlayer(player);
+      }
+    }
+
+    sender.emit('chat', renderChatMessage(message));
+
+  }
+}
+
+export const setLevel = new ChatCommand('set-level', new RegExp(/^set-level \d+$/), handler);
diff --git a/src/server/dungeon.ts b/src/server/dungeon.ts
new file mode 100644 (file)
index 0000000..33558a9
--- /dev/null
@@ -0,0 +1,178 @@
+import { Fight } from "shared/monsters";
+import { Dungeon, DungeonRoom, DungeonPlayer, DungeonState, DungeonStateSummaryVists, DungeonStateSummaryFights } from "../shared/dungeon";
+import { db } from './lib/db';
+import { Request, Response } from 'express';
+import { ErrorAlert } from "./views/alert";
+import { addEvent } from './events';
+
+export async function blockPlayerInDungeon(req: Request, res: Response, next: any) {
+  const state = await getActiveDungeon(req.player.id);
+  if(!state) {
+    next();
+  }
+  else {
+    res.send(ErrorAlert('You are currently exploring a dungeon'));
+  }
+}
+
+export async function getActiveDungeon(player_id: string): Promise<DungeonPlayer> {
+  return db.select('*').from<DungeonPlayer>('dungeon_players').where({
+    player_id
+  }).first();
+}
+
+export async function loadDungeon(dungeon_id: string): Promise<Dungeon> {
+  return db.select('*').from<Dungeon>('dungeons').where({id: dungeon_id}).first();
+}
+
+export async function getDungeonState(player_id: string, dungeon_id: string) {
+  return db.select('*').from<DungeonState>('dungeon_state').where({
+    player_id, 
+    dungeon_id
+  });
+}
+
+export async function addNewState(player_id: string, dungeon_id: string, event_name: string, props: Record<string, any> = {}) {
+  return db('dungeon_state').insert({
+    player_id,
+    dungeon_id,
+    event_name,
+    event_props: props
+  });
+}
+// in a dungeon fight, we dont care what room the player is in, 
+// but we care what room they are going to
+export async function completeDungeonFight(player_id: string, monster: Fight): Promise<DungeonPlayer> {
+  const activeDungeon = await getActiveDungeon(player_id);
+
+  await addNewState(player_id, activeDungeon.dungeon_id, 'FIGHT_COMPLETE', {
+    monster_id: monster.ref_id,
+    target_room: activeDungeon.target_room_id,
+    source_room: activeDungeon.current_room_id
+  });
+
+  await movePlayerToRoomInDungeon(player_id, activeDungeon.dungeon_id, activeDungeon.target_room_id);
+
+  return getActiveDungeon(player_id);
+}
+
+export async function movePlayerToRoomInDungeon(player_id: string, dungeon_id: string, room_id: string) {
+  await updatePlayerDungeonState(player_id, dungeon_id, { 
+    current_room_id: room_id,
+    target_room_id: room_id
+  });
+
+  await addNewState(player_id, dungeon_id, 'ROOM_VISIT', {
+    room_id
+  });
+}
+
+export async function getRoomVists(player_id: string, dungeon_id: string): Promise<DungeonStateSummaryVists> {
+
+  const res = await db.raw(`select 
+count(id) as visits, 
+event_props->>'room_id' as room_id
+from dungeon_state
+group by event_props->>'room_id',player_id,dungeon_id,event_name
+having player_id = ? and dungeon_id = ? and event_name = ?
+`, [
+    player_id,
+    dungeon_id,
+    'ROOM_VISIT'
+  ]);
+
+  let data = {};
+  res.rows.forEach((row: {room_id: string, visits: string}) => {
+    data[row.room_id] = parseInt(row.visits);
+  });
+
+  return data;
+}
+
+export async function getUniqueFights(player_id: string, dungeon_id: string): Promise<DungeonStateSummaryFights> {
+  // fights always happen between movement source->target. So when we record
+  // the state of a fight, we record the source/target room props.
+  // however, when we are checking if a fight occurrent in a room.. we really 
+  // only care about the TARGET room. 
+  const res = await db.raw(`select 
+count(id) as fights,
+event_props->>'monster_id' as monster_id,
+event_props->>'target_room' as room_id
+from dungeon_state 
+group by event_props->>'monster_id',event_props->>'target_room',
+player_id,dungeon_id,event_name 
+having player_id = ? and dungeon_id = ? and event_name = ?
+`, [
+    player_id,
+    dungeon_id,
+    'FIGHT_COMPLETE'
+  ]);
+
+  let data = {};
+  res.rows.forEach((row: {fights: string, monster_id: string, room_id: string}) => {
+    if(!data[row.room_id]) {
+      data[row.room_id] = {};
+    }
+
+    if(!data[row.room_id][row.monster_id]) {
+      data[row.room_id][row.monster_id] = row.fights;
+    }
+  });
+
+  return data;
+}
+
+export async function putPlayerInDungeon(player_id: string, dungeon_id: string): Promise<DungeonPlayer> {
+  const res: {rows: DungeonPlayer[] }= await db.raw(`
+    insert into dungeon_players 
+(player_id, dungeon_id, current_room_id) 
+values 
+(?, ?, (select starting_room from dungeons where id = ?)) returning *`, [player_id, dungeon_id, dungeon_id])
+
+  const data = res.rows.pop();
+
+  await addNewState(player_id, dungeon_id, 'ROOM_VISIT', {
+    room_id: data.current_room_id
+  });
+
+  return data;
+}
+
+
+export async function updatePlayerDungeonState(player_id: string, dungeon_id: string, fields: any) {
+  const res = await db('dungeon_players').where({
+    player_id,
+    dungeon_id
+  }).update(fields).returning('*');
+
+  return res.pop();
+}
+
+export async function loadRoom(room_id: string): Promise<DungeonRoom | undefined> {
+  return db.select('*')
+            .from<DungeonRoom>('dungeon_rooms')
+            .where({id: room_id})
+            .first();
+}
+
+export async function completeDungeon(player_id: string) {
+  const dungeonPlayer = await getActiveDungeon(player_id);
+  const dungeonState = await getDungeonState(player_id, dungeonPlayer.dungeon_id);
+
+  await db('dungeon_players').where({player_id}).delete();
+  await db('dungeon_state').where({player_id, dungeon_id: dungeonPlayer.dungeon_id}).delete();
+
+  let startTime = Number.MAX_VALUE;
+  let endTime = 0;
+  dungeonState.forEach(s => {
+    startTime = Math.min(startTime, s.create_date);
+    endTime = Math.max(endTime, s.create_date);
+  });
+
+  addEvent('DUNGEON_COMPLETE', player_id, {
+    dungeon_id: dungeonPlayer.dungeon_id,
+    start: startTime,
+    end: endTime,
+    duration: endTime - startTime
+  });
+}
diff --git a/src/server/events.ts b/src/server/events.ts
new file mode 100644 (file)
index 0000000..df0b819
--- /dev/null
@@ -0,0 +1,78 @@
+import { db } from './lib/db';
+import { version } from '../../package.json';
+import { CreatedEvent, Event, EventName } from '../shared/event';
+import { isEqual } from 'lodash';
+import { logger } from './lib/logger';
+import { EVENT_FLUSH_INTERVAL, EVENT_SECOND_BUCKET } from '../shared/constants';
+import { clickhouse } from './lib/clickhouse';
+
+const eventBuffer: CreatedEvent[] = [];
+const maxToAdd = 10;
+
+export async function flushBuffer() {
+  const events = eventBuffer.splice(0, maxToAdd);
+  if(events.length) {
+    await addEvents(events);
+    logger.log(`Flushed ${events.length} events`);
+  }
+  setTimeout(flushBuffer, EVENT_FLUSH_INTERVAL);
+}
+
+function bucketTime(date: Date): Date {
+  const d = new Date();
+  d.setFullYear(date.getFullYear());
+  d.setMonth(date.getMonth());
+  d.setDate(date.getDate());
+  d.setHours(date.getHours());
+  d.setMinutes(date.getMinutes());
+  d.setMilliseconds(0);
+
+  const s = date.getSeconds();
+
+  // round down to closest 5 second interval
+  d.setSeconds(s - (s%EVENT_SECOND_BUCKET));
+  return d;
+}
+
+
+export async function addEvent(event_name: EventName, player_id: string, props?: any, created?: Date) {
+  eventBuffer.push({
+    event_name,
+    player_id,
+    app_version: version,
+    props: props,
+    created
+  });
+}
+
+export async function addEvents(events: CreatedEvent[]) {
+  return db('events').insert(events);
+}
+
+export async function getEventHistory(player_id: string, event_name: EventName) {
+  return db.select('*').from<Event<any>>('events').where({
+    player_id,
+    event_name
+  });
+}
+
+export async function getEventHistoryToday(player_id: string, event_name: EventName) {
+  return db.select('*').from<Event<any>>('events').where({
+    player_id,
+    event_name
+  }).andWhere('created', '>=', db.raw('current_date::timestamp'));
+
+}
+
+export async function hasNeverHappenedBefeore(name: EventName, player_id: string, props?: any): Promise<boolean> {
+  return hasHappendXTimes(0, name, player_id, props);
+}
+
+export async function hasHappendXTimes(times: number, event_name: EventName, player_id: string, props?: any): Promise<boolean> {
+  const res: Event<any>[] = await getEventHistory(player_id, event_name);
+
+  if(props) {
+    return res.filter(row => isEqual(row.props, props)).length === times;
+  }
+  return res.length === times;
+}
index 011cc5c667dfa27a4f4f90035b94aa545a675aa5..4515c30b60edbd8890d9202ee69905b0995cdbb6 100644 (file)
@@ -11,6 +11,7 @@ import { getPlayerSkillsAsObject, updatePlayerSkills } from './skills';
 import { SkillID, Skills } from '../shared/skills';
 import { Request, Response } from 'express';
 import * as Alert from './views/alert';
 import { SkillID, Skills } from '../shared/skills';
 import { Request, Response } from 'express';
 import * as Alert from './views/alert';
+import { addEvent } from './events';
 
 export async function blockPlayerInFight(req: Request, res: Response, next: any) {
   const fight = await loadMonsterFromFight(req.player.id);
 
 export async function blockPlayerInFight(req: Request, res: Response, next: any) {
   const fight = await loadMonsterFromFight(req.player.id);
@@ -162,6 +163,13 @@ export async function fightRound(player: Player, monster: Fight,  data: {action:
     roundData.monster.hp = 0;
     roundData.winner = 'player';
 
     roundData.monster.hp = 0;
     roundData.winner = 'player';
 
+    addEvent('MONSTER_KILLED', player.id, {
+      monster_id: roundData.monster.ref_id,
+      monster_name: roundData.monster.name,
+      level: roundData.monster.level,
+      fightTrigger: roundData.monster.fight_trigger
+    });
+
     const expGained = exponentialExp(monster.exp, monster.level, player.level);
 
     roundData.rewards.exp = expGained;
     const expGained = exponentialExp(monster.exp, monster.level, player.level);
 
     roundData.rewards.exp = expGained;
@@ -173,6 +181,10 @@ export async function fightRound(player: Player, monster: Fight,  data: {action:
     if(player.exp >= expToLevel(player.level + 1)) {
       player.exp -= expToLevel(player.level + 1)
       player.level++;
     if(player.exp >= expToLevel(player.level + 1)) {
       player.exp -= expToLevel(player.level + 1)
       player.level++;
+      addEvent('LEVEL_UP', player.id, {
+        from_level: player.level-1,
+        to_level: player.level
+      });
       roundData.rewards.levelIncrease = true;
       let statPointsGained = 1;
 
       roundData.rewards.levelIncrease = true;
       let statPointsGained = 1;
 
diff --git a/src/server/lib/clickhouse.ts b/src/server/lib/clickhouse.ts
new file mode 100644 (file)
index 0000000..831f46d
--- /dev/null
@@ -0,0 +1,3 @@
+import { createClient } from '@clickhouse/client';
+
+export const clickhouse = createClient();
diff --git a/src/server/locations/dungeon.ts b/src/server/locations/dungeon.ts
new file mode 100644 (file)
index 0000000..26f7460
--- /dev/null
@@ -0,0 +1,186 @@
+import { Router } from "express";
+import { authEndpoint } from '../auth';
+import { logger } from "../lib/logger";
+import { getDungeon, getService } from '../map';
+import { completeDungeon, getActiveDungeon, getRoomVists, getUniqueFights, loadDungeon, loadRoom, movePlayerToRoomInDungeon, putPlayerInDungeon, updatePlayerDungeonState } from '../dungeon';
+import { Dungeon, DungeonRewards } from "../../shared/dungeon";
+import { dungeonRewards, renderDungeon } from '../views/dungeons/room';
+import * as Alert from '../views/alert';
+import { createFight, loadMonster } from "../monster";
+import { renderFightPreRoundDungeon } from "../views/fight";
+import { has, max, each } from 'lodash';
+import { expToLevel, maxHp, maxVigor } from "../../shared/player";
+import { updatePlayer } from "../player";
+import { getEventHistoryToday } from "../events";
+
+export const router = Router();
+
+router.get('/city/dungeon/:dungeon_id/:location_id', authEndpoint, async (req, res) => {
+  let dungeon: Dungeon;
+  let activeDungeon = await getActiveDungeon(req.player.id);
+  // because of how we treat locations + dungeons, the "event_name" of a location 
+  // is actually the uuid of the dungeon. How fancy
+  // in this case service.event_name === dungeon.id
+  const service = await getService(parseInt(req.params.location_id));
+
+  if(service.type !== 'DUNGEON') {
+    logger.log(`Attempting to enter a non-dungeon`);
+    res.sendStatus(400);
+    return;
+  }
+
+  if(service.city_id !== req.player.city_id) {
+    logger.log(`Player is not in the same place as the dungeon: [${req.params.location_id}]`);
+    res.sendStatus(400);
+    return;
+  }
+
+  if(!activeDungeon) {
+    // for a dungeon the "event_name" serves as a mapping between the 
+    // airtable integer id that is used for the location and the ifid (interactive fiction id) 
+    // that is generated by twine
+    activeDungeon = await putPlayerInDungeon(req.player.id, service.event_name);
+  }
+
+  const room = await loadRoom(activeDungeon.current_room_id);
+  const visits = await getRoomVists(req.player.id, service.event_name);
+
+  res.send(renderDungeon(service.city_name, service.name, room, visits));
+});
+
+router.post('/city/dungeon/step/:target_room_id', authEndpoint, async (req, res) => {
+  const activeDungeon = await getActiveDungeon(req.player.id);
+  if(!activeDungeon) {
+    logger.log(`Not in a dungeon`);
+    res.sendStatus(400);
+    return;
+  }
+
+  const dungeon = await loadDungeon(activeDungeon.dungeon_id);
+  const service = await getDungeon(dungeon.id);
+
+  const targetRoomId = req.params.target_room_id.toLowerCase().trim();
+  const currentRoom = await loadRoom(activeDungeon.current_room_id);
+
+  const targetExit = currentRoom.exits.filter(exit => { 
+    return exit.target_room_id === targetRoomId 
+  });
+
+  if(!targetExit.length) {
+    logger.log(`Invalid exit: [${targetRoomId}]`);
+    res.sendStatus(400);
+    return;
+  }
+
+  const nextRoom = await loadRoom(targetRoomId);
+
+  if(!nextRoom) {
+    logger.error(`Dang.. no valid room`, targetRoomId, currentRoom);
+    res.send(Alert.ErrorAlert(`${req.params.direction} is not a valid direction`)).status(400);
+    return;
+  }
+
+
+  // if the room contiains a fight and 
+  // its a one time fight the user hasn't finished
+  // OR
+  // its not a one time fight
+  // render the fight screen
+  if(nextRoom.settings.fight) {
+    const fights = await getUniqueFights(req.player.id, nextRoom.dungeon_id);
+    if(
+      (
+        nextRoom.settings.fight.one_time &&
+        (
+          has(fights, [nextRoom.id, nextRoom.settings.fight.monster_id])
+            ? 
+            fights[nextRoom.id][nextRoom.settings.fight.monster_id]
+            :
+            0
+        ) === 0
+      )
+      || 
+      !nextRoom.settings.fight.one_time
+    ){
+        const monster = await loadMonster(nextRoom.settings.fight.monster_id);
+        const fight = await createFight(req.player.id, monster, 'dungeon-forced');
+
+        // ensure that we know what room the player was attempting to go 
+        // to
+        await updatePlayerDungeonState(req.player.id, currentRoom.dungeon_id, {
+          current_room_id: currentRoom.id,
+          target_room_id:  nextRoom.id
+        });
+
+        // ok render the fight view instead!
+        res.send(renderFightPreRoundDungeon(service.city_name, service.name, fight));
+        return;
+    }
+  }
+
+  await movePlayerToRoomInDungeon(req.player.id, nextRoom.dungeon_id, nextRoom.id);
+  const visits = await getRoomVists(req.player.id, service.event_name);
+
+  res.send(renderDungeon(service.city_name, service.name, nextRoom, visits));
+});
+
+router.post('/city/dungeon/:dungeon_id/complete', authEndpoint, async (req, res) => {
+  const activeDungeon = await getActiveDungeon(req.player.id);
+  if(!activeDungeon) {
+    logger.log(`Not in a dungeon`);
+    res.sendStatus(400);
+    return;
+  }
+
+  const dungeon = await loadDungeon(activeDungeon.dungeon_id);
+  const currentRoom = await loadRoom(activeDungeon.current_room_id);
+
+  if(!currentRoom.settings.end) {
+    logger.log(`Not the end of the dungeon: [${currentRoom.id}]`);
+    res.sendStatus(400);
+    return;
+  }
+
+  const stats = await getUniqueFights(req.player.id, dungeon.id);
+
+  const rewards: DungeonRewards = {
+    exp: max([currentRoom.settings.rewards?.base.exp, 0]),
+    gold: max([currentRoom.settings.rewards?.base.gold, 0])
+  };
+
+  if(currentRoom.settings.rewards.per_kill_bonus) {
+    each(stats, (room) => {
+      each(room, (count) => {
+        if(currentRoom.settings.rewards.per_kill_bonus.exp) {
+          rewards.exp += (count * currentRoom.settings.rewards.per_kill_bonus.exp)
+        }
+        if(currentRoom.settings.rewards.per_kill_bonus.gold) {
+          rewards.gold += (count * currentRoom.settings.rewards.per_kill_bonus.gold)
+        }
+      });
+    });
+  }
+
+
+  // if this is not the first completion, lets give them diminishing returns
+  const completionsToday = await getEventHistoryToday(req.player.id, 'DUNGEON_COMPLETE');
+  let factor = completionsToday.length <= 5 ? 1 : 0.2;
+
+  // give the user these rewards!
+  req.player.gold += Math.ceil(rewards.gold * factor);
+  req.player.exp += Math.ceil(rewards.exp * factor);
+
+  while(req.player.exp >=  expToLevel(req.player.level + 1)) {
+    req.player.exp -= expToLevel(req.player.level + 1);
+    req.player.level++;
+    req.player.stat_points += req.player.profession === 'Wanderer' ? 1 : 2;
+    req.player.hp = maxHp(req.player.constitution, req.player.level);
+    req.player.vigor = maxVigor(req.player.constitution, req.player.level);
+  }
+
+  // delete the tracking for this dungeon-run
+  await completeDungeon(req.player.id);
+  await updatePlayer(req.player);
+
+  res.send(dungeonRewards(dungeon, rewards, completionsToday.length));
+});
index df70a3882f63eb12f3d110b0964fd01004348be6..45b008470c293dcc26d897d8ccf3287f4e71f371 100644 (file)
@@ -4,10 +4,11 @@ import type { Travel, TravelWithNames } from '../shared/travel';
 import { db } from './lib/db';
 import { random } from 'lodash';
 
 import { db } from './lib/db';
 import { random } from 'lodash';
 
-export async function getAllServices(city_id: number): Promise<Location[]> {
+export async function getAllServices(city_id: number, min_level: number): Promise<Location[]> {
   return db.select('*')
             .from<Location>('locations')
             .where({city_id})
   return db.select('*')
             .from<Location>('locations')
             .where({city_id})
+            .andWhere('min_level', '<=', min_level)
             .orderBy('type')
             .orderBy('display_order');
 }
             .orderBy('type')
             .orderBy('display_order');
 }
@@ -19,6 +20,13 @@ export async function getService(location_id: number): Promise<LocationWithCity>
   }).first();
 }
 
   }).first();
 }
 
+export async function getDungeon(dungeon_id: string): Promise<LocationWithCity> {
+  return db.select(['locations.*', 'cities.name as city_name']).
+          from<Location>('locations').join('cities', 'locations.city_id', '=', 'cities.id').where({
+    'locations.event_name': dungeon_id
+  }).first();
+}
+
 export async function getAllPaths(city_id: number): Promise<Path[]> {
   const res = await db.raw(`
                 select 
 export async function getAllPaths(city_id: number): Promise<Path[]> {
   const res = await db.raw(`
                 select 
@@ -82,7 +90,6 @@ export async function clearTravelPlan(player_id: string): Promise<Travel> {
 export async function completeTravel(player_id: string): Promise<Travel> {
   const rows = await db('travel').where({player_id}).delete().returning('*');
   if(rows.length !== 1) {
 export async function completeTravel(player_id: string): Promise<Travel> {
   const rows = await db('travel').where({player_id}).delete().returning('*');
   if(rows.length !== 1) {
-    console.log(rows);
     throw new Error('Unexpected response when moving');
   }
 
     throw new Error('Unexpected response when moving');
   }
 
index a9d077c23c68b474b0355cb0ff23ec76f9a55fbf..b9579698ab9fc9fa6a0eade540d0fd22886c4f4e 100644 (file)
@@ -31,6 +31,32 @@ export async function loadPlayer(authToken: string): Promise<Player> {
   return res;
 }
 
   return res;
 }
 
+export async function findPlayerByUsername(username: string): Promise<Player> {
+  const res = await db.first()
+            .select(
+              'players.*',
+              'profession_levels.level',
+              'profession_levels.exp',
+              db.raw(`coalesce(pp.permissions, '[]'::json) as permissions`)
+            )
+            .from<Player>('players')
+            .join('profession_levels', function() {
+              this.on(function() {
+                this.on('profession_levels.player_id', '=', 'players.id')
+                this.andOn('profession_levels.profession', '=', 'players.profession')
+              })
+            })
+            .leftJoin(
+              db.raw(`(select json_agg(pp.permission) as permissions, pp.player_id from player_permissions pp group by pp.player_id) pp`),
+              'pp.player_id','=', 'players.id'
+            )
+            .where({
+              'players.username': username
+            });
+
+  return res;
+}
+
 export async function createPlayer(): Promise<Player> {
   const raw: Partial<Player> = {
     username: `Player${Date.now().toString().substr(-7)}`,
 export async function createPlayer(): Promise<Player> {
   const raw: Partial<Player> = {
     username: `Player${Date.now().toString().substr(-7)}`,
diff --git a/src/server/views/components/explore-pane.ts b/src/server/views/components/explore-pane.ts
new file mode 100644 (file)
index 0000000..581677b
--- /dev/null
@@ -0,0 +1,5 @@
+export function ExplorePane(townId: number, contents: string): string {
+  return `<section id="explore" class="tab active" style="background-image: url('/assets/img/map/${townId}.jpeg')" hx-swap-oob="true">
+  ${contents}
+</section>`;
+}
diff --git a/src/server/views/dungeons/room.ts b/src/server/views/dungeons/room.ts
new file mode 100644 (file)
index 0000000..a4e3552
--- /dev/null
@@ -0,0 +1,71 @@
+import { DUNGEON_TRAVEL_BLOCK } from '../../../shared/constants';
+import { Dungeon, DungeonPlayer, DungeonRewards, DungeonRoom, DungeonState, DungeonStateSummaryVists } from '../../../shared/dungeon';
+import { Button, ButtonWithBlock } from '../components/button';
+import { Details, Title } from '../components/city';
+
+function renderMovementButtons(room: DungeonRoom): string { 
+  let html: string[] = [];
+  const now = Date.now();
+
+  room.exits.forEach(exit => {
+    html.push(ButtonWithBlock({
+      id: `target-${exit.target_room_id}`,
+      'hx-post': `/city/dungeon/step/${exit.target_room_id}`,
+      'hx-target': '#explore'
+    }, exit.name, now + DUNGEON_TRAVEL_BLOCK));
+  });
+
+  if(room.settings?.end) {
+    html.push(Button({
+      id: `target-exit`,
+      'hx-post': `/city/dungeon/${room.dungeon_id}/complete`,
+      'hx-target': '#modal-wrapper'
+    }, 'Complete Dungeon'));
+  }
+
+  return html.join("\n");
+}
+
+export function renderDungeonRoom(room: DungeonRoom, state: DungeonStateSummaryVists): string {
+  // first visit for this room?
+  let desc = room.description;
+  room.settings?.visit?.forEach(visit => {
+    // This ssets the stage for repeat events. 
+    // eventually the description might change if you visit 
+    // a spot MROE than 5 or 6 times
+    if(state[room.id]) {
+      if(visit?.visit_number === state[room.id]) {
+        desc = visit.description;
+      }
+    }
+  });
+
+  let html = `<div class="dungeon-room-description">${desc}</div><div class="dungeon-room-actions">${renderMovementButtons(room)}</div>`;
+
+  return html;
+
+}
+
+export function renderDungeon(city: string, dungeon_name: string, room: DungeonRoom, state: DungeonStateSummaryVists): string { 
+  return`
+  ${Title(city)}
+  ${Details(dungeon_name, renderDungeonRoom(room, state))}
+`;
+}
+
+export function dungeonRewards(dungeon: Dungeon, rewards: DungeonRewards, completions: number): string {
+  return `
+<dialog>
+  <div class="dungeon-rewards">
+    <p>Congratulations on completing the ${dungeon.name} Dungeon!</p>
+    <p>${completions <= 5 ? `${completions}/5 Runs` : `You've exhausted your runs!`}</p>
+    <b>Rewards:</b><br>
+    <b>Exp:</b> ${rewards.exp.toLocaleString()}<br>
+    <b>Gold:</b> ${rewards.gold.toLocaleString()}<br>
+  </div>
+  <div class="actions">
+    <button nav-trigger="#player-info nav a|3" formmethod="dialog" value="cancel" class="green">Exit</button>
+  </div>
+</dialog>
+  `;
+}
index c66cdbd87b2c40415a9ccb958940891457ac5190..46d962b09ec8dd83ce0f25773e50bf5bd881cd09 100644 (file)
@@ -1,7 +1,9 @@
+import { Dungeon } from "shared/dungeon";
 import { FightRound } from "shared/fight";
 import { LocationWithCity } from "shared/map";
 import { Fight, MonsterForFight } from "../../shared/monsters";
 import { Button, ButtonWithBlock } from "./components/button";
 import { FightRound } from "shared/fight";
 import { LocationWithCity } from "shared/map";
 import { Fight, MonsterForFight } from "../../shared/monsters";
 import { Button, ButtonWithBlock } from "./components/button";
+import { Details, Title } from "./components/city";
 
 export function renderRoundDetails(roundData: FightRound): string {
   let html: string[] = roundData.roundDetails.map(d => `<div>${d}</div>`);
 
 export function renderRoundDetails(roundData: FightRound): string {
   let html: string[] = roundData.roundDetails.map(d => `<div>${d}</div>`);
@@ -91,14 +93,21 @@ export function renderFight(monster: Fight, results: string = '', displayFightAc
         <button type="submit" class="fight-action" name="action" value="flee">Flee</button>
       </form>
       `: ''}
         <button type="submit" class="fight-action" name="action" value="flee">Flee</button>
       </form>
       `: ''}
-      </div>
+    </div>
     <div id="fight-results">${results}</div>
   </div>
     <div id="fight-results">${results}</div>
   </div>
-</div>`;
+`;
 
   return html;
 }
 
 
   return html;
 }
 
+export function renderFightPreRoundDungeon(city: string, dungeon_name: string, monster: Fight): string {
+  return`
+  ${Title(city)}
+  ${Details(dungeon_name, renderFight(monster, '', true))}
+`;
+}
+
 export function renderFightPreRound(monster: Fight,  displayFightActions: boolean = true, location: LocationWithCity, closestTown: number) {
   let html = `
 <section id="explore" class="tab active" style="background-image: url('/assets/img/map/${closestTown}.jpeg')" hx-swap-oob="true">
 export function renderFightPreRound(monster: Fight,  displayFightActions: boolean = true, location: LocationWithCity, closestTown: number) {
   let html = `
 <section id="explore" class="tab active" style="background-image: url('/assets/img/map/${closestTown}.jpeg')" hx-swap-oob="true">
index e432ebd31a0e49b6f8ab29ed2bb05de6d79242fc..0b6f2c255d7bb4175aef53c3281241a72c47cfe2 100644 (file)
@@ -9,7 +9,8 @@ export async function renderMap(data: { city: City, locations: Location[], paths
   const servicesParsed: Record<LocationType, string[]> = {
     'SERVICES': [],
     'STORES': [],
   const servicesParsed: Record<LocationType, string[]> = {
     'SERVICES': [],
     'STORES': [],
-    'EXPLORE': []
+    'EXPLORE': [],
+    'DUNGEON': []
   };
 
   data.locations.forEach(l => {
   };
 
   data.locations.forEach(l => {
@@ -28,7 +29,10 @@ export async function renderMap(data: { city: City, locations: Location[], paths
     html += `<div><h3>Stores</h3>${servicesParsed.STORES.join("<br>")}</div>`
   }
   if(servicesParsed.EXPLORE.length) {
     html += `<div><h3>Stores</h3>${servicesParsed.STORES.join("<br>")}</div>`
   }
   if(servicesParsed.EXPLORE.length) {
-    html += `<div><h3>Explore</h3>${servicesParsed.EXPLORE.join("<br>")}</div>`
+    html += `
+    <div><h3>Explore</h3>${servicesParsed.EXPLORE.join("<br>")}
+    ${servicesParsed.DUNGEON.length ? `<br>${servicesParsed.DUNGEON.join("<br>")}` : ''}
+    </div>`;
   }
   html += `
     <div>
   }
   html += `
     <div>
index 4cda940e25cf779c4762a11e10245d94f99cee56..7804b5662af372d567c54a93b6143648a1d02e5b 100644 (file)
@@ -3,3 +3,8 @@ export const STEP_DELAY = 2000;
 export const ALERT_DISPLAY_LENGTH = 3000;
 // this is displayed as a percentage out of 100
 export const CHANCE_TO_FIGHT_SPECIAL = 10;
 export const ALERT_DISPLAY_LENGTH = 3000;
 // this is displayed as a percentage out of 100
 export const CHANCE_TO_FIGHT_SPECIAL = 10;
+
+export const DUNGEON_TRAVEL_BLOCK = 3000;
+
+export const EVENT_FLUSH_INTERVAL = 10000;
+export const EVENT_SECOND_BUCKET = 3;
diff --git a/src/shared/dungeon.ts b/src/shared/dungeon.ts
new file mode 100644 (file)
index 0000000..e345523
--- /dev/null
@@ -0,0 +1,73 @@
+export type Dungeon = {
+  id: string;
+  name: string;
+  starting_room: string;
+}
+
+export type RoomExit = {
+  name: string;
+  target_room_id: string
+}
+
+export type RoomSettings = {
+  visit?: {
+    visit_number: number;
+    description?: string;
+  }[],
+  fight?: {
+    monster_id: number;
+    one_time: boolean;
+  },
+  end?: boolean;
+  rewards?: {
+    base: {
+      exp?: number,
+      gold?: number
+    },
+    per_kill_bonus?: {
+      exp?: number,
+      gold?: number
+    }
+  }
+}
+
+export type DungeonRoom = {
+  id: string;
+  dungeon_id: string;
+  description: string;
+  exits: RoomExit[];
+  settings: RoomSettings;
+}
+
+export type DungeonThing = {
+  room_id: string;
+  id: number;
+  name: string;
+  type: string;
+  properties: Record<string, any>;
+}
+
+export type DungeonState = {
+  player_id: string;
+  dungeon_id: string;
+  event_name: 'ROOM_VISIT' | 'FIGHT_COMPLETE';
+  event_props: any;
+  create_date: number;
+}
+
+export type DungeonPlayer = {
+  player_id: string;
+  dungeon_id: string;
+  current_room_id: string;
+  target_room_id: string;
+}
+
+// <room_id, total visit count>
+export type DungeonStateSummaryVists = Record<string, number>;
+// <room_id, <monster_id, count>>
+export type DungeonStateSummaryFights = Record<string, Record<string, number>>;
+
+export type DungeonRewards = {
+  exp: number;
+  gold: number;
+}
diff --git a/src/shared/event.ts b/src/shared/event.ts
new file mode 100644 (file)
index 0000000..bb2bec2
--- /dev/null
@@ -0,0 +1,23 @@
+type UUID = string;
+
+export type EventName = 'DUNGEON_COMPLETE'
+| 'MONSTER_KILLED'
+| 'PLAYER_KILLED'
+| 'LEVEL_UP'
+| 'LOGIN'
+;
+
+export type Event<T> = {
+  id: UUID;
+  event_name: EventName;
+  player_id: UUID;
+  app_version: string;
+  value: number;
+  props: T
+  created: Date;
+}
+
+export type CreatedEvent = Omit<Event<any>, 'id'|'created'|'value'> & {
+  created?: Date;
+  value?: number;
+}
index c05ae8fe01adef5c8c9c8b2234faba223c25e9f1..b4267dbe57d251853639cdd578844cb761c11d4c 100644 (file)
@@ -5,7 +5,7 @@ export type City = {
   name: string;
 }
 
   name: string;
 }
 
-export type LocationType = 'SERVICES' | 'STORES' | 'EXPLORE';
+export type LocationType = 'SERVICES' | 'STORES' | 'EXPLORE' | 'DUNGEON';
 
 export type Location = {
   id: number;
 
 export type Location = {
   id: number;
index c7f8700c86560a60baad62fb2f8ad1b19970de86..854eee5c3993279e55ba962224d001b0bffebe3d 100644 (file)
@@ -25,7 +25,7 @@ export type MonsterForList = {
   level: number;
 }
 
   level: number;
 }
 
-export type FightTrigger = 'explore' | 'travel';
+export type FightTrigger = 'explore' | 'travel' | 'dungeon-forced';
 
 export type Fight = Omit<Monster, 'id' | 'faction_id' | 'location_id' | 'minLevel' | 'maxLevel' | 'time_period'> & { 
   id: string;
 
 export type Fight = Omit<Monster, 'id' | 'faction_id' | 'location_id' | 'minLevel' | 'maxLevel' | 'time_period'> & { 
   id: string;
index 93d75f3bdb8ed3816ae3803f1e095ac07879f50c..339396dce76a04bc2a36dfadb2d869d689eee287 100644 (file)
@@ -3,7 +3,13 @@ import { Stat } from './stats';
 import { SkillDefinition, Skill } from './skills';
 import { EquippedItemDetails } from './equipped';
 
 import { SkillDefinition, Skill } from './skills';
 import { EquippedItemDetails } from './equipped';
 
-export type Permission = 'admin' | 'moderator';
+export function PermissionGuard(request: any): boolean {
+  const perms: Permission[] = ['admin', 'moderator', 'tester', 'archaeologist'];
+
+  return perms.includes(request);
+}
+
+export type Permission = 'admin' | 'moderator' | 'tester' | 'archaeologist';
 
 export type Player = {
   id: string,
 
 export type Player = {
   id: string,