A Typescript server emulator for Box Critters, a defunct virtual world.

Initial commit

Index 842bb7cb

+1
.env.example
···
+
JWT_TOKEN=
+6
.gitattributes
···
+
# Auto detect text files and perform LF normalization
+
* text=auto
+
public/** -linguist-vendored
+
public/* -linguist-vendored
+
public/** linguist-vendored
+
public/* linguist-vendored
+5
.gitignore
···
+
.env
+
.DS_Store
+
Thumbs.db
+
accounts.json
+
/node_modules
+15
Dockerfile
···
+
FROM denoland/deno:2.1.5
+
+
WORKDIR /app
+
+
COPY deno.json .
+
+
RUN deno install
+
+
COPY . .
+
RUN deno cache main.ts
+
+
ARG PORT=3257
+
EXPOSE $PORT
+
+
CMD ["task", "start"]
+189
Events.md
···
+
# Event Recreation Progress
+
+
A checklist for each party of which rooms are finished. Not all parties will be possible due to them not being archived sadly. This list ignores tedious animations which can be done at a later date.
+
+
## Default (no party)
+
+
- [x] Tavern
+
- [x] Crash Site
+
- [x] Cellar
+
- [x] Port
+
- [ ] Jungle
+
+
## 2019
+
+
### Easter
+
+
- [x] Tavern
+
+
### Halloween
+
+
- [ ] Tavern
+
- [ ] **Halloween Maze Entrance**
+
- [ ] **Halloween Maze**
+
- [ ] **Halloween Maze End**
+
+
## Battle Bears
+
+
This event had no significant changes. The Crash Site room became a default room, and is classified as that.
+
+
### Christmas
+
+
- [x] Tavern
+
- [x] **Snowman Village**
+
- [ ] Forest
+
+
## 2020
+
+
### Saint Patrick's Day
+
+
- [x] Tavern
+
- [x] **Leprechaun Village**
+
- [ ] Forest
+
+
### Easter
+
+
- [x] Tavern
+
- [x] Cellar
+
+
### Graduation
+
+
- [ ] Tavern
+
- [x] Cellar
+
- [ ] Port
+
+
### Summer
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
- [ ] Shack
+
+
### Space
+
+
- [x] Tavern
+
- [x] Port
+
- [x] Shack
+
- [x] **Solar System** *(The background and foreground that were archived are corrupted)*
+
+
### Halloween
+
+
- [x] Tavern
+
- [x] Cellar
+
- [x] Port
+
- [ ] Jungle
+
- [x] **Halloween Maze Entrance**
+
- [x] **Halloween Maze**
+
- [x] **Halloween Maze End**
+
+
### Club Penguin Celebration
+
+
- [x] Port (practically the same as `Halloween 2020`'s Port, just with a party hat on the outside of the `Shack`)
+
- [x] Shack
+
+
### CritterCon
+
+
- [ ] Port
+
- [ ] **Critter Con Hall**
+
+
### Holidays
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
- [ ] Shack
+
- [ ] Jungle
+
- [ ] **Cliff**
+
- [ ] **Snowman Village**
+
- [ ] **Forest**
+
+
## 2021
+
+
### Graduation
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
- [ ] Jungle
+
+
### Sports
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
- [ ] Shack
+
- [ ] Jungle
+
+
### New Years
+
+
- [ ] Port
+
*The wiki only includes a screenshot of the Port, and no other rooms.*
+
+
### 2nd Anniversary
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
+
### Saint Patrick's Day
+
+
- [ ] Tavern
+
- [ ] Port
+
- [ ] Jungle
+
- [ ] **Lava Lair**
+
+
### April Fools' Day
+
+
- [x] Tavern
+
- [x] Cellar
+
- [x] Port
+
- [x] Shack
+
- [x] Jungle
+
- [x] **Box Realm (work in progress)** *(the background is the only file found so far)*
+
+
### Easter
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
- [ ] Shack
+
+
### Sand Sculpture
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Bridge
+
- [ ] Port
+
- [ ] Shack
+
- [ ] Jungle
+
+
### Halloween
+
+
- [x] Tavern
+
- [ ] Crash Site
+
- [x] Cellar
+
- [ ] Port
+
- [x] Jungle
+
- [ ] **Halloween Maze Entrance**
+
- [ ] **Halloween Maze**
+
- [ ] **Halloween Maze End**
+
- [x] **Neighborhood**
+
- [x] **Crypt**
+
+
### Club Penguin Celebration
+
+
- [x] Port (practically the same as `Halloween 2021`'s Port, just with a party hat on the outside of the `Shack`)
+
- [x] Shack
+
+
### CritterCon
+
+
- [ ] Port
+
- [ ] Critter Con Hall
+
+
### Holidays
+
+
- [ ] Tavern
+
- [ ] Cellar
+
- [ ] Port
+
- [ ] Jungle
+
- [ ] Shack
+40
README.md
···
+
# Box Critters Recritted
+
+
Reopening the dusty box of the world of Box Critters! This is a Typescript server emulator using the Bun & Deno frameworks.
+
+
## Party Switcher
+
+
A custom party switcher has been implemented, you can change the party on the log-in page, or using the `/party [ID]` command in-game. For a breakdown of party room recreation progress, go [here](Events.md).
+
+
## Development
+
+
> Installation
+
```bash
+
deno install
+
```
+
+
> Serving
+
```bash
+
deno run start
+
> Listening on http://localhost:3257/
+
```
+
+
## APIs
+
+
The game has 4 APIs:
+
+
### (GET) `/api/server/players`
+
+
This API returns information on the player(s) in-game, if any.
+
+
### (GET) `/api/server/rooms`
+
+
This API returns almost-identical information as the `/api/client/rooms` API, however it returns information on all hashes of all rooms with no required party ID URL parameter.
+
+
### (POST) `/api/client/login`
+
+
This API takes in all the information provided by the user on log-in and generates a JWT for that session.
+
+
### (GET) `/api/client/rooms?partyId=`
+
+
This API returns information on the parties the game supports, and depending on the party ID provided in the URL, information for each room that party changes in some way. If the party does not change the room in any way, the default version of that room will be returned. All the data is gathered by the server reading the `/public/media/rooms/` directory of the game - and cached for future requests.
bun.lockb

This is a binary file and will not be displayed.

+10
chalk.d.ts
···
+
/*
+
Typescript doesn't seem to recognize the functions of the chalk_deno module, so some are specified here to avoid type warnings.
+
*/
+
+
declare module "https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js" {
+
export function red(text: string): string;
+
export function green(text: string): string;
+
export function blue(text: string): string;
+
export function gray(text: string): string;
+
}
+140
constants/items.ts
···
+
export const shop = {
+
lastItem: { itemId: "beard2_brown", cost: 20 },
+
freeItem: { itemId: "goggles_pink", cost: 0},
+
nextItem: { itemId: "scout_uniform", cost: 0},
+
collection: [
+
{ itemId: "santa_hat", cost: 100 }
+
]
+
}
+
+
export const codes = {
+
rocketsnail: ["viking", "rocket_red"],
+
andybulletin: "propeller",
+
cute: "toque_pink",
+
oommgames: "space_red",
+
boxcritters3d: ["3d_white", "3d_black"],
+
goodnight: "sleeping",
+
madeincanada: "toque_white",
+
thekeeper: "party_green",
+
bunnyhug: "hoodie_blue",
+
greenplumber: "ballcap_green",
+
duckhunter: "float_pink",
+
piratepack: "pirate_patch",
+
pickle: "pickle",
+
oscarproductions: "guitar_blue",
+
livestream: "headphones_black",
+
creative: "headphones_black",
+
fun: "propeller_pink",
+
marco: "keytar_red",
+
snowball: "hoodie_white",
+
critbits: "ballcap_pink",
+
esporte: "kit_yellow",
+
squeeze: "hoodie_orange",
+
imagination: "box_brown",
+
sparkle: "propeller_silver",
+
adventure: "snorkel_blue",
+
tamago: "sun_square",
+
redcross: "australian_hat",
+
discordcritterspt: "headphones_green",
+
scarletraven: "wings_black",
+
boxcritterswiki: "paperhat_colour",
+
wikicritters: "brain_green",
+
boxcrittersguild: "cardboard_sword_silver",
+
staysafe: "doctor_mask_blue",
+
glitter: "pirate_capt_pink"
+
}
+
+
export const throwback = [
+
"goggles_black",
+
"pot",
+
"sombrero_yellow",
+
"cone",
+
"toque_purple",
+
"sun_orange",
+
"pirate_capt_black",
+
"lifejacket_red",
+
"ballcap_black",
+
"super_cape_red",
+
"tshirt_white",
+
"lei_red",
+
"ballcap_blue",
+
"viking_blue",
+
"overalls_orange",
+
"bunny_blue",
+
"messenger_brown",
+
"plaid_black",
+
"tinfoil_hat",
+
"monk",
+
"pirate_hat_black",
+
"hawaii_orange",
+
"space_black",
+
"traffic_cone",
+
"grass_yellow",
+
"ushanka",
+
"bandana_purple",
+
"ski_suit_blue",
+
"hotdog",
+
"space_blue",
+
"sweater_orange",
+
"tactical_headset",
+
"ballcap_yellow",
+
"ringmaster_suit_red",
+
"flight_helmet_red",
+
"rainhat_yellow",
+
"raincoat_yellow",
+
"ballcap_red",
+
"ballerina_pink",
+
"hawaii_blue",
+
"stripe_red_white",
+
"grass_green",
+
"plaid_blue",
+
"toque_orange",
+
"school_pack_orange",
+
"pirate_crew_blue",
+
"ringmaster_hat_black",
+
"super_mask_black",
+
"pirate_hat_pink",
+
"rainhat_red",
+
"tophat_black",
+
"scarf_red",
+
"skeleton_body",
+
"dracula_cloak",
+
"pumpkin",
+
"hotdog",
+
"beard3_black",
+
"scarf_purple",
+
"puffy_red",
+
"blockhead",
+
"hoodie_purple",
+
"winter_dress_red",
+
"bulb_blue",
+
"tacky_red",
+
"pirate_bandana_red"
+
]
+
+
export const eventCodes = {
+
christmas2019: {
+
jinglebells: "elf_hat_green",
+
winter: "winter_dress",
+
joy: "bulb_yellow",
+
rudolph: "reindeer_head",
+
elf: "elf_suit_green",
+
cheer: "bulb_green",
+
glow: "bulb_red",
+
rednose: "reindeer_body",
+
blizzard: "wizard_blizzard",
+
family: "ornament_red",
+
shine: "angel_halo",
+
peace: "angel_wings",
+
jolly: "santa_beard",
+
merry: "santa_hat",
+
christmas: "santa_suit",
+
boxingday: "onsie_plaid_red",
+
warm: "sleeping_red",
+
tacky: "tacky_green",
+
snow: "goggles_white",
+
beautiful: "winter_dress_red",
+
newyear: "tuxedo_black",
+
"2020": "tophat_black"
+
}
+
}
+14
constants/parties.json
···
+
[
+
"default",
+
"easter2019",
+
"christmas2019",
+
"lucky2020",
+
"easter2020",
+
"space",
+
"grad2020",
+
"summer2020",
+
"halloween2019",
+
"halloween2020",
+
"fools2021",
+
"halloween2021"
+
]
+90
constants/world.ts
···
+
import { PlayerCrumb, Room } from "../types.ts"
+
import { indexRoomData } from "../utils.ts";
+
+
export const rooms: Record<string, Record<string, Room>> = await indexRoomData()
+
export const spawnRoom = "tavern";
+
+
export const players: Record<string, PlayerCrumb> = {
+
"0": {
+
"i": "0",
+
"n": "Huggable",
+
"c": "huggable",
+
"x": 1670,
+
"y": 323,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
"_roomId": "crash_site"
+
}
+
}
+
export const queue: Array<string> = []
+
+
export const roomExits = {
+
"cellar->tavern": { x: 360, y: 410, r: 0 },
+
"crash_site->cellar": { x: 615, y: 400, r: 0 },
+
"shack->port": { x: 550, y: 235, r: 0 },
+
"jungle->port": { x: 650, y: 230, r: 0 },
+
"snowman_village->tavern": { x: 563, y: 368, r: 0 }
+
}
+
+
// deno-lint-ignore no-explicit-any
+
export const npcs: { [key: string]: any } = {
+
snowman_village: [
+
{
+
"i": "NPC0",
+
"n": "Snow Girl",
+
"c": "snowgirl",
+
"x": 1289,
+
"y": 228,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": ""
+
},
+
{
+
"i": "NPC1",
+
"n": "Snow Patrol",
+
"c": "snow_patrol",
+
"x": 1644,
+
"y": 221,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": ""
+
},
+
{
+
"i": "NPC2",
+
"n": "Snow Greeter",
+
"c": "snow_greeter",
+
"x": 443,
+
"y": 317,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": ""
+
},
+
{
+
"i": "NPC3",
+
"n": "Snow Grandma",
+
"c": "snowgrandma",
+
"x": 1938,
+
"y": 251,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": ""
+
},
+
{
+
"i": "NPC4",
+
"n": "Snow Keeper",
+
"c": "snowkeeper",
+
"x": 893,
+
"y": 216,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": ""
+
}
+
]
+
}
+12
deno.json
···
+
{
+
"imports": {
+
"hono": "jsr:@hono/hono@^4.6.11"
+
},
+
"tasks": {
+
"start": "deno run --watch --allow-net --allow-read --allow-env --env-file=.env --allow-write main.ts"
+
},
+
"compilerOptions": {
+
"jsx": "precompile",
+
"jsxImportSource": "hono/jsx"
+
}
+
}
+647
deno.lock
···
+
{
+
"version": "4",
+
"specifiers": {
+
"jsr:@hono/hono@^4.6.11": "4.6.11",
+
"jsr:@std/encoding@*": "1.0.5",
+
"npm:fs@^0.0.1-security": "0.0.1-security",
+
"npm:http@^0.0.1-security": "0.0.1-security",
+
"npm:nodemon@^3.1.7": "3.1.9",
+
"npm:path@~0.12.7": "0.12.7",
+
"npm:socket.io@^4.8.1": "4.8.1",
+
"npm:zod@^3.24.1": "3.24.2"
+
},
+
"jsr": {
+
"@hono/hono@4.6.11": {
+
"integrity": "07399d911f09e94b7dc1e0e0a0577d35fe66578af20163d513958364c4e9e702"
+
},
+
"@std/encoding@1.0.5": {
+
"integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04"
+
}
+
},
+
"npm": {
+
"@socket.io/component-emitter@3.1.2": {
+
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
+
},
+
"@types/cors@2.8.17": {
+
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
+
"dependencies": [
+
"@types/node@22.12.0"
+
]
+
},
+
"@types/node@22.12.0": {
+
"integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
+
"dependencies": [
+
"undici-types"
+
]
+
},
+
"@types/node@22.13.8": {
+
"integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==",
+
"dependencies": [
+
"undici-types"
+
]
+
},
+
"accepts@1.3.8": {
+
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+
"dependencies": [
+
"mime-types",
+
"negotiator"
+
]
+
},
+
"anymatch@3.1.3": {
+
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+
"dependencies": [
+
"normalize-path",
+
"picomatch"
+
]
+
},
+
"balanced-match@1.0.2": {
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+
},
+
"base64id@2.0.0": {
+
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
+
},
+
"binary-extensions@2.3.0": {
+
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="
+
},
+
"brace-expansion@1.1.11": {
+
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+
"dependencies": [
+
"balanced-match",
+
"concat-map"
+
]
+
},
+
"braces@3.0.3": {
+
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+
"dependencies": [
+
"fill-range"
+
]
+
},
+
"chokidar@3.6.0": {
+
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+
"dependencies": [
+
"anymatch",
+
"braces",
+
"fsevents",
+
"glob-parent",
+
"is-binary-path",
+
"is-glob",
+
"normalize-path",
+
"readdirp"
+
]
+
},
+
"concat-map@0.0.1": {
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+
},
+
"cookie@0.7.2": {
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="
+
},
+
"cors@2.8.5": {
+
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+
"dependencies": [
+
"object-assign",
+
"vary"
+
]
+
},
+
"debug@4.3.7": {
+
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+
"dependencies": [
+
"ms"
+
]
+
},
+
"debug@4.4.0": {
+
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+
"dependencies": [
+
"ms"
+
]
+
},
+
"engine.io-parser@5.2.3": {
+
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="
+
},
+
"engine.io@6.6.4": {
+
"integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
+
"dependencies": [
+
"@types/cors",
+
"@types/node@22.13.8",
+
"accepts",
+
"base64id",
+
"cookie",
+
"cors",
+
"debug@4.3.7",
+
"engine.io-parser",
+
"ws"
+
]
+
},
+
"fill-range@7.1.1": {
+
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+
"dependencies": [
+
"to-regex-range"
+
]
+
},
+
"fs@0.0.1-security": {
+
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
+
},
+
"fsevents@2.3.3": {
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
+
},
+
"glob-parent@5.1.2": {
+
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+
"dependencies": [
+
"is-glob"
+
]
+
},
+
"has-flag@3.0.0": {
+
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+
},
+
"http@0.0.1-security": {
+
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
+
},
+
"ignore-by-default@1.0.1": {
+
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
+
},
+
"inherits@2.0.3": {
+
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
+
},
+
"is-binary-path@2.1.0": {
+
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+
"dependencies": [
+
"binary-extensions"
+
]
+
},
+
"is-extglob@2.1.1": {
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+
},
+
"is-glob@4.0.3": {
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+
"dependencies": [
+
"is-extglob"
+
]
+
},
+
"is-number@7.0.0": {
+
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+
},
+
"mime-db@1.52.0": {
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+
},
+
"mime-types@2.1.35": {
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+
"dependencies": [
+
"mime-db"
+
]
+
},
+
"minimatch@3.1.2": {
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+
"dependencies": [
+
"brace-expansion"
+
]
+
},
+
"ms@2.1.3": {
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+
},
+
"negotiator@0.6.3": {
+
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+
},
+
"nodemon@3.1.9": {
+
"integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==",
+
"dependencies": [
+
"chokidar",
+
"debug@4.4.0",
+
"ignore-by-default",
+
"minimatch",
+
"pstree.remy",
+
"semver",
+
"simple-update-notifier",
+
"supports-color",
+
"touch",
+
"undefsafe"
+
]
+
},
+
"normalize-path@3.0.0": {
+
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+
},
+
"object-assign@4.1.1": {
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+
},
+
"path@0.12.7": {
+
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
+
"dependencies": [
+
"process",
+
"util"
+
]
+
},
+
"picomatch@2.3.1": {
+
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+
},
+
"process@0.11.10": {
+
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
+
},
+
"pstree.remy@1.1.8": {
+
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
+
},
+
"readdirp@3.6.0": {
+
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+
"dependencies": [
+
"picomatch"
+
]
+
},
+
"semver@7.7.1": {
+
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="
+
},
+
"simple-update-notifier@2.0.0": {
+
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+
"dependencies": [
+
"semver"
+
]
+
},
+
"socket.io-adapter@2.5.5": {
+
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
+
"dependencies": [
+
"debug@4.3.7",
+
"ws"
+
]
+
},
+
"socket.io-parser@4.2.4": {
+
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+
"dependencies": [
+
"@socket.io/component-emitter",
+
"debug@4.3.7"
+
]
+
},
+
"socket.io@4.8.1": {
+
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
+
"dependencies": [
+
"accepts",
+
"base64id",
+
"cors",
+
"debug@4.3.7",
+
"engine.io",
+
"socket.io-adapter",
+
"socket.io-parser"
+
]
+
},
+
"supports-color@5.5.0": {
+
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+
"dependencies": [
+
"has-flag"
+
]
+
},
+
"to-regex-range@5.0.1": {
+
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+
"dependencies": [
+
"is-number"
+
]
+
},
+
"touch@3.1.1": {
+
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA=="
+
},
+
"undefsafe@2.0.5": {
+
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
+
},
+
"undici-types@6.20.0": {
+
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
+
},
+
"util@0.10.4": {
+
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+
"dependencies": [
+
"inherits"
+
]
+
},
+
"vary@1.1.2": {
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
+
},
+
"ws@8.17.1": {
+
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="
+
},
+
"zod@3.24.2": {
+
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="
+
}
+
},
+
"redirects": {
+
"https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts"
+
},
+
"remote": {
+
"https://deno.land/std@0.150.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
+
"https://deno.land/std@0.150.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
+
"https://deno.land/std@0.150.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0",
+
"https://deno.land/std@0.150.0/async/debounce.ts": "564273ef242bcfcda19a439132f940db8694173abffc159ea34f07d18fc42620",
+
"https://deno.land/std@0.150.0/async/deferred.ts": "bc18e28108252c9f67dfca2bbc4587c3cbf3aeb6e155f8c864ca8ecff992b98a",
+
"https://deno.land/std@0.150.0/async/delay.ts": "cbbdf1c87d1aed8edc7bae13592fb3e27e3106e0748f089c263390d4f49e5f6c",
+
"https://deno.land/std@0.150.0/async/mod.ts": "9852cd8ed897ab2d41a8fbee611d574e97898327db5c19d9d58e41126473f02c",
+
"https://deno.land/std@0.150.0/async/mux_async_iterator.ts": "5b4aca6781ad0f2e19ccdf1d1a1c092ccd3e00d52050d9c27c772658c8367256",
+
"https://deno.land/std@0.150.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239",
+
"https://deno.land/std@0.150.0/async/tee.ts": "bcfae0017ebb718cf4eef9e2420e8675d91cb1bcc0ed9b668681af6e6caad846",
+
"https://deno.land/std@0.150.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4",
+
"https://deno.land/std@0.150.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a",
+
"https://deno.land/std@0.150.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf",
+
"https://deno.land/std@0.150.0/fmt/colors.ts": "6f9340b7fb8cc25a993a99e5efc56fe81bb5af284ff412129dd06df06f53c0b4",
+
"https://deno.land/std@0.150.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d",
+
"https://deno.land/std@0.150.0/http/server.ts": "0b0a9f3abfcfecead944b31ee9098a0c11a59b0495bf873ee200eb80e7441483",
+
"https://deno.land/std@0.150.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b",
+
"https://deno.land/std@0.150.0/log/handlers.ts": "b88c24df61eaeee8581dbef3622f21aebfd061cd2fda49affc1711c0e54d57da",
+
"https://deno.land/std@0.150.0/log/levels.ts": "82c965b90f763b5313e7595d4ba78d5095a13646d18430ebaf547526131604d1",
+
"https://deno.land/std@0.150.0/log/logger.ts": "4d25581bc02dfbe3ad7e8bb480e1f221793a85be5e056185a0cea134f7a7fdf4",
+
"https://deno.land/std@0.150.0/log/mod.ts": "65d2702785714b8d41061426b5c279f11b3dcbc716f3eb5384372a430af63961",
+
"https://deno.land/std@0.150.0/testing/_diff.ts": "029a00560b0d534bc0046f1bce4bd36b3b41ada3f2a3178c85686eb2ff5f1413",
+
"https://deno.land/std@0.150.0/testing/_format.ts": "0d8dc79eab15b67cdc532826213bbe05bccfd276ca473a50a3fc7bbfb7260642",
+
"https://deno.land/std@0.150.0/testing/_test_suite.ts": "ad453767aeb8c300878a6b7920e20370f4ce92a7b6c8e8a5d1ac2b7c14a09acb",
+
"https://deno.land/std@0.150.0/testing/asserts.ts": "0ee58a557ac764e762c62bb21f00e7d897e3919e71be38b2d574fb441d721005",
+
"https://deno.land/std@0.150.0/testing/bdd.ts": "182bb823e09bd75b76063ecf50722870101b7cfadf97a09fa29127279dc21128",
+
"https://deno.land/std@0.158.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
+
"https://deno.land/std@0.158.0/async/deferred.ts": "c01de44b9192359cebd3fe93273fcebf9e95110bf3360023917da9a2d1489fae",
+
"https://deno.land/std@0.158.0/async/delay.ts": "0419dfc993752849692d1f9647edf13407c7facc3509b099381be99ffbc9d699",
+
"https://deno.land/std@0.158.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4",
+
"https://deno.land/std@0.158.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a",
+
"https://deno.land/std@0.158.0/bytes/mod.ts": "763f97d33051cc3f28af1a688dfe2830841192a9fea0cbaa55f927b49d49d0bf",
+
"https://deno.land/std@0.158.0/io/buffer.ts": "fae02290f52301c4e0188670e730cd902f9307fb732d79c4aa14ebdc82497289",
+
"https://deno.land/std@0.162.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06",
+
"https://deno.land/std@0.162.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0",
+
"https://deno.land/std@0.162.0/async/debounce.ts": "dc8b92d4a4fe7eac32c924f2b8d3e62112530db70cadce27042689d82970b350",
+
"https://deno.land/std@0.162.0/async/deferred.ts": "d8fb253ffde2a056e4889ef7e90f3928f28be9f9294b6505773d33f136aab4e6",
+
"https://deno.land/std@0.162.0/async/delay.ts": "0419dfc993752849692d1f9647edf13407c7facc3509b099381be99ffbc9d699",
+
"https://deno.land/std@0.162.0/async/mod.ts": "dd0a8ed4f3984ffabe2fcca7c9f466b7932d57b1864ffee148a5d5388316db6b",
+
"https://deno.land/std@0.162.0/async/mux_async_iterator.ts": "3447b28a2a582224a3d4d3596bccbba6e85040da3b97ed64012f7decce98d093",
+
"https://deno.land/std@0.162.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239",
+
"https://deno.land/std@0.162.0/async/tee.ts": "9af3a3e7612af75861308b52249e167f5ebc3dcfc8a1a4d45462d96606ee2b70",
+
"https://deno.land/std@0.162.0/http/server.ts": "e99c1bee8a3f6571ee4cdeb2966efad465b8f6fe62bec1bdb59c1f007cc4d155",
+
"https://deno.land/std@0.177.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
+
"https://deno.land/std@0.177.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
+
"https://deno.land/std@0.177.0/bytes/index_of_needle.ts": "65c939607df609374c4415598fa4dad04a2f14c4d98cd15775216f0aaf597f24",
+
"https://deno.land/std@0.177.0/encoding/base64.ts": "7de04c2f8aeeb41453b09b186480be90f2ff357613b988e99fabb91d2eeceba1",
+
"https://deno.land/std@0.177.0/encoding/base64url.ts": "3f1178f6446834457b16bfde8b559c1cd3481727fe384d3385e4a9995dc2d851",
+
"https://deno.land/std@0.177.0/node/_core.ts": "9a58c0ef98ee77e9b8fcc405511d1b37a003a705eb6a9b6e95f75434d8009adc",
+
"https://deno.land/std@0.177.0/node/_utils.ts": "7fd55872a0cf9275e3c080a60e2fa6d45b8de9e956ebcde9053e72a344185884",
+
"https://deno.land/std@0.177.0/node/buffer.ts": "85617be2063eccaf177dbb84c7580d1e32023724ed14bd9df4e453b152a26167",
+
"https://deno.land/std@0.177.0/node/internal/buffer.mjs": "e92303a3cc6d9aaabcd270a937ad9319825d9ba08cb332650944df4562029b27",
+
"https://deno.land/std@0.177.0/node/internal/crypto/_keys.ts": "8f3c3b5a141aa0331a53c205e9338655f1b3b307a08085fd6ff6dda6f7c4190b",
+
"https://deno.land/std@0.177.0/node/internal/crypto/constants.ts": "544d605703053218499b08214f2e25cf4310651d535b7ab995891c4b7a217693",
+
"https://deno.land/std@0.177.0/node/internal/error_codes.ts": "8495e33f448a484518d76fa3d41d34fc20fe03c14b30130ad8e936b0035d4b8b",
+
"https://deno.land/std@0.177.0/node/internal/errors.ts": "1c699b8a3cb93174f697a348c004b1c6d576b66688eac8a48ebb78e65c720aae",
+
"https://deno.land/std@0.177.0/node/internal/hide_stack_frames.ts": "9dd1bad0a6e62a1042ce3a51eb1b1ecee2f246907bff44835f86e8f021de679a",
+
"https://deno.land/std@0.177.0/node/internal/normalize_encoding.mjs": "fd1d9df61c44d7196432f6e8244621468715131d18cc79cd299fc78ac549f707",
+
"https://deno.land/std@0.177.0/node/internal/primordials.mjs": "a72d86b5aa55d3d50b8e916b6a59b7cc0dc5a31da8937114b4a113ad5aa08c74",
+
"https://deno.land/std@0.177.0/node/internal/util.mjs": "f7fe2e1ca5e66f550ad0856b9f5ee4d666f0c071fe212ea7fc7f37cfa81f97a5",
+
"https://deno.land/std@0.177.0/node/internal/util/inspect.mjs": "11d7c9cab514b8e485acc3978c74b837263ff9c08ae4537fa18ad56bae633259",
+
"https://deno.land/std@0.177.0/node/internal/util/types.ts": "0e587b44ec5e017cf228589fc5ce9983b75beece6c39409c34170cfad49d6417",
+
"https://deno.land/std@0.177.0/node/internal/validators.mjs": "e02f2b02dd072a5d623970292588d541204dc82207b4c58985d933a5f4b382e6",
+
"https://deno.land/std@0.177.0/node/internal_binding/_libuv_winerror.ts": "30c9569603d4b97a1f1a034d88a3f74800d5ea1f12fcc3d225c9899d4e1a518b",
+
"https://deno.land/std@0.177.0/node/internal_binding/_node.ts": "cb2389b0eab121df99853eb6a5e3a684e4537e065fb8bf2cca0cbf219ce4e32e",
+
"https://deno.land/std@0.177.0/node/internal_binding/_utils.ts": "7c58a2fbb031a204dee9583ba211cf9c67922112fe77e7f0b3226112469e9fe1",
+
"https://deno.land/std@0.177.0/node/internal_binding/_winerror.ts": "3e8cfdfe22e89f13d2b28529bab35155e6b1730c0221ec5a6fc7077dc037be13",
+
"https://deno.land/std@0.177.0/node/internal_binding/buffer.ts": "31729e0537921d6c730ad0afea44a7e8a0a1044d070ade8368226cb6f7390c8b",
+
"https://deno.land/std@0.177.0/node/internal_binding/constants.ts": "21ff9d1ee71d0a2086541083a7711842fc6ae25e264dbf45c73815aadce06f4c",
+
"https://deno.land/std@0.177.0/node/internal_binding/string_decoder.ts": "54c3c1cbd5a9254881be58bf22637965dc69535483014dab60487e299cb95445",
+
"https://deno.land/std@0.177.0/node/internal_binding/types.ts": "2187595a58d2cf0134f4db6cc2a12bf777f452f52b15b6c3aed73fa072aa5fc3",
+
"https://deno.land/std@0.177.0/node/internal_binding/util.ts": "808ff3b92740284184ab824adfc420e75398c88c8bccf5111f0c24ac18c48f10",
+
"https://deno.land/std@0.177.0/node/internal_binding/uv.ts": "eb0048e30af4db407fb3f95563e30d70efd6187051c033713b0a5b768593a3a3",
+
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
+
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
+
"https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8",
+
"https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
+
"https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c",
+
"https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c",
+
"https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
+
"https://deno.land/std@0.224.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b",
+
"https://deno.land/std@0.224.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf",
+
"https://deno.land/std@0.224.0/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d",
+
"https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
+
"https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3",
+
"https://deno.land/std@0.224.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607",
+
"https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a",
+
"https://deno.land/std@0.224.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883",
+
"https://deno.land/std@0.224.0/path/_interface.ts": "8dfeb930ca4a772c458a8c7bbe1e33216fe91c253411338ad80c5b6fa93ddba0",
+
"https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15",
+
"https://deno.land/std@0.224.0/path/basename.ts": "7ee495c2d1ee516ffff48fb9a93267ba928b5a3486b550be73071bc14f8cc63e",
+
"https://deno.land/std@0.224.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643",
+
"https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36",
+
"https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c",
+
"https://deno.land/std@0.224.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441",
+
"https://deno.land/std@0.224.0/path/format.ts": "6ce1779b0980296cf2bc20d66436b12792102b831fd281ab9eb08fa8a3e6f6ac",
+
"https://deno.land/std@0.224.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069",
+
"https://deno.land/std@0.224.0/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972",
+
"https://deno.land/std@0.224.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7",
+
"https://deno.land/std@0.224.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141",
+
"https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a",
+
"https://deno.land/std@0.224.0/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0",
+
"https://deno.land/std@0.224.0/path/mod.ts": "f6bd79cb08be0e604201bc9de41ac9248582699d1b2ee0ab6bc9190d472cf9cd",
+
"https://deno.land/std@0.224.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352",
+
"https://deno.land/std@0.224.0/path/normalize_glob.ts": "cc89a77a7d3b1d01053b9dcd59462b75482b11e9068ae6c754b5cf5d794b374f",
+
"https://deno.land/std@0.224.0/path/parse.ts": "77ad91dcb235a66c6f504df83087ce2a5471e67d79c402014f6e847389108d5a",
+
"https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d",
+
"https://deno.land/std@0.224.0/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0",
+
"https://deno.land/std@0.224.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
+
"https://deno.land/std@0.224.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1",
+
"https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00",
+
"https://deno.land/std@0.224.0/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2",
+
"https://deno.land/std@0.224.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1",
+
"https://deno.land/std@0.224.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40",
+
"https://deno.land/std@0.224.0/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f",
+
"https://deno.land/std@0.224.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede",
+
"https://deno.land/std@0.224.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
+
"https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63",
+
"https://deno.land/std@0.224.0/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25",
+
"https://deno.land/std@0.224.0/path/posix/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604",
+
"https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91",
+
"https://deno.land/std@0.224.0/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6",
+
"https://deno.land/std@0.224.0/path/posix/parse.ts": "09dfad0cae530f93627202f28c1befa78ea6e751f92f478ca2cc3b56be2cbb6a",
+
"https://deno.land/std@0.224.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c",
+
"https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf",
+
"https://deno.land/std@0.224.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf",
+
"https://deno.land/std@0.224.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0",
+
"https://deno.land/std@0.224.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add",
+
"https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d",
+
"https://deno.land/std@0.224.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b",
+
"https://deno.land/std@0.224.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40",
+
"https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808",
+
"https://deno.land/std@0.224.0/path/windows/basename.ts": "6bbc57bac9df2cec43288c8c5334919418d784243a00bc10de67d392ab36d660",
+
"https://deno.land/std@0.224.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4",
+
"https://deno.land/std@0.224.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5",
+
"https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9",
+
"https://deno.land/std@0.224.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef",
+
"https://deno.land/std@0.224.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6",
+
"https://deno.land/std@0.224.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01",
+
"https://deno.land/std@0.224.0/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8",
+
"https://deno.land/std@0.224.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a",
+
"https://deno.land/std@0.224.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9",
+
"https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf",
+
"https://deno.land/std@0.224.0/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25",
+
"https://deno.land/std@0.224.0/path/windows/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604",
+
"https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780",
+
"https://deno.land/std@0.224.0/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6",
+
"https://deno.land/std@0.224.0/path/windows/parse.ts": "08804327b0484d18ab4d6781742bf374976de662f8642e62a67e93346e759707",
+
"https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7",
+
"https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972",
+
"https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e",
+
"https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c",
+
"https://deno.land/std@0.94.0/node/tty.ts": "9fa7f7b461759774b4eeab00334ac5d25b69bf0de003c02814be01e65150da79",
+
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/ansi-styles/index.js": "7cc96ab93d1c9cfc0746e9dffb40be872e42ee242906f48e68df0d2c9669f737",
+
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/has-flag/index.js": "aed21e4eba656057e7b8c6024305f5354d2ebee2adc857a1d8cd5207923de7e5",
+
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js": "6339123f32f7eb4b17c5c9c926ecdf3dbc353fd4fda7811ad2d3c1d4b98a7420",
+
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/supports-color/index.js": "4d7f2d216b6ac9013d9ec7e004de21f5a7d00bf2be4075bab2d82638d0d41a86",
+
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/templates.js": "f2e12be18cb84710e341e5499528280278052909fa74a12cefc9e2cc26a597ac",
+
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/util.js": "cd08297ec411dcee91826ad01a00d3427235d4548ba605a59e64f0da83af8306",
+
"https://deno.land/x/hono@v3.0.0/adapter/deno/serve-static.ts": "d1c21498ced39849fa0bb23b372bf5d30677916fdbc875902735700ca1e789e3",
+
"https://deno.land/x/hono@v3.0.0/client/client.ts": "c720020a167139dfe8d6af7b91c0c5db38186722b380fb0bb251bae685a2103e",
+
"https://deno.land/x/hono@v3.0.0/client/index.ts": "7ad089b121f2613a0eaedd3d8aa8307a48bf3bf48f6c38281d6e73ee35f0d7ea",
+
"https://deno.land/x/hono@v3.0.0/client/types.ts": "47b83ab3dea4d6ef60a7c5ed11bfdb5113df7ded05403869e029f5317e44c827",
+
"https://deno.land/x/hono@v3.0.0/client/utils.ts": "781ec703f3e685cf02a201cfa717be111820ef89bebd7cb5968c6823310aebaa",
+
"https://deno.land/x/hono@v3.0.0/compose.ts": "9bc97f737857da061b2294eb9812b7e0ca773acc2e913c11bf93d5732c0469f0",
+
"https://deno.land/x/hono@v3.0.0/context.ts": "b053da7ec49c7a32f10572f951ae68347260ffab62fcce76cbc2ce544fe44d23",
+
"https://deno.land/x/hono@v3.0.0/hono.ts": "4929cefc738a4bd5efea2371f0c5338d27e9de91045b1c223ab98eda5a0364d5",
+
"https://deno.land/x/hono@v3.0.0/http-exception.ts": "e74a5e8504ecf795e9babdc2bab9d4052e1806a7396efc89f8e1ed83e813e3be",
+
"https://deno.land/x/hono@v3.0.0/middleware.ts": "d2c886e2f63b91b17f05a11f598ca101912c8fbfd66a25aef345b934ccfcd603",
+
"https://deno.land/x/hono@v3.0.0/middleware/basic-auth/index.ts": "0664ddf00c9f08a5109f92e93264678de55e90c341955c746bafa8f99ecab1eb",
+
"https://deno.land/x/hono@v3.0.0/middleware/bearer-auth/index.ts": "11d4ead9b57f5bcb2b6b4bf27076871f15da0e1e8828b2b79d90c15423357b47",
+
"https://deno.land/x/hono@v3.0.0/middleware/cache/index.ts": "2e86e089ebce01611ab5e231a6fbf7953ebabee3ab68362fcacea752ef48a0ab",
+
"https://deno.land/x/hono@v3.0.0/middleware/compress/index.ts": "0b8ddbd70688361d5ef4e9418afa28affe2500a7f41a231a87873dad26ef5548",
+
"https://deno.land/x/hono@v3.0.0/middleware/cors/index.ts": "10a743dcc793204835a5299070e2afaabb7e81cb742262faa9f1181f5e5d65ac",
+
"https://deno.land/x/hono@v3.0.0/middleware/etag/index.ts": "e679c30ddd2600087521a7c1dac3798c2319c09d203bb627b54357b984fc72e7",
+
"https://deno.land/x/hono@v3.0.0/middleware/html/index.ts": "a5028d8170dcc030d003749e743213e6532ff65798b741b81220207abc9af82d",
+
"https://deno.land/x/hono@v3.0.0/middleware/jsx/index.ts": "1925e4bf01ef1252b9bda6b0c4f79520b138ee319c41df3218cb3bf10c0ed248",
+
"https://deno.land/x/hono@v3.0.0/middleware/jwt/index.ts": "4af4649d9ae8ff2e767e53692d1974c956f523ab47a2560b869083cb493441ca",
+
"https://deno.land/x/hono@v3.0.0/middleware/logger/index.ts": "281b0fe431183a5d7b8d576645370efbd2737aeefaac7dc989d1c90dc03c52c0",
+
"https://deno.land/x/hono@v3.0.0/middleware/powered-by/index.ts": "7ec561885ac0410786f78aeb9789ed7869edb2d43c615cbc3d14f21baee87359",
+
"https://deno.land/x/hono@v3.0.0/middleware/pretty-json/index.ts": "f4a4b2fa2ecb73e23da6f0ef716fe7d6a7f05c69f64dc7f89fc68cbcb204a87f",
+
"https://deno.land/x/hono@v3.0.0/mod.ts": "e771d1c9f711b78f7540134e44a1ceda308fe0af58cb4c83ce7434c56c224670",
+
"https://deno.land/x/hono@v3.0.0/request.ts": "e3b38e76f7d13596266e644c8598d324c56ae73af263c3bf029f6ffeafdb221b",
+
"https://deno.land/x/hono@v3.0.0/router.ts": "21448bc2e6019574c10fae11237da4367037fa107e68bf3d049cd2fd0efd2adb",
+
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/index.ts": "52755829213941756159b7a963097bafde5cc4fc22b13b1c7c9184dc0512d1db",
+
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/node.ts": "8006b5bccb83d9fc98e0562a5545f6dd0be639ce445b089a6171c9c617aa8693",
+
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/router.ts": "35a405c855cf6d10c350a651169c2c728fd19aaf3d3a0c416e5a06752914e6bd",
+
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/trie.ts": "567493b301c44174f0895aedb8d055bbecf88f8a25626fa8ca61333bbd0c882c",
+
"https://deno.land/x/hono@v3.0.0/router/smart-router/index.ts": "74f9b4fe15ea535900f2b9b048581915f12fe94e531dd2b0032f5610e61c3bef",
+
"https://deno.land/x/hono@v3.0.0/router/smart-router/router.ts": "1d54f5c87875d856ed5fc2d22a100e1ff31debe3e9d8e9b1cc18d8e5706239f2",
+
"https://deno.land/x/hono@v3.0.0/router/trie-router/index.ts": "3eb75e7f71ba81801631b30de6b1f5cefb2c7239c03797e2b2cbab5085911b41",
+
"https://deno.land/x/hono@v3.0.0/router/trie-router/node.ts": "ca5b6a1ce6b6dc01003809bfa9cb93a323b37b27feee14c64655015deff7d2a9",
+
"https://deno.land/x/hono@v3.0.0/router/trie-router/router.ts": "0a969528a0c1680b552b20f0ca90e484e968ac279be9d5fd952b61a804d680e7",
+
"https://deno.land/x/hono@v3.0.0/types.ts": "a96ee9693b0de61b42016b80d3cbd958fabb795d8e4becb455845d70f66e7c87",
+
"https://deno.land/x/hono@v3.0.0/utils/body.ts": "b6b5ed679122968a74845df4c5454c677f09adc4f3466d822f3b1397884e540e",
+
"https://deno.land/x/hono@v3.0.0/utils/buffer.ts": "d28ab08d2571e890ec2ad7ce4c0318a503094f8403eac3d5eb18a8e5c23b29b2",
+
"https://deno.land/x/hono@v3.0.0/utils/cookie.ts": "545872bd7af3b455c24fd386ecbccfd161e7d4a0038d6b09b1bb22723602f90a",
+
"https://deno.land/x/hono@v3.0.0/utils/crypto.ts": "bda0e141bbe46d3a4a20f8fbcb6380d473b617123d9fdfa93e4499410b537acc",
+
"https://deno.land/x/hono@v3.0.0/utils/encode.ts": "b628be2de7ab48cc806b1e5b93b3baf6886b7656d6d65dfa80f4fcd9c0f63a5f",
+
"https://deno.land/x/hono@v3.0.0/utils/filepath.ts": "5f708bb6a2f0b8e83a3333868ac86abdfba15e52b46acb5e13018fa2138f0826",
+
"https://deno.land/x/hono@v3.0.0/utils/html.ts": "636c4a04eaea1c52c14a37cd28d83cba66b293d1e31420a4475e951268901ae4",
+
"https://deno.land/x/hono@v3.0.0/utils/http-status.ts": "2d6003e352c1fe918db663fa4bd2b20bf0b9d4e1699ba5e163f317f00b29d938",
+
"https://deno.land/x/hono@v3.0.0/utils/jwt/index.ts": "5e4b82a42eb3603351dfce726cd781ca41cb57437395409d227131aec348d2d5",
+
"https://deno.land/x/hono@v3.0.0/utils/jwt/jwt.ts": "4fefadfb914ad88282949d0cce2be16bdbd0b0614d15ecbb340c4fc47a9929b9",
+
"https://deno.land/x/hono@v3.0.0/utils/jwt/types.ts": "715514fe59f0c37048b2940528bd4f4ec5795e04f0bc1d6d7e33f8d8bb1fe9de",
+
"https://deno.land/x/hono@v3.0.0/utils/mime.ts": "e17bdbac85b97c3d223c48874c2abe867e0720461e9f7e0c340141c080b9c6d6",
+
"https://deno.land/x/hono@v3.0.0/utils/types.ts": "173dedfe018b447cc6b067d2b6968c1f1dccba67ad50526d356b79e0465a5753",
+
"https://deno.land/x/hono@v3.0.0/utils/url.ts": "2cf0f38d976761296ccaf2622b6999b7c0c1d3a1f63f066a6f832288ea7c28d9",
+
"https://deno.land/x/hono@v3.0.0/validator/index.ts": "3dc2c9418dee74333ea8b98642ce112b6d449042bbcefe1e835047fcf2458170",
+
"https://deno.land/x/hono@v3.0.0/validator/validator.ts": "9b2c9983b6b8a31cbc1f687096e232fb159004c15421e39cb050da2ac0f78c95",
+
"https://deno.land/x/hono@v4.3.11/adapter/deno/serve-static.ts": "db226d30f08f1a8bb77653ead42a911357b2f8710d653e43c01eccebb424b295",
+
"https://deno.land/x/hono@v4.3.11/compose.ts": "37d6e33b7db80e4c43a0629b12fd3a1e3406e7d9e62a4bfad4b30426ea7ae4f1",
+
"https://deno.land/x/hono@v4.3.11/context.ts": "facfd749d823a645039571d66d9d228f5ae6836818b65d3b6c4c6891adfe071e",
+
"https://deno.land/x/hono@v4.3.11/hono-base.ts": "fd7e9c1bba1e13119e95158270011784da3a7c3014c149ba0700e700f840ae0d",
+
"https://deno.land/x/hono@v4.3.11/hono.ts": "23edd0140bf0bd5a68c14ae96e5856a5cec6b844277e853b91025e91ea74f416",
+
"https://deno.land/x/hono@v4.3.11/http-exception.ts": "f5dd375e61aa4b764eb9b99dd45a7160f8317fd36d3f79ae22585b9a5e8ad7c5",
+
"https://deno.land/x/hono@v4.3.11/middleware/serve-static/index.ts": "14b760bbbc4478cc3a7fb9728730bc6300581c890365b7101b80c16e70e4b21e",
+
"https://deno.land/x/hono@v4.3.11/request.ts": "7b08602858e642d1626c3106c0bedc2aa8d97e30691a079351d9acef7c5955e6",
+
"https://deno.land/x/hono@v4.3.11/router.ts": "880316f561918fc197481755aac2165fdbe2f530b925c5357a9f98d6e2cc85c7",
+
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/index.ts": "52755829213941756159b7a963097bafde5cc4fc22b13b1c7c9184dc0512d1db",
+
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/node.ts": "7efaa6f4301efc2aad0519c84973061be8555da02e5868409293a1fd98536aaf",
+
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/router.ts": "632f2fa426b3e45a66aeed03f7205dad6d13e8081bed6f8d1d987b6cad8fb455",
+
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/trie.ts": "852ce7207e6701e47fa30889a0d2b8bfcd56d8862c97e7bc9831e0a64bd8835f",
+
"https://deno.land/x/hono@v4.3.11/router/smart-router/index.ts": "74f9b4fe15ea535900f2b9b048581915f12fe94e531dd2b0032f5610e61c3bef",
+
"https://deno.land/x/hono@v4.3.11/router/smart-router/router.ts": "dc22a8505a0f345476f07dca3054c0c50a64d7b81c9af5a904476490dfd5cbb4",
+
"https://deno.land/x/hono@v4.3.11/router/trie-router/index.ts": "3eb75e7f71ba81801631b30de6b1f5cefb2c7239c03797e2b2cbab5085911b41",
+
"https://deno.land/x/hono@v4.3.11/router/trie-router/node.ts": "d3e00e8f1ba7fb26896459d5bba882356891a07793387c4655d1864c519a91de",
+
"https://deno.land/x/hono@v4.3.11/router/trie-router/router.ts": "54ced78d35676302c8fcdda4204f7bdf5a7cc907fbf9967c75674b1e394f830d",
+
"https://deno.land/x/hono@v4.3.11/types.ts": "b561c3ee846121b33c2d81331246cdedf7781636ed72dad7406677105b4275de",
+
"https://deno.land/x/hono@v4.3.11/utils/body.ts": "774cb319dfbe886a9d39f12c43dea15a39f9d01e45de0323167cdd5d0aad14d4",
+
"https://deno.land/x/hono@v4.3.11/utils/filepath.ts": "a83e5fe87396bb291a6c5c28e13356fcbea0b5547bad2c3ba9660100ff964000",
+
"https://deno.land/x/hono@v4.3.11/utils/html.ts": "6ea4f6bf41587a51607dff7a6d2865ef4d5001e4203b07e5c8a45b63a098e871",
+
"https://deno.land/x/hono@v4.3.11/utils/http-status.ts": "f5b820f2793e45209f34deddf147b23e3133a89eb4c57dc643759a504706636b",
+
"https://deno.land/x/hono@v4.3.11/utils/mime.ts": "d1fc2c047191ccb01d736c6acf90df731324536298181dba0ecc2259e5f7d661",
+
"https://deno.land/x/hono@v4.3.11/utils/types.ts": "050bfa9dc6d0cc4b7c5069944a8bd60066c2f9f95ee69833623ad104f11f92bf",
+
"https://deno.land/x/hono@v4.3.11/utils/url.ts": "855169632c61d03703bd08cafb27664ba3fdb352892f01687d5cce8fd49e3cb1",
+
"https://deno.land/x/imagescript@1.3.0/ImageScript.js": "cf90773c966031edd781ed176c598f7ed495e7694cd9b86c986d2d97f783cca0",
+
"https://deno.land/x/imagescript@1.3.0/mod.ts": "18a6cb83c55e690c873505f6fe867364c678afb64934fe7aef593a6b92f79995",
+
"https://deno.land/x/imagescript@1.3.0/png/src/crc.mjs": "5cf50de181d61dd00e66a240d811018ba5070afa8bba302f393604404604de84",
+
"https://deno.land/x/imagescript@1.3.0/png/src/mem.mjs": "4968d400dae069b4bf0ef4767c1802fd2cc7d15d90eda4cfadf5b4cd19b96c6d",
+
"https://deno.land/x/imagescript@1.3.0/png/src/png.mjs": "96ef0ceff1b5a6cd9304749e5f187b4ab238509fb5f9a8be8ee934240271ed8d",
+
"https://deno.land/x/imagescript@1.3.0/png/src/zlib.mjs": "9867dc3fab1d31b664f9344b0d7e977f493d9c912a76c760d012ed2b89f7061c",
+
"https://deno.land/x/imagescript@1.3.0/utils/buffer.js": "952cb1beb8827e50a493a5d1f29a4845e8c648789406d389dd51f51205ba02d8",
+
"https://deno.land/x/imagescript@1.3.0/utils/crc32.js": "573d6222b3605890714ebc374e687ec2aa3e9a949223ea199483e47ca4864f7d",
+
"https://deno.land/x/imagescript@1.3.0/utils/png.js": "fbed9117e0a70602645d70df9c103ff6e79c03e987bd5c1685dcb4200729b6de",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/font.js": "9e75d842608c057045698d6a7cdf5ffd27241b5cdea0391c89a1917b31294524",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/gif.js": "8b86f7b96486bb8ff50fbc7c7487f86cb5cef85e6acd71e1def78a1aa2f12e4f",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/jpeg.js": "75295e2fcf96b4f7bb894b3844fdaa8140d63169d28b466b5d5be89d59a7b6e6",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/png.js": "0659536a8dd8f892c8346e268b2754b4414fad0ec1e9794dfcde1ba1c804ee02",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/svg.js": "f5c8a9d1977b51a7c07549ceb6bbbaca9497321a193f28b3dc229a42d91bcf14",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/tiff.js": "c2d7bdaef094df25aae1752e75167f485e89275d76a1379e39d8949580b7af4f",
+
"https://deno.land/x/imagescript@1.3.0/utils/wasm/zlib.js": "749875f83abffe24d3b977475a0cbd5f9b52bee1fbdbef61ec183cbfc17805f6",
+
"https://deno.land/x/imagescript@1.3.0/v2/framebuffer.mjs": "add44ff184636659714b3c6d4b896f628545451abffbc30b5bcc2e8d9a73d012",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/blur.mjs": "80716f1ffab8a2aeb54a036f583bf51a2b9dd37e005adc000add803df8e8a12f",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/color.mjs": "5e72cdcbf97dc939a2795223f01e3cb0544c0c56b03ea2aa026050df58348814",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/crop.mjs": "69431fa6f687fd9f0c31eff0ec27d7ac925275005e53a37f0c3fab4cc4d9a9ea",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/fill.mjs": "cf1b9488314753fbc9ebf03410ac74c2a34ea5a69fb6892cd6e8366cd1930d93",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/flip.mjs": "825a34a66567dcf15e76a719f1bf2f66fb106503cd69942292b1b0ae05c5718e",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/index.mjs": "423ba687119be2bba8cec72890577d3afa3621b6b8108912242fe937a183f2aa",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/iterator.mjs": "c2adf3d90ce00719a02c48c97634574176a3501ff026676259bd71aa8f5d69b9",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/overlay.mjs": "7e6e2c2ffd25006d52597ab8babc5f8f503d388a3fdf2fbc0eaea02799a020c9",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/resize.mjs": "814e78ebce8eaf8f1f918688db7b52a141405e06a36ed4b25d04413d69e7d17b",
+
"https://deno.land/x/imagescript@1.3.0/v2/ops/rotate.mjs": "a1b65616717bd2eed8db406affea3263b4674dada46b56441ef38167a187455d",
+
"https://deno.land/x/imagescript@1.3.0/v2/util/mem.mjs": "4968d400dae069b4bf0ef4767c1802fd2cc7d15d90eda4cfadf5b4cd19b96c6d",
+
"https://deno.land/x/socket_io@0.2.0/deps.ts": "136b3fc7c55f2a06a367965da3395f1bf7de66d36a0d91c5f795084fa8c67ab3",
+
"https://deno.land/x/socket_io@0.2.0/mod.ts": "61277a4145c378b602e8146ed4302117f130cf3b018c0a07bcb917e1b8c9fcf4",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io-parser/base64-arraybuffer.ts": "57ccea6679609df5416159fcc8a47936ad28ad6fe32235ef78d9223a3a823407",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io-parser/mod.ts": "27d35094e2159ba49f6e74f11ed83b6208a6adb5a2d5ab3cbbdcdc9dc0e36ae7",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/cors.ts": "e39b530dc3526ef85f288766ce592fa5cce2ec38b3fa19922041a7885b79b67c",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/server.ts": "1321852222ccf6b656787881fe0112c2a62930beaf1a56b6f5b327511323176f",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/socket.ts": "b4ae4e2fad305c6178785a1a2ae220e38dfb31dc0ae43759c3d3a4f96ca48c9b",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/transport.ts": "8d09ae6bde2f71942cfbae96265aa693387e054776cf2ef5a3b4f8aafa9a427f",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/transports/polling.ts": "3d3cf369eb430360b57eaf18c74fb7783a1641ed8613c460cdfa8f663ca66be4",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/transports/websocket.ts": "fd818e91e10c55b587a221669f90cc79df42574f781e50ef73bf3539fd9bcfee",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/lib/util.ts": "9f396a141422c8a2e2ef4cbb31c8b7ec96665d8f1ca397888eaaa9ad28ca8c65",
+
"https://deno.land/x/socket_io@0.2.0/packages/engine.io/mod.ts": "3f7d85ebd3bee6e17838f4867927d808f35090a71e088fd4dd802e3255d44c4a",
+
"https://deno.land/x/socket_io@0.2.0/packages/event-emitter/mod.ts": "dcb2cb9c0b409060cf15a6306a8dbebea844aa3c58f782ed1d4bc3ccef7c2835",
+
"https://deno.land/x/socket_io@0.2.0/packages/msgpack/lib/decode.ts": "5906fa37474130b09fe308adb53c95e40d2484a015891be3249fb2f626c462bb",
+
"https://deno.land/x/socket_io@0.2.0/packages/msgpack/lib/encode.ts": "15dab78be56d539c03748c9d57086f7fd580eb8fbe2f8209c28750948c7d962e",
+
"https://deno.land/x/socket_io@0.2.0/packages/msgpack/mod.ts": "c7f4a9859af3e0b23794b400546d93475b19ba5110a02245112a0a994a31d309",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io-parser/mod.ts": "44479cf563b0ad80efedd1059cd40114bc5db199b45e623c2764e80f4a264f8c",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io-redis-adapter/mod.ts": "45d6b7f077f94fec385152bda7fda5ac3153c2ca3548cf4859891af673fa97cc",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/adapter.ts": "8f606f3fe57712221a73a6b01aa8bf1be39c4530ec8ebb8d2905d5313d4da9c4",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/broadcast-operator.ts": "d842eb933acc996a05ac701f6d83ffee49ee9c905c9adbdee70832776045bf63",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/client.ts": "b78e965dc3ab35d2fb9ccb859f4e1ce43d7c830aae9448d4958fa8ef9627eb4d",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/namespace.ts": "920f16545ec4220831b0aa2164e256915c7f4dec318d1527efeae1596b831fe3",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/parent-namespace.ts": "2d7f8a70498d161856aec522ae2f98727d58c5a9c252ad51a6ab5830b4fa9e2e",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/server.ts": "bd450bea5573bb6144a5eded1c03dda93cb3ed8c8c671a6de0261f97307d6c71",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/lib/socket.ts": "7b88e37eabd31ce21039321325f51c955d9123133506acf3af692bf9337f081b",
+
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/mod.ts": "dfd465bdcf23161af0c4d79fb8fc8912418c46a20d15e8b314cec6d9fb508196",
+
"https://deno.land/x/socket_io@0.2.0/test_deps.ts": "1f9dfa07a1e806ccddc9fa5f7255338d9dff67c40d7e83795f4f0f7bd710bde9",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/backoff.ts": "33e4a6e245f8743fbae0ce583993a671a3ac2ecee433a3e7f0bd77b5dd541d84",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/command.ts": "802df3a1f49f6c49fe3e8fcf13fd0cc360b8a02369de0310a72d7f0c8e4ceaab",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/connection.ts": "c31d2e0cb360bc641e7286f1d53cf58790fbcda025c06887f84a821f39d0fdff",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/errors.ts": "bc8f7091cb9f36cdd31229660e0139350b02c26851e3ac69d592c066745feb27",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/executor.ts": "03e5f43df4e0c9c62b0e1be778811d45b6a1966ddf406e21ed5a227af70b7183",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/mod.ts": "20908f005f5c102525ce6aa9261648c95c5f61c6cf782b2cbb2fce88b1220f69",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/pipeline.ts": "80cc26a881149264d51dd019f1044c4ec9012399eca9f516057dc81c9b439370",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/_util.ts": "0525f7f444a96b92cd36423abdfe221f8d8de4a018dc5cb6750a428a5fc897c2",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/command.ts": "b1efd3b62fe5d1230e6d96b5c65ba7de1592a1eda2cc927161e5997a15f404ac",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/mod.ts": "f2601df31d8adc71785b5d19f6a7e43dfce94adbb6735c4dafc1fb129169d11a",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/reply.ts": "beac2061b03190bada179aef1a5d92b47a5104d9835e8c7468a55c24812ae9e4",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/types.ts": "40b0a568cb7fd4dc9107997062584d24e5c6ffa1f21acb6410aa19c92f89e9e1",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/pubsub.ts": "324b87dae0700e4cb350780ce3ae5bc02780f79f3de35e01366b894668b016c6",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/redis.ts": "a5c2cf8c72e7c92c9c8c6911f98227062649f6cba966938428c5414200f3aa54",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/stream.ts": "f116d73cfe04590ff9fa8a3c08be8ff85219d902ef2f6929b8c1e88d92a07810",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/vendor/https/deno.land/std/async/deferred.ts": "7391210927917113e04247ef013d800d54831f550e9a0b439244675c56058c55",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/vendor/https/deno.land/std/async/delay.ts": "c7e2604f7cb5ef00d595de8dd600604902d5be03a183b515b3d6d4bbb48e1700",
+
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/vendor/https/deno.land/std/io/buffer.ts": "8c5f84b7ecf71bc3e12aa299a9fae9e72e495db05281fcdd62006ecd3c5ed3f3"
+
},
+
"workspace": {
+
"dependencies": [
+
"jsr:@hono/hono@^4.6.11"
+
],
+
"packageJson": {
+
"dependencies": [
+
"npm:fs@^0.0.1-security",
+
"npm:http@^0.0.1-security",
+
"npm:nodemon@^3.1.7",
+
"npm:path@~0.12.7",
+
"npm:socket.io@^4.8.1",
+
"npm:zod@^3.24.1"
+
]
+
}
+
}
+
}
+475
io.ts
···
+
// deno-lint-ignore-file no-explicit-any
+
import { Server } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
import { decode } from 'hono/jwt';
+
import chalk from "https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js";
+
import { z } from "zod";
+
+
import * as world from "./constants/world.ts";
+
import * as items from "./constants/items.ts";
+
import * as utils from "./utils.ts";
+
import { LocalPlayer, PlayerCrumb, ShopData, CritterId } from "./types.ts";
+
+
import parties from "./constants/parties.json" with { type: "json" };
+
import itemsJSON from "./public/base/items.json" with { type: "json" };
+
+
export const io = new Server();
+
io.on("connection", (socket) => {
+
let localPlayer: LocalPlayer;
+
+
/** Condensed player data that is sufficient enough for other clients */
+
let localCrumb: PlayerCrumb;
+
+
// TODO: implement checking PlayFab API with ticket
+
socket.once("login", async (ticket: string) => {
+
if (z.object({
+
ticket: z.string()
+
}).safeParse({ ticket: ticket }).success == false) return;
+
+
let playerData;
+
try {
+
playerData = decode(ticket);
+
} catch(_e) {
+
socket.disconnect(true);
+
return
+
}
+
+
// TODO: make this just an inline function instead of having an onPropertyChange function, I'm just really lazy right now lol -index
+
function onPropertyChange(property: string, value: any) {
+
utils.updateAccount(localPlayer.nickname, property, value);
+
}
+
+
const createArrayHandler = (propertyName: string) => ({
+
get(target: any, property: string) {
+
if (typeof target[property] === 'function') {
+
return function (...args: any[]) {
+
const result = target[property].apply(target, args);
+
onPropertyChange(propertyName, target);
+
return result;
+
};
+
}
+
return target[property];
+
}
+
});
+
+
const handler = {
+
set(target: any, property: string, value: any) {
+
if (Array.isArray(value)) {
+
target[property] = new Proxy(value, createArrayHandler(property));
+
onPropertyChange(property, target[property]);
+
} else {
+
target[property] = value;
+
onPropertyChange(property, value);
+
}
+
return true;
+
},
+
get(target: any, property: string) {
+
if (Array.isArray(target[property])) {
+
return new Proxy(target[property], createArrayHandler(property));
+
}
+
return target[property];
+
}
+
};
+
+
const payload = playerData.payload;
+
const sub = payload.sub as {
+
playerId: string,
+
nickname: string,
+
critterId: CritterId,
+
partyId: string,
+
persistent: boolean,
+
mods: Array<string>
+
};
+
const persistentAccount = await utils.getAccount(sub.nickname);
+
if (!sub.persistent || persistentAccount.individual == null) {
+
localPlayer = {
+
playerId: sub.playerId,
+
nickname: sub.nickname,
+
critterId: sub.critterId,
+
ignore: [],
+
friends: [],
+
inventory: [],
+
gear: [],
+
eggs: [],
+
coins: 150,
+
isMember: false,
+
isGuest: false,
+
isTeam: false,
+
x: 0,
+
y: 0,
+
rotation: 0,
+
mutes: [],
+
+
_partyId: sub.partyId, // This key is replaced down the line anyway
+
_mods: []
+
};
+
+
if (sub.persistent) {
+
utils.createAccount(localPlayer);
+
localPlayer = new Proxy<LocalPlayer>(utils.expandAccount(localPlayer), handler);
+
};
+
} else {
+
persistentAccount.individual.critterId = sub.critterId || "hamster";
+
persistentAccount.individual._partyId = sub.partyId || "default";
+
persistentAccount.individual._mods = sub.mods || [];
+
+
localPlayer = new Proxy<LocalPlayer>(utils.expandAccount(persistentAccount.individual), handler);
+
}
+
+
localPlayer._partyId = socket.handshake.query.get('partyId') || 'default';
+
world.queue.splice(world.queue.indexOf(localPlayer.nickname), 1);
+
+
localCrumb = utils.makeCrumb(localPlayer, world.spawnRoom);
+
socket.join(world.spawnRoom);
+
+
world.players[localPlayer.playerId] = localCrumb;
+
socket.emit("login", {
+
player: localPlayer,
+
spawnRoom: world.spawnRoom,
+
});
+
});
+
+
socket.on("joinRoom", (roomId: string) => {
+
if (z.object({
+
roomId: z.enum(Object.keys(world.rooms) as any)
+
}).safeParse({ roomId: roomId }).success == false) return;
+
+
const _room = (world.rooms[roomId] || { default: null }).default;
+
if (!_room) return;
+
+
socket.leave(localCrumb._roomId);
+
socket.broadcast.in(localCrumb._roomId).emit("R", localCrumb);
+
+
const modEnabled = (localPlayer._mods || []).includes('roomExits');
+
//@ts-ignore: Index type is correct
+
const correctExit = world.roomExits[localCrumb._roomId + "->" + roomId]
+
if (modEnabled && correctExit) {
+
localPlayer.x = correctExit.x;
+
localPlayer.y = correctExit.y;
+
localPlayer.rotation = correctExit.r;
+
};
+
+
if (!modEnabled || !correctExit) {
+
localPlayer.x = _room.startX;
+
localPlayer.y = _room.startY;
+
localPlayer.rotation = _room.startR | 180;
+
};
+
+
localCrumb = utils.makeCrumb(localPlayer, roomId);
+
world.players[localPlayer.playerId] = localCrumb;
+
+
console.log(chalk.green('> ' + localPlayer.nickname + ' joined "' + roomId + '"!'));
+
socket.join(roomId);
+
+
let playerCrumbs = Object.values(world.players).filter((crumb) => crumb._roomId == roomId);
+
if (world.npcs[roomId]) {
+
playerCrumbs = [
+
...playerCrumbs,
+
...world.npcs[roomId]
+
];
+
};
+
socket.emit("joinRoom", {
+
name: _room.name,
+
roomId: roomId,
+
playerCrumbs: playerCrumbs
+
});
+
+
socket.broadcast.in(localCrumb._roomId).emit("A", localCrumb);
+
});
+
+
socket.on("moveTo", (x: number, y: number) => {
+
const roomData = world.rooms[localCrumb._roomId][localPlayer._partyId];
+
if (z.object({
+
x: z.number().min(0).max(roomData.width),
+
y: z.number().min(0).max(roomData.height)
+
}).safeParse({ x: x, y: y }).success == false) return;
+
+
const newDirection = utils.getDirection(localPlayer.x, localPlayer.y, x, y);
+
+
localPlayer.x = x;
+
localPlayer.y = y;
+
localPlayer.rotation = newDirection;
+
+
localCrumb.x = x;
+
localCrumb.y = y;
+
localCrumb.r = newDirection;
+
+
io.in(localCrumb._roomId).volatile.emit("X", {
+
i: localPlayer.playerId,
+
x: x,
+
y: y,
+
r: newDirection
+
});
+
});
+
+
socket.on("message", (text: string) => {
+
if (z.object({
+
text: z.string().nonempty()
+
}).safeParse({ text: text }).success == false) return;
+
+
console.log(chalk.gray(`> ${localPlayer.nickname} sent message: "%s"`), text);
+
localCrumb.m = text;
+
+
socket.broadcast.in(localCrumb._roomId).emit("M", {
+
i: localPlayer.playerId,
+
m: text
+
});
+
+
setTimeout(() => {
+
if (localCrumb.m != text) return;
+
localCrumb.m = "";
+
}, 5e3);
+
});
+
+
socket.on("emote", (emote: string) => {
+
if (z.object({
+
emote: z.string().nonempty() // TODO: make this an enum
+
}).safeParse({ emote: emote }).success == false) return;
+
+
console.log(chalk.gray(`> ${localPlayer.nickname} sent emote: %s`), emote);
+
localCrumb.e = emote;
+
+
socket.broadcast.in(localCrumb._roomId).emit("E", {
+
i: localPlayer.playerId,
+
e: emote
+
});
+
+
setTimeout(() => {
+
if (localCrumb.e != emote) return;
+
localCrumb.e = "";
+
}, 5e3);
+
});
+
+
// ? Options is specified just because sometimes it is sent, but its always an empty string
+
socket.on("code", (code: string, _options?: string) => {
+
if (z.object({
+
command: z.enum([
+
"pop",
+
"freeitem",
+
"tbt",
+
"darkmode",
+
"spydar",
+
"allitems"
+
])
+
}).safeParse({
+
command: code
+
}).success == false) return;
+
+
console.log(chalk.gray(`> ${localPlayer.nickname} sent code: %s`), code);
+
+
const addItem = function(id: string, showGUI: boolean = false) {
+
if (!localPlayer.inventory.includes(id)) {
+
socket.emit("addItem", { itemId: id, showGUI: showGUI });
+
localPlayer.inventory.push(id);
+
}
+
}
+
+
// Misc. Codes
+
switch(code) {
+
case 'pop': {
+
socket.emit("pop", Object.values(world.players).filter((critter) => critter.c != "huggable").length);
+
break;
+
}
+
case 'freeitem': {
+
addItem(items.shop.freeItem.itemId, true);
+
break;
+
}
+
case 'tbt': {
+
const _throwbackItem = utils.getNewCodeItem(localPlayer, items.throwback);
+
if (_throwbackItem) addItem(_throwbackItem, true);
+
break;
+
}
+
case 'darkmode': {
+
addItem("3d_black", true);
+
break;
+
}
+
case 'spydar': {
+
localPlayer.gear = [
+
"sun_orange",
+
"super_mask_black",
+
"toque_blue",
+
"dracula_cloak",
+
"headphones_black",
+
"hoodie_black"
+
];
+
+
if (localCrumb._roomId == "tavern") {
+
localPlayer.x = 216;
+
localPlayer.y = 118;
+
+
localCrumb.x = 216;
+
localCrumb.y = 118;
+
+
io.in(localCrumb._roomId).volatile.emit("X", {
+
i: localPlayer.playerId,
+
x: 216,
+
y: 118
+
});
+
}
+
+
io.in(localCrumb._roomId).emit("G", {
+
i: localPlayer.playerId,
+
g: localPlayer.gear
+
});
+
+
socket.emit("updateGear", localPlayer.gear);
+
break;
+
}
+
case 'allitems': {
+
for (const item of itemsJSON) {
+
addItem(item.itemId, false);
+
}
+
break;
+
}
+
};
+
+
// Item Codes
+
const _itemCodes = items.codes as Record<string, string|Array<string>>
+
const item = _itemCodes[code];
+
+
if (typeof(item) == "string") {
+
addItem(item, true);
+
} else if (typeof(item) == "object") {
+
for (const _ of item) {
+
addItem(_, true);
+
}
+
}
+
+
// Event Codes (eg. Christmas 2019)
+
const _eventItemCodes = items.eventCodes as Record<string, Record<string, string>>;
+
const eventItem = (_eventItemCodes[localPlayer._partyId] || {})[code];
+
if (eventItem) addItem(eventItem);
+
});
+
+
socket.on("updateGear", (gear: Array<string>) => {
+
if (z.object({
+
gear: z.array(z.string().nonempty()).default([])
+
}).strict().safeParse({ gear: gear }).success == false) return;
+
+
const _gear = [];
+
for (const itemId of gear) {
+
if (localPlayer.inventory.includes(itemId)) {
+
_gear.push(itemId)
+
}
+
}
+
localPlayer.gear = _gear;
+
+
io.in(localCrumb._roomId).emit("G", {
+
i: localPlayer.playerId,
+
g: localPlayer.gear
+
});
+
+
socket.emit("updateGear", localPlayer.gear);
+
});
+
+
socket.on("getShop", () => {
+
const _shopItems = items.shop as unknown as ShopData;
+
socket.emit("getShop", {
+
lastItem: _shopItems.lastItem.itemId,
+
freeItem: _shopItems.freeItem.itemId,
+
nextItem: _shopItems.nextItem.itemId,
+
collection: _shopItems.collection.map((item) => item.itemId)
+
})
+
});
+
+
socket.on("buyItem", (itemId: string) => {
+
if (z.object({
+
itemId: z.string().nonempty()
+
}).strict().safeParse({ itemId: itemId }).success == false) return;
+
+
// ? Free item is excluded from this list because the game just sends the "/freeitem" code
+
const currentShop = items.shop;
+
const _shopItems = [currentShop.lastItem, currentShop.nextItem, ...currentShop.collection]
+
+
const target = _shopItems.find((item) => item.itemId == itemId)!;
+
if (!target) {
+
console.log(chalk.red("> There is no item in this week's shop with itemId: %s"), itemId);
+
return;
+
};
+
+
if (localPlayer.coins >= target.cost && !localPlayer.inventory.includes(itemId)) {
+
console.log(chalk.green("[+] Bought item: %s for %d coins"), itemId, target.cost);
+
localPlayer.coins -= target.cost;
+
localPlayer.inventory.push(itemId);
+
+
socket.emit("buyItem", { itemId: itemId });
+
socket.emit("updateCoins", { balance: localPlayer.coins });
+
}
+
});
+
+
socket.on("trigger", async () => {
+
const activatedTrigger = await utils.getTrigger(localPlayer, localCrumb._roomId, localPlayer._partyId);
+
if (!activatedTrigger) return;
+
+
if (activatedTrigger.hasItems) {
+
for (const item of activatedTrigger.hasItems) {
+
if (!localPlayer.inventory.includes(item)) return;
+
}
+
}
+
+
if (activatedTrigger.grantItem) {
+
let items = activatedTrigger.grantItem;
+
if (typeof(items) == 'string') items = [items];
+
+
for (const item of items) {
+
if (!localPlayer.inventory.includes(item)) {
+
socket.emit("addItem", { itemId: item, showGUI: true });
+
localPlayer.inventory.push(item);
+
}
+
}
+
}
+
+
if (activatedTrigger.addEgg) {
+
const egg = activatedTrigger.addEgg;
+
socket.emit("addEgg", egg);
+
localPlayer.eggs.push(egg);
+
}
+
});
+
+
socket.on("addIgnore", (playerId: string) => {
+
if (z.object({
+
playerId: z.enum(Object.keys(world.players) as any)
+
}).strict().safeParse({ playerId: playerId }).success == false) return;
+
+
if (Object.keys(world.players).includes(playerId) && !localPlayer.ignore.includes(playerId)) {
+
localPlayer.ignore.push(playerId);
+
};
+
});
+
+
socket.on("attack", (playerId: string) => {
+
if (z.object({
+
playerId: z.enum(Object.keys(world.players) as any)
+
}).strict().safeParse({ playerId: playerId }).success == false) return;
+
+
if (!localPlayer.gear.includes('bb_beebee')) return;
+
const monster = Object.values(world.players).find((player) => player.i == playerId && player.c == "huggable");
+
+
if (monster) {
+
io.in(localCrumb._roomId).emit("R", monster);
+
+
localPlayer.coins += 10;
+
socket.emit("updateCoins", { balance: localPlayer.coins });
+
+
delete world.players[playerId];
+
};
+
});
+
+
socket.on("switchParty", (partyId: string) => {
+
if (z.object({
+
partyId: z.enum(parties as any)
+
}).strict().safeParse({ partyId: partyId }).success == false) return;
+
+
localPlayer._partyId = partyId;
+
socket.emit("switchParty");
+
});
+
+
socket.on("beep", () => socket.emit("beep"));
+
+
socket.on("disconnect", (reason) => {
+
if (reason == "server namespace disconnect") return;
+
+
if (localPlayer && localCrumb) {
+
io.in(localCrumb._roomId).emit("R", localCrumb);
+
delete world.players[localPlayer.playerId];
+
};
+
});
+
});
+144
main.ts
···
+
import { serve } from "https://deno.land/std@0.162.0/http/server.ts";
+
+
import { sign } from 'hono/jwt';
+
import { Hono } from "https://deno.land/x/hono@v3.0.0/mod.ts";
+
import { serveStatic } from "https://deno.land/x/hono@v3.0.0/middleware.ts";
+
import { env } from 'hono/adapter';
+
import { validator } from 'hono/validator';
+
+
import { io } from "./io.ts";
+
import * as world from "./constants/world.ts";
+
import { Room } from "./types.ts";
+
import * as schemas from "./schema.ts";
+
import { getAccount } from "./utils.ts";
+
import parties from "./constants/parties.json" with { type: 'json' };
+
+
const app = new Hono();
+
app.get('/*', serveStatic({ root: './public' }));
+
+
// APIs for debugging and other purposes
+
app.get('/api/server/players', (c) => c.json({ players: world.players }));
+
+
app.get('/api/server/rooms', (c) => c.json(world.rooms));
+
+
app.get('/api/server/persistence', async (c) => {
+
const account = await getAccount();
+
return c.json({
+
success: true,
+
data: account
+
});
+
})
+
+
// APIs for use by the client
+
app.post('/api/client/login', validator('json', async (_value, c) => {
+
try {
+
const body = await c.req.json();
+
const parsed = schemas.login.safeParse(body);
+
if (!parsed.success) {
+
return c.json({
+
success: false,
+
message: "Validation failure",
+
error: parsed.error
+
}, 400);
+
};
+
return parsed.data;
+
} catch(_e) {
+
return c.json({
+
success: false,
+
message: "Bad request"
+
}, 400);
+
}
+
// deno-lint-ignore no-explicit-any
+
}) as any, async (c) => {
+
const body = c.req.valid('json') as {
+
nickname: string,
+
critterId: string,
+
partyId: string,
+
persistent: boolean,
+
mods: Array<string>
+
};
+
+
const _players = Object.values(world.players);
+
if (_players.find((player) => player.n == body.nickname) || world.queue.includes(body.nickname)) {
+
return c.json({
+
success: false,
+
message: "There is already a player with this nickname online."
+
});
+
}
+
+
const JWT_CONTENT = {
+
sub: {
+
playerId: crypto.randomUUID(),
+
...body // ZOD validator is set to make the body strict, so this expansion should be fine
+
},
+
exp: Math.floor(Date.now() / 1000) + 60 * 5 // 5 mins expiry
+
};
+
+
//@ts-ignore: Deno lint
+
const { JWT_TOKEN } = env<{ JWT_TOKEN: string }>(c);
+
const token = await sign(JWT_CONTENT, JWT_TOKEN);
+
+
world.queue.push(body.nickname);
+
return c.json({
+
success: true,
+
playerId: JWT_CONTENT.sub.playerId,
+
token: token
+
});
+
});
+
+
app.get('/api/client/rooms', (c) => {
+
const partyId = c.req.query('partyId') || 'default';
+
if (!parties.includes(partyId)) {
+
return c.json({
+
success: false,
+
message: "Invalid partyId hash provided."
+
});
+
}
+
+
let missing = 0;
+
const roomResponse = Object.keys(world.rooms).reduce((res: Array<Room>, roomId) => {
+
const room = world.rooms[roomId];
+
+
if (room[partyId]) {
+
if (!room[partyId].partyExclusive || room[partyId].partyExclusive.includes(partyId)) {
+
res.push(room[partyId]);
+
} else {
+
missing++;
+
}
+
} else {
+
if (!room.default.partyExclusive || room.default.partyExclusive.includes(partyId)) {
+
res.push(room.default);
+
} else {
+
missing++;
+
}
+
}
+
return res;
+
}, []);
+
+
if (missing == Object.keys(world.rooms).length) {
+
return c.json({
+
success: false,
+
message: "No rooms were fetched while indexxing using the specified partyId hash."
+
});
+
}
+
+
const res = roomResponse.filter((room) => room != null);
+
if (c.req.query('debug')) {
+
const roomNames = res.map((room) => room.name);
+
return c.json({
+
parties: parties,
+
data: roomNames
+
});
+
}
+
+
return c.json({
+
parties: parties,
+
data: res
+
});
+
});
+
+
const handler = io.handler(async (req) => {
+
return await app.fetch(req);
+
});
+
+
await serve(handler, { port: 3257 });
+573
package-lock.json
···
+
{
+
"name": "box-critters-revival",
+
"version": "1.0.0",
+
"lockfileVersion": 3,
+
"requires": true,
+
"packages": {
+
"": {
+
"name": "box-critters-revival",
+
"version": "1.0.0",
+
"license": "ISC",
+
"dependencies": {
+
"fs": "^0.0.1-security",
+
"http": "^0.0.1-security",
+
"nodemon": "^3.1.7",
+
"path": "^0.12.7",
+
"socket.io": "^4.8.1"
+
}
+
},
+
"node_modules/@socket.io/component-emitter": {
+
"version": "3.1.2",
+
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
+
},
+
"node_modules/@types/cookie": {
+
"version": "0.4.1",
+
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
+
"integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
+
},
+
"node_modules/@types/cors": {
+
"version": "2.8.17",
+
"resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz",
+
"integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==",
+
"dependencies": {
+
"@types/node": "*"
+
}
+
},
+
"node_modules/@types/node": {
+
"version": "22.9.1",
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.1.tgz",
+
"integrity": "sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==",
+
"dependencies": {
+
"undici-types": "~6.19.8"
+
}
+
},
+
"node_modules/accepts": {
+
"version": "1.3.8",
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+
"dependencies": {
+
"mime-types": "~2.1.34",
+
"negotiator": "0.6.3"
+
},
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
+
"node_modules/anymatch": {
+
"version": "3.1.3",
+
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+
"dependencies": {
+
"normalize-path": "^3.0.0",
+
"picomatch": "^2.0.4"
+
},
+
"engines": {
+
"node": ">= 8"
+
}
+
},
+
"node_modules/balanced-match": {
+
"version": "1.0.2",
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+
},
+
"node_modules/base64id": {
+
"version": "2.0.0",
+
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+
"integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+
"engines": {
+
"node": "^4.5.0 || >= 5.9"
+
}
+
},
+
"node_modules/binary-extensions": {
+
"version": "2.3.0",
+
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+
"engines": {
+
"node": ">=8"
+
},
+
"funding": {
+
"url": "https://github.com/sponsors/sindresorhus"
+
}
+
},
+
"node_modules/brace-expansion": {
+
"version": "1.1.11",
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+
"dependencies": {
+
"balanced-match": "^1.0.0",
+
"concat-map": "0.0.1"
+
}
+
},
+
"node_modules/braces": {
+
"version": "3.0.3",
+
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+
"dependencies": {
+
"fill-range": "^7.1.1"
+
},
+
"engines": {
+
"node": ">=8"
+
}
+
},
+
"node_modules/chokidar": {
+
"version": "3.6.0",
+
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+
"dependencies": {
+
"anymatch": "~3.1.2",
+
"braces": "~3.0.2",
+
"glob-parent": "~5.1.2",
+
"is-binary-path": "~2.1.0",
+
"is-glob": "~4.0.1",
+
"normalize-path": "~3.0.0",
+
"readdirp": "~3.6.0"
+
},
+
"engines": {
+
"node": ">= 8.10.0"
+
},
+
"funding": {
+
"url": "https://paulmillr.com/funding/"
+
},
+
"optionalDependencies": {
+
"fsevents": "~2.3.2"
+
}
+
},
+
"node_modules/concat-map": {
+
"version": "0.0.1",
+
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+
},
+
"node_modules/cookie": {
+
"version": "0.7.2",
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
+
"node_modules/cors": {
+
"version": "2.8.5",
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+
"dependencies": {
+
"object-assign": "^4",
+
"vary": "^1"
+
},
+
"engines": {
+
"node": ">= 0.10"
+
}
+
},
+
"node_modules/debug": {
+
"version": "4.3.7",
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+
"dependencies": {
+
"ms": "^2.1.3"
+
},
+
"engines": {
+
"node": ">=6.0"
+
},
+
"peerDependenciesMeta": {
+
"supports-color": {
+
"optional": true
+
}
+
}
+
},
+
"node_modules/engine.io": {
+
"version": "6.6.2",
+
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
+
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
+
"dependencies": {
+
"@types/cookie": "^0.4.1",
+
"@types/cors": "^2.8.12",
+
"@types/node": ">=10.0.0",
+
"accepts": "~1.3.4",
+
"base64id": "2.0.0",
+
"cookie": "~0.7.2",
+
"cors": "~2.8.5",
+
"debug": "~4.3.1",
+
"engine.io-parser": "~5.2.1",
+
"ws": "~8.17.1"
+
},
+
"engines": {
+
"node": ">=10.2.0"
+
}
+
},
+
"node_modules/engine.io-parser": {
+
"version": "5.2.3",
+
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+
"engines": {
+
"node": ">=10.0.0"
+
}
+
},
+
"node_modules/fill-range": {
+
"version": "7.1.1",
+
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+
"dependencies": {
+
"to-regex-range": "^5.0.1"
+
},
+
"engines": {
+
"node": ">=8"
+
}
+
},
+
"node_modules/fs": {
+
"version": "0.0.1-security",
+
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
+
},
+
"node_modules/fsevents": {
+
"version": "2.3.3",
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+
"hasInstallScript": true,
+
"optional": true,
+
"os": [
+
"darwin"
+
],
+
"engines": {
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+
}
+
},
+
"node_modules/glob-parent": {
+
"version": "5.1.2",
+
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+
"dependencies": {
+
"is-glob": "^4.0.1"
+
},
+
"engines": {
+
"node": ">= 6"
+
}
+
},
+
"node_modules/has-flag": {
+
"version": "3.0.0",
+
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+
"engines": {
+
"node": ">=4"
+
}
+
},
+
"node_modules/http": {
+
"version": "0.0.1-security",
+
"resolved": "https://registry.npmjs.org/http/-/http-0.0.1-security.tgz",
+
"integrity": "sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g=="
+
},
+
"node_modules/ignore-by-default": {
+
"version": "1.0.1",
+
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
+
},
+
"node_modules/inherits": {
+
"version": "2.0.3",
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
+
},
+
"node_modules/is-binary-path": {
+
"version": "2.1.0",
+
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+
"dependencies": {
+
"binary-extensions": "^2.0.0"
+
},
+
"engines": {
+
"node": ">=8"
+
}
+
},
+
"node_modules/is-extglob": {
+
"version": "2.1.1",
+
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+
"engines": {
+
"node": ">=0.10.0"
+
}
+
},
+
"node_modules/is-glob": {
+
"version": "4.0.3",
+
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+
"dependencies": {
+
"is-extglob": "^2.1.1"
+
},
+
"engines": {
+
"node": ">=0.10.0"
+
}
+
},
+
"node_modules/is-number": {
+
"version": "7.0.0",
+
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+
"engines": {
+
"node": ">=0.12.0"
+
}
+
},
+
"node_modules/mime-db": {
+
"version": "1.52.0",
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
+
"node_modules/mime-types": {
+
"version": "2.1.35",
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+
"dependencies": {
+
"mime-db": "1.52.0"
+
},
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
+
"node_modules/minimatch": {
+
"version": "3.1.2",
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+
"dependencies": {
+
"brace-expansion": "^1.1.7"
+
},
+
"engines": {
+
"node": "*"
+
}
+
},
+
"node_modules/ms": {
+
"version": "2.1.3",
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+
},
+
"node_modules/negotiator": {
+
"version": "0.6.3",
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+
"engines": {
+
"node": ">= 0.6"
+
}
+
},
+
"node_modules/nodemon": {
+
"version": "3.1.7",
+
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz",
+
"integrity": "sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==",
+
"dependencies": {
+
"chokidar": "^3.5.2",
+
"debug": "^4",
+
"ignore-by-default": "^1.0.1",
+
"minimatch": "^3.1.2",
+
"pstree.remy": "^1.1.8",
+
"semver": "^7.5.3",
+
"simple-update-notifier": "^2.0.0",
+
"supports-color": "^5.5.0",
+
"touch": "^3.1.0",
+
"undefsafe": "^2.0.5"
+
},
+
"bin": {
+
"nodemon": "bin/nodemon.js"
+
},
+
"engines": {
+
"node": ">=10"
+
},
+
"funding": {
+
"type": "opencollective",
+
"url": "https://opencollective.com/nodemon"
+
}
+
},
+
"node_modules/normalize-path": {
+
"version": "3.0.0",
+
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+
"engines": {
+
"node": ">=0.10.0"
+
}
+
},
+
"node_modules/object-assign": {
+
"version": "4.1.1",
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+
"engines": {
+
"node": ">=0.10.0"
+
}
+
},
+
"node_modules/path": {
+
"version": "0.12.7",
+
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
+
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
+
"dependencies": {
+
"process": "^0.11.1",
+
"util": "^0.10.3"
+
}
+
},
+
"node_modules/picomatch": {
+
"version": "2.3.1",
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+
"engines": {
+
"node": ">=8.6"
+
},
+
"funding": {
+
"url": "https://github.com/sponsors/jonschlinkert"
+
}
+
},
+
"node_modules/process": {
+
"version": "0.11.10",
+
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+
"engines": {
+
"node": ">= 0.6.0"
+
}
+
},
+
"node_modules/pstree.remy": {
+
"version": "1.1.8",
+
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
+
},
+
"node_modules/readdirp": {
+
"version": "3.6.0",
+
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+
"dependencies": {
+
"picomatch": "^2.2.1"
+
},
+
"engines": {
+
"node": ">=8.10.0"
+
}
+
},
+
"node_modules/semver": {
+
"version": "7.6.3",
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+
"bin": {
+
"semver": "bin/semver.js"
+
},
+
"engines": {
+
"node": ">=10"
+
}
+
},
+
"node_modules/simple-update-notifier": {
+
"version": "2.0.0",
+
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+
"dependencies": {
+
"semver": "^7.5.3"
+
},
+
"engines": {
+
"node": ">=10"
+
}
+
},
+
"node_modules/socket.io": {
+
"version": "4.8.1",
+
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
+
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
+
"dependencies": {
+
"accepts": "~1.3.4",
+
"base64id": "~2.0.0",
+
"cors": "~2.8.5",
+
"debug": "~4.3.2",
+
"engine.io": "~6.6.0",
+
"socket.io-adapter": "~2.5.2",
+
"socket.io-parser": "~4.2.4"
+
},
+
"engines": {
+
"node": ">=10.2.0"
+
}
+
},
+
"node_modules/socket.io-adapter": {
+
"version": "2.5.5",
+
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
+
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
+
"dependencies": {
+
"debug": "~4.3.4",
+
"ws": "~8.17.1"
+
}
+
},
+
"node_modules/socket.io-parser": {
+
"version": "4.2.4",
+
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+
"dependencies": {
+
"@socket.io/component-emitter": "~3.1.0",
+
"debug": "~4.3.1"
+
},
+
"engines": {
+
"node": ">=10.0.0"
+
}
+
},
+
"node_modules/supports-color": {
+
"version": "5.5.0",
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+
"dependencies": {
+
"has-flag": "^3.0.0"
+
},
+
"engines": {
+
"node": ">=4"
+
}
+
},
+
"node_modules/to-regex-range": {
+
"version": "5.0.1",
+
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+
"dependencies": {
+
"is-number": "^7.0.0"
+
},
+
"engines": {
+
"node": ">=8.0"
+
}
+
},
+
"node_modules/touch": {
+
"version": "3.1.1",
+
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+
"bin": {
+
"nodetouch": "bin/nodetouch.js"
+
}
+
},
+
"node_modules/undefsafe": {
+
"version": "2.0.5",
+
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
+
},
+
"node_modules/undici-types": {
+
"version": "6.19.8",
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+
},
+
"node_modules/util": {
+
"version": "0.10.4",
+
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+
"dependencies": {
+
"inherits": "2.0.3"
+
}
+
},
+
"node_modules/vary": {
+
"version": "1.1.2",
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+
"engines": {
+
"node": ">= 0.8"
+
}
+
},
+
"node_modules/ws": {
+
"version": "8.17.1",
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+
"engines": {
+
"node": ">=10.0.0"
+
},
+
"peerDependencies": {
+
"bufferutil": "^4.0.1",
+
"utf-8-validate": ">=5.0.2"
+
},
+
"peerDependenciesMeta": {
+
"bufferutil": {
+
"optional": true
+
},
+
"utf-8-validate": {
+
"optional": true
+
}
+
}
+
}
+
}
+
}
+21
package.json
···
+
{
+
"name": "box-critters-revival",
+
"version": "1.0.0",
+
"description": "",
+
"main": "main.js",
+
"scripts": {
+
"test": "echo \"Error: no test specified\" && exit 1",
+
"start": "nodemon main.js",
+
"dev": "nodemon main.js"
+
},
+
"author": "",
+
"license": "ISC",
+
"dependencies": {
+
"fs": "^0.0.1-security",
+
"http": "^0.0.1-security",
+
"nodemon": "^3.1.7",
+
"path": "^0.12.7",
+
"socket.io": "^4.8.1",
+
"zod": "^3.24.1"
+
}
+
}
+25
schema.ts
···
+
import { z } from 'zod';
+
import parties from './constants/parties.json' with { type: 'json' };
+
+
/*
+
LOGIN API
+
*/
+
export const login = z.object({
+
nickname: z.string().nonempty().max(25),
+
critterId: z.enum([
+
"hamster",
+
"beaver",
+
"lizard",
+
"raccoon",
+
"penguin",
+
"snail",
+
"snow_greeter",
+
"snowkeeper",
+
"snowgirl",
+
"snow_patrol",
+
"snowgrandma"
+
]).default("hamster"),
+
partyId: z.enum(parties as [string, ...string[]]).default("default"),
+
persistent: z.boolean().default(false),
+
mods: z.array(z.enum(["roomExits"])).default([])
+
}).strict(); // Strict to disallow extra keys
+124
types.ts
···
+
export type CritterId = "hamster" | "snail" | "lizard" | "beaver" | "raccoon" | "penguin" | "huggable";
+
+
export type Trigger = {
+
hex: string,
+
world?: { joinRoom: string },
+
room?: { hide: Array<string> },
+
server?: {
+
grantItem?: string | Array<string>,
+
hasItems?: Array<string>,
+
joinGame?: string,
+
addEgg?: string
+
}
+
}
+
+
export type Room = {
+
roomId: string,
+
name: string,
+
width: number,
+
height: number,
+
startX: number,
+
startY: number,
+
startR: number,
+
media: {
+
background: string,
+
foreground?: string,
+
treasure?: string,
+
navMesh: string,
+
music?: string,
+
video?: string,
+
},
+
layout: string,
+
triggers: Array<Trigger>,
+
spriteSheet: string,
+
extra: null,
+
partyExclusive?: Array<string>
+
}
+
+
export type LocalPlayer = {
+
playerId: string,
+
nickname: string,
+
critterId: CritterId,
+
ignore: Array<string>,
+
friends: Array<string>,
+
inventory: Array<string>,
+
gear: Array<string>,
+
/** Eggs is the term used to describe any object used in a scavenger hunt. Any prop name found in that list will be hidden and replaced with it's "_found" suffix prop counterpart */
+
eggs: Array<string>,
+
coins: number,
+
isMember: boolean | false,
+
isGuest: boolean | false,
+
isTeam: boolean | false,
+
x: number | 440,
+
y: number | 210,
+
rotation: number | 180,
+
mutes: Array<unknown>,
+
+
_partyId: string,
+
_mods: Array<string>,
+
+
// deno-lint-ignore no-explicit-any
+
[key: string]: any
+
}
+
+
export type PlayerCrumb = {
+
/** Player ID */
+
i: string,
+
/** Player Nickname */
+
n: string,
+
/** Critter (Hamster, Beaver, Lizard, Snail, etc) */
+
c: CritterId,
+
x: number,
+
y: number,
+
r: number,
+
/** Gear (equipped items) */
+
g: Array<string>,
+
+
/** Message */
+
m: string,
+
/** Emote */
+
e: string,
+
+
_roomId: string
+
}
+
+
export type ShopData = {
+
lastItem: { itemId: string, cost: number },
+
freeItem: { itemId: string, cost: number },
+
nextItem: { itemId: string, cost: number },
+
collection: Array<{ itemId: string, cost: number }>
+
}
+
+
/*
+
Socket.io
+
*/
+
export interface ServerToClientEvents {
+
login: () => {player: LocalPlayer};
+
updateGear: () => { i: number, g: Array<string> };
+
updateCoins: () => { balance: number };
+
addItem: () => { itemId: string };
+
addEgg: () => string;
+
A: () => PlayerCrumb;
+
R: () => PlayerCrumb;
+
X: () => { i: number, x: number, y: number };
+
M: () => { i: number, m: string };
+
E: () => { i: number, e: string };
+
G: () => { i: number, g: Array<string> };
+
}
+
+
export interface ClientToServerEvents {
+
login: (ticket: string) => void;
+
joinLobby: () => unknown; // Unsure what this is for, I don't think the game had several servers
+
joinRoom: (roomId: string) => { name: string, roomId: string, playerCrumbs: Array<PlayerCrumb> };
+
message: (text: string) => void;
+
emote: (emote: string) => void;
+
code: (code: string, options?: string) => void;
+
addIgnore: (id: number) => void;
+
addFriend: (id: number) => void;
+
moveTo: (x: number, y: number) => void;
+
updateCritter: () => unknown; // Unsure of what this is, maybe settings?
+
updateGear: (gear: Array<string>) => void;
+
getShop: () => ShopData;
+
buyItem: (itemId: string) => void;
+
trigger: () => void;
+
}
+167
utils.ts
···
+
import { Image } from 'https://deno.land/x/imagescript@1.3.0/mod.ts';
+
import chalk from "https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js"
+
import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
+
+
import { rooms, spawnRoom } from "./constants/world.ts";
+
import { Room, LocalPlayer, PlayerCrumb } from "./types.ts";
+
+
/** Condenses the local player variable into data that is sufficient enough for other clients */
+
export function makeCrumb(player: LocalPlayer, roomId: string): PlayerCrumb {
+
return {
+
i: player.playerId,
+
n: player.nickname,
+
c: player.critterId,
+
x: player.x,
+
y: player.y,
+
r: player.rotation,
+
g: player.gear,
+
+
// message & emote
+
m: "",
+
e: "",
+
+
_roomId: roomId
+
}
+
}
+
+
// TODO: use the correct triggers for the active party
+
export async function getTrigger(player: LocalPlayer, roomId: string, partyId: string) {
+
const room = rooms[roomId][partyId];
+
if (!room) {
+
console.log(chalk.red(`[!] Cannot find room: "${roomId}@${partyId}"!`));
+
return;
+
}
+
+
try {
+
//@ts-ignore: Deno lint
+
const treasureBuffer = await Deno.readFile(room.media.treasure?.replace('..','public'));
+
const treasure = await Image.decode(treasureBuffer);
+
if (!treasure) throw new Error('Missing map server for room "' + roomId + '"!');
+
+
const pixel = treasure.getPixelAt(player.x, player.y);
+
const r = (pixel >> 24) & 0xFF, g = (pixel >> 16) & 0xFF, b = (pixel >> 8) & 0xFF;
+
const hexCode = ((r << 16) | (g << 8) | b).toString(16).padStart(6, '0');
+
+
const trigger = room.triggers.find((trigger) => trigger.hex == hexCode);
+
if (trigger) {
+
return trigger.server;
+
} else {
+
return null;
+
}
+
} catch(e) {
+
console.warn(chalk.red('[!] Caught error while checking for activated trigger.'), e);
+
}
+
}
+
+
export function getNewCodeItem(player: LocalPlayer, items: Array<string>) {
+
const itemsSet = new Set(player.inventory);
+
const available = items.filter(item => !itemsSet.has(item));
+
return available.length === 0 ? null : available[Math.floor(Math.random() * available.length)];
+
}
+
+
/**
+
* Indexes the /media/rooms directory for all versions of all rooms
+
* @returns All versions of every room
+
*/
+
export async function indexRoomData() {
+
const _roomData: Record<string, Record<string, Room>> = {};
+
+
const basePath = join(Deno.cwd(), 'public', 'media', 'rooms');
+
const _rooms = Deno.readDir(basePath);
+
+
for await (const room of _rooms) {
+
if (room.isDirectory) {
+
_roomData[room.name] = {};
+
const roomPath = join(basePath, room.name);
+
const versions = Deno.readDir(roomPath);
+
for await (const version of versions) {
+
if (version.isDirectory) {
+
const versionPath = join(roomPath, version.name, 'data.json');
+
try {
+
const data = await Deno.readTextFile(versionPath);
+
_roomData[room.name][version.name] = JSON.parse(data);
+
} catch(_) {
+
console.log(chalk.red('[!] "%s@%s" is missing a data.json file'), room.name, version.name);
+
};
+
}
+
}
+
}
+
}
+
+
return _roomData
+
}
+
+
export async function getAccount(nickname?: string) {
+
let accounts = [];
+
try {
+
const data = await Deno.readTextFile('accounts.json');
+
accounts = JSON.parse(data);
+
} catch (error) {
+
if (error instanceof Deno.errors.NotFound) {
+
console.log(chalk.gray('Persistent login JSON is missing, using blank JSON array..'));
+
accounts = [];
+
} else {
+
console.log(chalk.red('[!] Failure to fetch persistent login data with nickname: '), nickname);
+
throw error;
+
};
+
}
+
+
if (nickname) {
+
const existingAccount = accounts.find((player: { nickname: string }) => player.nickname == nickname);
+
if (existingAccount) {
+
return {
+
all: accounts,
+
individual: existingAccount,
+
}
+
} else {
+
return {
+
all: accounts,
+
individual: null
+
}
+
}
+
} else {
+
return accounts;
+
}
+
}
+
+
export async function updateAccount(nickname: string, property: string, value: unknown) {
+
if (["x", "y", "rotation", "_partyId"].includes(property)) return;
+
const accounts = await getAccount(nickname);
+
+
accounts.individual[property] = value;
+
await Deno.writeTextFile('accounts.json', JSON.stringify(accounts.all, null, 2));
+
}
+
+
export function trimAccount(player: LocalPlayer) {
+
for (const key of [
+
"critterId",
+
"x",
+
"y",
+
"rotation",
+
"_partyId",
+
"_mods"
+
]) {
+
delete player[key];
+
}
+
return player;
+
}
+
+
export function expandAccount(player: LocalPlayer) {
+
const defaultPos = rooms[spawnRoom].default;
+
player.x = defaultPos.startX;
+
player.y = defaultPos.startY;
+
player.rotation = defaultPos.startR;
+
return player;
+
}
+
+
export function getDirection(x: number, y: number, targetX: number, targetY: number) {
+
const a = Math.floor((180 * Math.atan2(targetX - x, y - targetY)) / Math.PI);
+
return a < 0 ? a + 360 : a;
+
}
+
+
export async function createAccount(player: LocalPlayer) {
+
const accounts = await getAccount();
+
accounts.push(trimAccount(player));
+
+
await Deno.writeTextFile('accounts.json', JSON.stringify(accounts, null, 2));
+
}