A Typescript server emulator for Box Critters, a defunct virtual world.
at main 3.4 kB view raw
1import { Server, Socket } from "https://deno.land/x/socket_io@0.2.0/mod.ts"; 2import z from "zod"; 3 4import * as world from "@/constants/world.ts"; 5import * as utils from "@/utils.ts"; 6import * as types from "@/types.ts"; 7 8import parties from "@/constants/parties.json" with { type: "json" }; 9 10export function listen( 11 _io: Server, 12 socket: Socket, 13 ctx: types.SocketHandlerContext, 14) { 15 socket.on("joinRoom", (roomId: string) => { 16 if (!ctx.localPlayer || !ctx.localCrumb) return; 17 18 if ( 19 z.object({ 20 roomId: z.enum(Object.keys(world.rooms) as [string, ...string[]]), 21 }).safeParse({ roomId: roomId }).success == false 22 ) return; 23 24 const _room = (world.rooms[roomId] || { default: null }).default; 25 if (!_room) return; 26 27 socket.leave(ctx.localCrumb._roomId); 28 socket.broadcast.in(ctx.localCrumb._roomId).emit("R", ctx.localCrumb); 29 30 const modEnabled = (ctx.localPlayer._mods || []).includes("roomExits"); 31 //@ts-ignore: Index type is correct 32 const correctExit = world.roomExits[ctx.localCrumb._roomId + "->" + roomId]; 33 if (modEnabled && correctExit) { 34 ctx.localPlayer.x = correctExit.x; 35 ctx.localPlayer.y = correctExit.y; 36 ctx.localPlayer.rotation = correctExit.r; 37 } 38 39 if (!modEnabled || !correctExit) { 40 ctx.localPlayer.x = _room.startX; 41 ctx.localPlayer.y = _room.startY; 42 ctx.localPlayer.rotation = _room.startR | 180; 43 } 44 45 ctx.localCrumb = utils.makeCrumb(ctx.localPlayer, roomId); 46 world.players[ctx.localPlayer.playerId] = ctx.localCrumb; 47 48 console.log("> " + ctx.localPlayer.nickname + ' joined "' + roomId + '"!'); 49 socket.join(roomId); 50 51 let playerCrumbs = Object.values(world.players).filter((crumb) => 52 crumb._roomId == roomId 53 ); 54 if (world.npcs[roomId]) { 55 playerCrumbs = [ 56 ...playerCrumbs, 57 ...world.npcs[roomId], 58 ]; 59 } 60 socket.emit("joinRoom", { 61 name: _room.name, 62 roomId: roomId, 63 playerCrumbs: playerCrumbs, 64 }); 65 66 socket.broadcast.in(ctx.localCrumb._roomId).emit("A", ctx.localCrumb); 67 }); 68 69 socket.on("switchParty", (partyId: string) => { 70 if (!ctx.localPlayer || !ctx.localCrumb) return; 71 72 if ( 73 z.object({ 74 partyId: z.enum(Object.keys(parties) as [string, ...string[]]), 75 }).strict().safeParse({ partyId: partyId }).success == false 76 ) return; 77 78 ctx.localPlayer._partyId = partyId; 79 socket.emit("switchParty"); 80 }); 81 82 socket.on("trigger", async () => { 83 if (!ctx.localPlayer || !ctx.localCrumb) return; 84 85 const activatedTrigger = await utils.getTrigger( 86 ctx.localPlayer, 87 ctx.localCrumb._roomId, 88 ctx.localPlayer._partyId, 89 ); 90 if (!activatedTrigger) return; 91 92 if (activatedTrigger.hasItems) { 93 for (const item of activatedTrigger.hasItems) { 94 if (!ctx.localPlayer.inventory.includes(item)) return; 95 } 96 } 97 98 if (activatedTrigger.grantItem) { 99 let items = activatedTrigger.grantItem; 100 if (typeof items == "string") items = [items]; 101 102 for (const item of items) { 103 if (!ctx.localPlayer.inventory.includes(item)) { 104 socket.emit("addItem", { itemId: item, showGUI: true }); 105 ctx.localPlayer.inventory.push(item); 106 } 107 } 108 } 109 110 if (activatedTrigger.addEgg) { 111 const egg = activatedTrigger.addEgg; 112 socket.emit("addEgg", egg); 113 ctx.localPlayer.eggs.push(egg); 114 } 115 }); 116}