├── server ├── .gitignore ├── package.json ├── default.json ├── index.js └── yarn.lock ├── .DS_Store ├── client ├── public │ ├── favicon.ico │ ├── models │ │ ├── Tablet.glb │ │ ├── Skyscraper.glb │ │ └── items │ │ │ ├── bear.glb │ │ │ ├── bench.glb │ │ │ ├── chair.glb │ │ │ ├── desk.glb │ │ │ ├── dryer.glb │ │ │ ├── laptop.glb │ │ │ ├── plant.glb │ │ │ ├── radio.glb │ │ │ ├── table.glb │ │ │ ├── washer.glb │ │ │ ├── bathtub.glb │ │ │ ├── rugRound.glb │ │ │ ├── speaker.glb │ │ │ ├── stoolBar.glb │ │ │ ├── toaster.glb │ │ │ ├── trashcan.glb │ │ │ ├── bedDouble.glb │ │ │ ├── bedSingle.glb │ │ │ ├── kitchenBar.glb │ │ │ ├── kitchenSink.glb │ │ │ ├── loungeChair.glb │ │ │ ├── loungeSofa.glb │ │ │ ├── plantSmall.glb │ │ │ ├── rugRounded.glb │ │ │ ├── rugSquare.glb │ │ │ ├── showerRound.glb │ │ │ ├── tableCoffee.glb │ │ │ ├── bathroomSink.glb │ │ │ ├── chairCushion.glb │ │ │ ├── chairRounded.glb │ │ │ ├── deskComputer.glb │ │ │ ├── kitchenFridge.glb │ │ │ ├── kitchenStove.glb │ │ │ ├── rugRectangle.glb │ │ │ ├── speakerSmall.glb │ │ │ ├── toiletSquare.glb │ │ │ ├── bathroomCabinet.glb │ │ │ ├── bathroomMirror.glb │ │ │ ├── benchCushionLow.glb │ │ │ ├── bookcaseOpenLow.glb │ │ │ ├── cabinetBedDrawer.glb │ │ │ ├── coatRackStanding.glb │ │ │ ├── kitchenBlender.glb │ │ │ ├── kitchenCabinet.glb │ │ │ ├── kitchenMicrowave.glb │ │ │ ├── lampRoundFloor.glb │ │ │ ├── lampRoundTable.glb │ │ │ ├── lampSquareFloor.glb │ │ │ ├── lampSquareTable.glb │ │ │ ├── loungeDesignSofa.glb │ │ │ ├── loungeSofaCorner.glb │ │ │ ├── stoolBarSquare.glb │ │ │ ├── tableCrossCloth.glb │ │ │ ├── televisionModern.glb │ │ │ ├── bookcaseClosedWide.glb │ │ │ ├── cardboardBoxClosed.glb │ │ │ ├── chairModernCushion.glb │ │ │ ├── kitchenFridgeLarge.glb │ │ │ ├── loungeSofaOttoman.glb │ │ │ ├── televisionVintage.glb │ │ │ ├── bathroomCabinetDrawer.glb │ │ │ ├── cabinetBedDrawerTable.glb │ │ │ ├── chairModernFrameCushion.glb │ │ │ ├── loungeDesignSofaCorner.glb │ │ │ ├── tableCoffeeGlassSquare.glb │ │ │ ├── kitchenCabinetCornerInner.glb │ │ │ └── kitchenCabinetCornerRound.glb │ ├── animations │ │ ├── M_Walk_001.glb │ │ ├── M_Dances_001.glb │ │ ├── M_Standing_Idle_001.glb │ │ └── M_Standing_Expressions_001.glb │ ├── textures │ │ └── venice_sunset_1k.hdr │ └── fonts │ │ └── Inter_Bold.json ├── postcss.config.js ├── vite.config.js ├── README.md ├── src │ ├── main.jsx │ ├── index.css │ ├── hooks │ │ └── useGrid.jsx │ ├── components │ │ ├── Loader.jsx │ │ ├── Skyscraper.jsx │ │ ├── LobbyAvatar.jsx │ │ ├── Tablet.jsx │ │ ├── Item.jsx │ │ ├── Shop.jsx │ │ ├── SocketManager.jsx │ │ ├── Experience.jsx │ │ ├── Lobby.jsx │ │ ├── Avatar.jsx │ │ ├── Room.jsx │ │ └── UI.jsx │ ├── App.jsx │ └── assets │ │ └── react.svg ├── tailwind.config.js ├── .gitignore ├── index.html └── package.json └── README.md /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | rooms.json -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/.DS_Store -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/public/models/Tablet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/Tablet.glb -------------------------------------------------------------------------------- /client/public/models/Skyscraper.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/Skyscraper.glb -------------------------------------------------------------------------------- /client/public/models/items/bear.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bear.glb -------------------------------------------------------------------------------- /client/public/models/items/bench.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bench.glb -------------------------------------------------------------------------------- /client/public/models/items/chair.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/chair.glb -------------------------------------------------------------------------------- /client/public/models/items/desk.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/desk.glb -------------------------------------------------------------------------------- /client/public/models/items/dryer.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/dryer.glb -------------------------------------------------------------------------------- /client/public/models/items/laptop.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/laptop.glb -------------------------------------------------------------------------------- /client/public/models/items/plant.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/plant.glb -------------------------------------------------------------------------------- /client/public/models/items/radio.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/radio.glb -------------------------------------------------------------------------------- /client/public/models/items/table.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/table.glb -------------------------------------------------------------------------------- /client/public/models/items/washer.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/washer.glb -------------------------------------------------------------------------------- /client/public/animations/M_Walk_001.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/animations/M_Walk_001.glb -------------------------------------------------------------------------------- /client/public/models/items/bathtub.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bathtub.glb -------------------------------------------------------------------------------- /client/public/models/items/rugRound.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/rugRound.glb -------------------------------------------------------------------------------- /client/public/models/items/speaker.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/speaker.glb -------------------------------------------------------------------------------- /client/public/models/items/stoolBar.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/stoolBar.glb -------------------------------------------------------------------------------- /client/public/models/items/toaster.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/toaster.glb -------------------------------------------------------------------------------- /client/public/models/items/trashcan.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/trashcan.glb -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Video Thumbnail](https://img.youtube.com/vi/73XOJlLhhZg/maxresdefault.jpg) 2 | 3 | [Video tutorial](https://youtu.be/73XOJlLhhZg) 4 | -------------------------------------------------------------------------------- /client/public/animations/M_Dances_001.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/animations/M_Dances_001.glb -------------------------------------------------------------------------------- /client/public/models/items/bedDouble.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bedDouble.glb -------------------------------------------------------------------------------- /client/public/models/items/bedSingle.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bedSingle.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenBar.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenBar.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenSink.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenSink.glb -------------------------------------------------------------------------------- /client/public/models/items/loungeChair.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/loungeChair.glb -------------------------------------------------------------------------------- /client/public/models/items/loungeSofa.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/loungeSofa.glb -------------------------------------------------------------------------------- /client/public/models/items/plantSmall.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/plantSmall.glb -------------------------------------------------------------------------------- /client/public/models/items/rugRounded.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/rugRounded.glb -------------------------------------------------------------------------------- /client/public/models/items/rugSquare.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/rugSquare.glb -------------------------------------------------------------------------------- /client/public/models/items/showerRound.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/showerRound.glb -------------------------------------------------------------------------------- /client/public/models/items/tableCoffee.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/tableCoffee.glb -------------------------------------------------------------------------------- /client/public/models/items/bathroomSink.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bathroomSink.glb -------------------------------------------------------------------------------- /client/public/models/items/chairCushion.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/chairCushion.glb -------------------------------------------------------------------------------- /client/public/models/items/chairRounded.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/chairRounded.glb -------------------------------------------------------------------------------- /client/public/models/items/deskComputer.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/deskComputer.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenFridge.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenFridge.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenStove.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenStove.glb -------------------------------------------------------------------------------- /client/public/models/items/rugRectangle.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/rugRectangle.glb -------------------------------------------------------------------------------- /client/public/models/items/speakerSmall.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/speakerSmall.glb -------------------------------------------------------------------------------- /client/public/models/items/toiletSquare.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/toiletSquare.glb -------------------------------------------------------------------------------- /client/public/textures/venice_sunset_1k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/textures/venice_sunset_1k.hdr -------------------------------------------------------------------------------- /client/public/models/items/bathroomCabinet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bathroomCabinet.glb -------------------------------------------------------------------------------- /client/public/models/items/bathroomMirror.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bathroomMirror.glb -------------------------------------------------------------------------------- /client/public/models/items/benchCushionLow.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/benchCushionLow.glb -------------------------------------------------------------------------------- /client/public/models/items/bookcaseOpenLow.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bookcaseOpenLow.glb -------------------------------------------------------------------------------- /client/public/models/items/cabinetBedDrawer.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/cabinetBedDrawer.glb -------------------------------------------------------------------------------- /client/public/models/items/coatRackStanding.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/coatRackStanding.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenBlender.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenBlender.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenCabinet.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenCabinet.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenMicrowave.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenMicrowave.glb -------------------------------------------------------------------------------- /client/public/models/items/lampRoundFloor.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/lampRoundFloor.glb -------------------------------------------------------------------------------- /client/public/models/items/lampRoundTable.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/lampRoundTable.glb -------------------------------------------------------------------------------- /client/public/models/items/lampSquareFloor.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/lampSquareFloor.glb -------------------------------------------------------------------------------- /client/public/models/items/lampSquareTable.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/lampSquareTable.glb -------------------------------------------------------------------------------- /client/public/models/items/loungeDesignSofa.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/loungeDesignSofa.glb -------------------------------------------------------------------------------- /client/public/models/items/loungeSofaCorner.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/loungeSofaCorner.glb -------------------------------------------------------------------------------- /client/public/models/items/stoolBarSquare.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/stoolBarSquare.glb -------------------------------------------------------------------------------- /client/public/models/items/tableCrossCloth.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/tableCrossCloth.glb -------------------------------------------------------------------------------- /client/public/models/items/televisionModern.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/televisionModern.glb -------------------------------------------------------------------------------- /client/public/animations/M_Standing_Idle_001.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/animations/M_Standing_Idle_001.glb -------------------------------------------------------------------------------- /client/public/models/items/bookcaseClosedWide.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bookcaseClosedWide.glb -------------------------------------------------------------------------------- /client/public/models/items/cardboardBoxClosed.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/cardboardBoxClosed.glb -------------------------------------------------------------------------------- /client/public/models/items/chairModernCushion.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/chairModernCushion.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenFridgeLarge.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenFridgeLarge.glb -------------------------------------------------------------------------------- /client/public/models/items/loungeSofaOttoman.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/loungeSofaOttoman.glb -------------------------------------------------------------------------------- /client/public/models/items/televisionVintage.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/televisionVintage.glb -------------------------------------------------------------------------------- /client/public/models/items/bathroomCabinetDrawer.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/bathroomCabinetDrawer.glb -------------------------------------------------------------------------------- /client/public/models/items/cabinetBedDrawerTable.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/cabinetBedDrawerTable.glb -------------------------------------------------------------------------------- /client/public/models/items/chairModernFrameCushion.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/chairModernFrameCushion.glb -------------------------------------------------------------------------------- /client/public/models/items/loungeDesignSofaCorner.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/loungeDesignSofaCorner.glb -------------------------------------------------------------------------------- /client/public/models/items/tableCoffeeGlassSquare.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/tableCoffeeGlassSquare.glb -------------------------------------------------------------------------------- /client/public/animations/M_Standing_Expressions_001.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/animations/M_Standing_Expressions_001.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenCabinetCornerInner.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenCabinetCornerInner.glb -------------------------------------------------------------------------------- /client/public/models/items/kitchenCabinetCornerRound.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wass08/r3f-sims-online-final/HEAD/client/public/models/items/kitchenCabinetCornerRound.glb -------------------------------------------------------------------------------- /client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # r3f-vite-starter 2 | A boilerplate to build R3F projects 3 | 4 | ``` 5 | yarn 6 | yarn dev 7 | ``` 8 | 9 | 10 | ![image](https://user-images.githubusercontent.com/6551176/221732091-23ee52cb-4150-42fa-b998-43628d7a6b0d.png) 11 | -------------------------------------------------------------------------------- /client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | fontFamily: { 6 | sans: ["Poppins", "sans-serif"], 7 | }, 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | }; 12 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "scripts": { 4 | "dev": "nodemon index.js --ignore '*.json'", 5 | "start": "NODE_ENV=production node index.js" 6 | }, 7 | "dependencies": { 8 | "pathfinding": "^0.4.18", 9 | "socket.io": "^4.7.2" 10 | }, 11 | "devDependencies": { 12 | "nodemon": "^3.0.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap"); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | html, 8 | body { 9 | width: 100%; 10 | height: 100%; 11 | } 12 | #root { 13 | width: 100%; 14 | height: 100%; 15 | will-change: transform; 16 | } 17 | 18 | body { 19 | margin: 0; 20 | } 21 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Wawa Hotel 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/src/hooks/useGrid.jsx: -------------------------------------------------------------------------------- 1 | import { useAtom } from "jotai"; 2 | import { mapAtom } from "../components/SocketManager"; 3 | 4 | import * as THREE from "three"; 5 | 6 | export const useGrid = () => { 7 | const [map] = useAtom(mapAtom); 8 | 9 | const vector3ToGrid = (vector3) => { 10 | return [ 11 | Math.floor(vector3.x * map.gridDivision), 12 | Math.floor(vector3.z * map.gridDivision), 13 | ]; 14 | }; 15 | 16 | const gridToVector3 = (gridPosition, width = 1, height = 1) => { 17 | return new THREE.Vector3( 18 | width / map.gridDivision / 2 + gridPosition[0] / map.gridDivision, 19 | 0, 20 | height / map.gridDivision / 2 + gridPosition[1] / map.gridDivision 21 | ); 22 | }; 23 | 24 | return { 25 | vector3ToGrid, 26 | gridToVector3, 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /client/src/components/Loader.jsx: -------------------------------------------------------------------------------- 1 | import { useProgress } from "@react-three/drei"; 2 | import { useAtom } from "jotai"; 3 | import { itemsAtom } from "./SocketManager"; 4 | 5 | export const Loader = ({ loaded }) => { 6 | const { progress } = useProgress(); 7 | const [items] = useAtom(itemsAtom); 8 | return ( 9 |
10 |
15 |

WAWA MANSION

16 |
17 |
21 |
22 | {progress < 100 &&

Loading resources...

} 23 |
24 |
25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "r3f-vite-starter", 3 | "private": true, 4 | "version": "1.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@react-three/drei": "9.75.0", 13 | "@react-three/fiber": "8.13.3", 14 | "@react-three/postprocessing": "^2.15.1", 15 | "@readyplayerme/react-avatar-creator": "^0.3.0", 16 | "@types/three": "0.152.1", 17 | "autoprefixer": "^10.4.15", 18 | "framer-motion": "^10.16.4", 19 | "framer-motion-3d": "^10.16.4", 20 | "jotai": "^2.2.3", 21 | "lodash": "^4.17.21", 22 | "postcss": "^8.4.28", 23 | "react": "^18.2.0", 24 | "react-dom": "^18.2.0", 25 | "socket.io-client": "^4.7.2", 26 | "three": "0.153.0", 27 | "three-stdlib": "^2.24.1" 28 | }, 29 | "devDependencies": { 30 | "@types/react": "^18.0.27", 31 | "@types/react-dom": "^18.0.10", 32 | "@vitejs/plugin-react": "^3.1.0", 33 | "tailwindcss": "^3.3.3", 34 | "vite": "^4.1.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/components/Skyscraper.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | Command: npx gltfjsx@6.2.3 public/models/Skyscraper.glb -o src/components/Skyscraper.jsx -r public 4 | */ 5 | 6 | import { useGLTF } from "@react-three/drei"; 7 | import React from "react"; 8 | 9 | export function Skyscraper(props) { 10 | const { nodes, materials } = useGLTF("/models/Skyscraper.glb"); 11 | return ( 12 | 13 | 18 | 23 | 28 | 33 | 38 | 43 | 44 | ); 45 | } 46 | 47 | useGLTF.preload("/models/Skyscraper.glb"); 48 | -------------------------------------------------------------------------------- /client/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Canvas } from "@react-three/fiber"; 2 | // import { EffectComposer, N8AO } from "@react-three/postprocessing"; 3 | import { ScrollControls, useProgress } from "@react-three/drei"; 4 | import { useAtom } from "jotai"; 5 | import { useEffect, useState } from "react"; 6 | import { Experience } from "./components/Experience"; 7 | import { Loader } from "./components/Loader"; 8 | import { 9 | SocketManager, 10 | itemsAtom, 11 | roomIDAtom, 12 | } from "./components/SocketManager"; 13 | import { UI } from "./components/UI"; 14 | 15 | function App() { 16 | const [roomID] = useAtom(roomIDAtom); 17 | 18 | const { progress } = useProgress(); 19 | const [loaded, setLoaded] = useState(false); 20 | const [items] = useAtom(itemsAtom); 21 | 22 | useEffect(() => { 23 | if (progress === 100 && items) { 24 | setLoaded(true); // As progress can go back to 0 when new resources are loaded, we need to make sure we don't fade out the UI when that happens 25 | } 26 | }, [progress]); 27 | return ( 28 | <> 29 | 30 | 37 | 38 | 39 | 40 | 41 | {/* Impact badly performances without a noticeable good result */} 42 | {/* 43 | 44 | */} 45 | 46 | 47 | {loaded && } 48 | 49 | ); 50 | } 51 | 52 | export default App; 53 | -------------------------------------------------------------------------------- /client/src/components/LobbyAvatar.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | Command: npx gltfjsx@6.2.3 public/models/Animated Woman.glb -o src/components/AnimatedWoman.jsx -r public 4 | */ 5 | 6 | import { useAnimations, useGLTF } from "@react-three/drei"; 7 | import { useAtom } from "jotai"; 8 | import React, { useEffect, useRef, useState } from "react"; 9 | import { avatarUrlAtom } from "./UI"; 10 | 11 | export function LobbyAvatar({ ...props }) { 12 | const [avatarUrl] = useAtom(avatarUrlAtom); 13 | const avatar = useRef(); 14 | const group = useRef(); 15 | const { scene } = useGLTF(avatarUrl); 16 | 17 | const { animations: waveAnimation } = useGLTF( 18 | "/animations/M_Standing_Expressions_001.glb" 19 | ); 20 | const { animations: idleAnimation } = useGLTF( 21 | "/animations/M_Standing_Idle_001.glb" 22 | ); 23 | 24 | const { actions } = useAnimations( 25 | [waveAnimation[0], idleAnimation[0]], 26 | avatar 27 | ); 28 | const [animation, setAnimation] = useState("M_Standing_Idle_001"); 29 | const [init, setInit] = useState(avatarUrl); 30 | 31 | useEffect(() => { 32 | actions[animation] 33 | .reset() 34 | .fadeIn(init === avatarUrl ? 0.32 : 0) 35 | .play(); 36 | setInit(avatarUrl); 37 | return () => actions[animation]?.fadeOut(0.32); 38 | }, [animation, avatarUrl]); 39 | 40 | const delayWave = (delay) => { 41 | setTimeout(() => { 42 | setAnimation("M_Standing_Expressions_001"); 43 | setTimeout(() => { 44 | setAnimation("M_Standing_Idle_001"); 45 | delayWave(3000); 46 | }, 6000); 47 | }, delay); 48 | }; 49 | 50 | useEffect(() => { 51 | delayWave(12); 52 | }, []); 53 | 54 | return ( 55 | 56 | 57 | 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /client/src/components/Tablet.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | Command: npx gltfjsx@6.2.3 public/models/Tablet.glb -o src/components/Tablet.jsx -r public 4 | */ 5 | 6 | import { useGLTF } from "@react-three/drei"; 7 | import React from "react"; 8 | 9 | export function Tablet(props) { 10 | const { nodes, materials } = useGLTF("/models/Tablet.glb"); 11 | return ( 12 | 13 | 17 | 21 | 25 | 29 | 33 | 37 | 41 | 45 | 49 | 53 | 57 | 61 | 65 | 66 | ); 67 | } 68 | 69 | useGLTF.preload("/models/Tablet.glb"); 70 | -------------------------------------------------------------------------------- /client/src/components/Item.jsx: -------------------------------------------------------------------------------- 1 | import { useCursor, useGLTF } from "@react-three/drei"; 2 | import { useAtom } from "jotai"; 3 | import { useEffect, useMemo, useState } from "react"; 4 | import { SkeletonUtils } from "three-stdlib"; 5 | import { useGrid } from "../hooks/useGrid"; 6 | import { mapAtom } from "./SocketManager"; 7 | import { buildModeAtom } from "./UI"; 8 | 9 | export const Item = ({ 10 | item, 11 | onClick, 12 | isDragging, 13 | dragPosition, 14 | canDrop, 15 | dragRotation, 16 | }) => { 17 | const { name, gridPosition, size, rotation: itemRotation } = item; 18 | 19 | const rotation = isDragging ? dragRotation : itemRotation; 20 | const { gridToVector3 } = useGrid(); 21 | const [map] = useAtom(mapAtom); 22 | const { scene } = useGLTF(`/models/items/${name}.glb`); 23 | // Skinned meshes cannot be re-used in threejs without cloning them 24 | const clone = useMemo(() => SkeletonUtils.clone(scene), [scene]); 25 | const width = rotation === 1 || rotation === 3 ? size[1] : size[0]; 26 | const height = rotation === 1 || rotation === 3 ? size[0] : size[1]; 27 | const [hover, setHover] = useState(false); 28 | const [buildMode] = useAtom(buildModeAtom); 29 | useCursor(buildMode ? hover : undefined); 30 | 31 | useEffect(() => { 32 | clone.traverse((child) => { 33 | if (child.isMesh) { 34 | child.castShadow = true; 35 | child.receiveShadow = true; 36 | } 37 | }); 38 | }, []); 39 | 40 | return ( 41 | setHover(true)} 49 | onPointerLeave={() => setHover(false)} 50 | > 51 | 52 | {isDragging && ( 53 | 54 | 57 | 62 | 63 | )} 64 | 65 | ); 66 | }; 67 | -------------------------------------------------------------------------------- /client/src/components/Shop.jsx: -------------------------------------------------------------------------------- 1 | import { useCursor, useGLTF, useScroll } from "@react-three/drei"; 2 | import { useFrame } from "@react-three/fiber"; 3 | import { useAtom } from "jotai"; 4 | import { useMemo, useRef, useState } from "react"; 5 | import { SkeletonUtils } from "three-stdlib"; 6 | import { useGrid } from "../hooks/useGrid"; 7 | import { itemsAtom, mapAtom } from "./SocketManager"; 8 | 9 | const ShopItem = ({ item, ...props }) => { 10 | const { name, size, rotation } = item; 11 | const { scene } = useGLTF(`/models/items/${name}.glb`); 12 | // Skinned meshes cannot be re-used in threejs without cloning them 13 | const clone = useMemo(() => SkeletonUtils.clone(scene), [scene]); 14 | const { gridToVector3 } = useGrid(); 15 | const [hover, setHover] = useState(false); 16 | useCursor(hover); 17 | return ( 18 | 19 | setHover(true)} 23 | onPointerLeave={() => setHover(false)} 24 | > 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | export const Shop = ({ onItemSelected }) => { 32 | const [items] = useAtom(itemsAtom); 33 | const [map] = useAtom(mapAtom); 34 | 35 | const maxX = useRef(0); 36 | 37 | const shopItems = useMemo(() => { 38 | let x = 0; 39 | return Object.values(items).map((item, index) => { 40 | const xPos = x; 41 | x += item.size[0] / map.gridDivision + 1; 42 | maxX.current = x; 43 | return ( 44 | { 49 | e.stopPropagation(); // Prevents the onPlaneClicked from firing just after we pick up an item 50 | onItemSelected(item); 51 | }} 52 | /> 53 | ); 54 | }); 55 | }, [items]); 56 | 57 | const shopContainer = useRef(); 58 | const scrollData = useScroll(); 59 | const scale = 0.42; 60 | useFrame(() => { 61 | shopContainer.current.position.x = 62 | -scrollData.offset * maxX.current * scale; 63 | }); 64 | return ( 65 | 66 | {shopItems} 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /client/src/components/SocketManager.jsx: -------------------------------------------------------------------------------- 1 | import { useGLTF } from "@react-three/drei"; 2 | import { atom, useAtom } from "jotai"; 3 | import { useEffect } from "react"; 4 | import { io } from "socket.io-client"; 5 | 6 | export const socket = io( 7 | import.meta.env.VITE_SERVER_URL || "http://localhost:3000" 8 | ); 9 | export const charactersAtom = atom([]); 10 | export const mapAtom = atom(null); 11 | export const userAtom = atom(null); 12 | export const itemsAtom = atom(null); 13 | export const roomIDAtom = atom(null); 14 | export const roomsAtom = atom([]); 15 | 16 | export const SocketManager = () => { 17 | const [_characters, setCharacters] = useAtom(charactersAtom); 18 | const [_map, setMap] = useAtom(mapAtom); 19 | const [_user, setUser] = useAtom(userAtom); 20 | const [items, setItems] = useAtom(itemsAtom); 21 | const [_rooms, setRooms] = useAtom(roomsAtom); 22 | 23 | useEffect(() => { 24 | if (!items) { 25 | return; 26 | } 27 | Object.values(items).forEach((item) => { 28 | useGLTF.preload(`/models/items/${item.name}.glb`); 29 | }); 30 | }, [items]); 31 | useEffect(() => { 32 | function onConnect() { 33 | console.log("connected"); 34 | } 35 | function onDisconnect() { 36 | console.log("disconnected"); 37 | } 38 | 39 | function onWelcome(value) { 40 | setRooms(value.rooms); 41 | setItems(value.items); 42 | } 43 | 44 | function onRoomJoined(value) { 45 | setMap(value.map); 46 | setUser(value.id); 47 | setCharacters(value.characters); 48 | } 49 | 50 | function onCharacters(value) { 51 | setCharacters(value); 52 | } 53 | 54 | function onMapUpdate(value) { 55 | setMap(value.map); 56 | setCharacters(value.characters); 57 | } 58 | 59 | function onRooms(value) { 60 | setRooms(value); 61 | } 62 | 63 | socket.on("connect", onConnect); 64 | socket.on("disconnect", onDisconnect); 65 | socket.on("roomJoined", onRoomJoined); 66 | socket.on("rooms", onRooms); 67 | socket.on("welcome", onWelcome); 68 | socket.on("characters", onCharacters); 69 | socket.on("mapUpdate", onMapUpdate); 70 | return () => { 71 | socket.off("connect", onConnect); 72 | socket.off("disconnect", onDisconnect); 73 | socket.off("roomJoined", onRoomJoined); 74 | socket.off("rooms", onRooms); 75 | socket.off("welcome", onWelcome); 76 | socket.off("characters", onCharacters); 77 | socket.off("mapUpdate", onMapUpdate); 78 | }; 79 | }, []); 80 | }; 81 | -------------------------------------------------------------------------------- /client/src/components/Experience.jsx: -------------------------------------------------------------------------------- 1 | import { CameraControls, Environment, Sky } from "@react-three/drei"; 2 | 3 | import { useFrame } from "@react-three/fiber"; 4 | import { useAtom } from "jotai"; 5 | import { useEffect, useRef } from "react"; 6 | import { Lobby } from "./Lobby"; 7 | import { Room } from "./Room"; 8 | import { mapAtom, roomIDAtom, userAtom } from "./SocketManager"; 9 | import { buildModeAtom, shopModeAtom } from "./UI"; 10 | export const Experience = ({ loaded }) => { 11 | const [buildMode] = useAtom(buildModeAtom); 12 | const [shopMode] = useAtom(shopModeAtom); 13 | 14 | const controls = useRef(); 15 | const [roomID] = useAtom(roomIDAtom); 16 | const [map] = useAtom(mapAtom); 17 | const [user] = useAtom(userAtom); 18 | 19 | useEffect(() => { 20 | // INITIAL POSITION 21 | if (!loaded) { 22 | controls.current.setPosition(0, 8, 2); 23 | controls.current.setTarget(0, 8, 0); 24 | return; 25 | } 26 | // LOBBY 27 | if (!roomID) { 28 | controls.current.setPosition(0, 8, 2); 29 | controls.current.setTarget(0, 8, 0); 30 | controls.current.setPosition(0, 0, 2, true); 31 | controls.current.setTarget(0, 0, 0, true); 32 | return; 33 | } 34 | if (shopMode) { 35 | controls.current.setPosition(0, 1, 6, true); 36 | controls.current.setTarget(0, 0, 0, true); 37 | return; 38 | } 39 | if (buildMode) { 40 | controls.current.setPosition(14, 10, 14, true); 41 | controls.current.setTarget(3.5, 0, 3.5, true); 42 | return; 43 | } 44 | 45 | // ROOM 46 | if (!buildMode && !shopMode && roomID) { 47 | controls.current.setPosition(0, 10, 5); 48 | controls.current.setTarget(0, 10, 0); 49 | return; 50 | } 51 | }, [buildMode, roomID, shopMode, loaded]); 52 | 53 | useFrame(({ scene }) => { 54 | if (!user) { 55 | return; 56 | } 57 | 58 | const character = scene.getObjectByName(`character-${user}`); 59 | if (!character) { 60 | return; 61 | } 62 | controls.current.setTarget( 63 | character.position.x, 64 | 0, 65 | character.position.z, 66 | true 67 | ); 68 | controls.current.setPosition( 69 | character.position.x + 8, 70 | character.position.y + 8, 71 | character.position.z + 8, 72 | true 73 | ); 74 | }); 75 | 76 | return ( 77 | <> 78 | 85 | 86 | 87 | 88 | 94 | 99 | 100 | 116 | {roomID && map && } 117 | {loaded && !roomID && } 118 | 119 | ); 120 | }; 121 | -------------------------------------------------------------------------------- /client/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/Lobby.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | AccumulativeShadows, 3 | Html, 4 | RandomizedLight, 5 | Text3D, 6 | useFont, 7 | } from "@react-three/drei"; 8 | import { motion } from "framer-motion-3d"; 9 | import { useAtom } from "jotai"; 10 | import { Suspense, useMemo, useRef } from "react"; 11 | import { LobbyAvatar } from "./LobbyAvatar"; 12 | import { Skyscraper } from "./Skyscraper"; 13 | import { mapAtom, roomIDAtom, roomsAtom, socket } from "./SocketManager"; 14 | import { Tablet } from "./Tablet"; 15 | import { avatarUrlAtom } from "./UI"; 16 | let firstLoad = true; 17 | export const Lobby = () => { 18 | const [rooms] = useAtom(roomsAtom); 19 | const [avatarUrl] = useAtom(avatarUrlAtom); 20 | const [_roomID, setRoomID] = useAtom(roomIDAtom); 21 | const [_map, setMap] = useAtom(mapAtom); 22 | const joinRoom = (roomId) => { 23 | socket.emit("joinRoom", roomId, { 24 | avatarUrl, 25 | }); 26 | setMap(null); 27 | setRoomID(roomId); 28 | }; 29 | 30 | const isMobile = window.innerWidth < 1024; 31 | 32 | const tablet = useRef(); 33 | 34 | const goldenRatio = Math.min(1, window.innerWidth / 1600); 35 | 36 | const accumulativeShadows = useMemo( 37 | () => ( 38 | 46 | 53 | 60 | 61 | ), 62 | [] 63 | ); 64 | const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); // ugly safari fix as transform position is buggy on it 65 | 66 | return ( 67 | 68 | { 86 | firstLoad = false; 87 | }} 88 | > 89 | 90 | 96 |
103 |
104 |

105 | WELCOME TO 106 |
107 | WAWA MANSION 108 |

109 |

110 | Please select a room to relax 111 |

112 | {rooms.map((room) => ( 113 |
joinRoom(room.id)} 117 | > 118 |

119 | {room.name} 120 |

121 |
122 |
0 ? "bg-green-500" : "bg-orange-500" 125 | }`} 126 | >
127 | {room.nbCharacters} people in this room 128 |
129 |
130 | ))} 131 |
132 |
133 | 134 |
135 | 136 | 147 | MANSION 148 | 149 | 150 | 151 | 162 | WAWA 163 | 164 | 165 | 166 | 167 | 168 | 169 | {accumulativeShadows} 170 | 171 | 177 | 178 |
179 | ); 180 | }; 181 | 182 | useFont.preload("/fonts/Inter_Bold.json"); 183 | -------------------------------------------------------------------------------- /client/src/components/Avatar.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | Auto-generated by: https://github.com/pmndrs/gltfjsx 3 | Command: npx gltfjsx@6.2.3 public/models/Animated Woman.glb -o src/components/AnimatedWoman.jsx -r public 4 | */ 5 | 6 | import { Html, useAnimations, useGLTF } from "@react-three/drei"; 7 | import { useFrame, useGraph } from "@react-three/fiber"; 8 | import { useAtom } from "jotai"; 9 | import React, { useEffect, useMemo, useRef, useState } from "react"; 10 | import { SkeletonUtils } from "three-stdlib"; 11 | import { useGrid } from "../hooks/useGrid"; 12 | import { socket, userAtom } from "./SocketManager"; 13 | 14 | import { motion } from "framer-motion-3d"; 15 | 16 | const MOVEMENT_SPEED = 4; 17 | 18 | export function Avatar({ 19 | id, 20 | avatarUrl = "https://models.readyplayer.me/64f0265b1db75f90dcfd9e2c.glb", 21 | ...props 22 | }) { 23 | const [chatMessage, setChatMessage] = useState(""); 24 | const position = useMemo(() => props.position, []); 25 | 26 | const avatar = useRef(); 27 | const [path, setPath] = useState(); 28 | const { gridToVector3 } = useGrid(); 29 | 30 | const group = useRef(); 31 | const { scene } = useGLTF(avatarUrl); 32 | // Skinned meshes cannot be re-used in threejs without cloning them 33 | const clone = useMemo(() => SkeletonUtils.clone(scene), [scene]); 34 | // useGraph creates two flat object collections for nodes and materials 35 | const { nodes } = useGraph(clone); 36 | 37 | const { animations: walkAnimation } = useGLTF("/animations/M_Walk_001.glb"); 38 | const { animations: danceAnimation } = useGLTF( 39 | "/animations/M_Dances_001.glb" 40 | ); 41 | const { animations: idleAnimation } = useGLTF( 42 | "/animations/M_Standing_Idle_001.glb" 43 | ); 44 | 45 | const { actions } = useAnimations( 46 | [walkAnimation[0], idleAnimation[0], danceAnimation[0]], 47 | avatar 48 | ); 49 | const [animation, setAnimation] = useState("M_Standing_Idle_001"); 50 | const [isDancing, setIsDancing] = useState(false); 51 | const [init, setInit] = useState(false); 52 | const [showChatBubble, setShowChatBubble] = useState(false); 53 | 54 | useEffect(() => { 55 | clone.traverse((child) => { 56 | if (child.isMesh) { 57 | child.castShadow = true; 58 | child.receiveShadow = true; 59 | } 60 | }); 61 | }, [clone]); 62 | 63 | useEffect(() => { 64 | actions[animation] 65 | .reset() 66 | .fadeIn(init ? 0.32 : 0) 67 | .play(); 68 | setInit(true); 69 | return () => actions[animation]?.fadeOut(0.32); 70 | }, [animation, avatarUrl]); 71 | 72 | useEffect(() => { 73 | function onPlayerDance(value) { 74 | if (value.id === id) { 75 | setIsDancing(true); 76 | } 77 | } 78 | function onPlayerMove(value) { 79 | if (value.id === id) { 80 | const path = []; 81 | value.path?.forEach((gridPosition) => { 82 | path.push(gridToVector3(gridPosition)); 83 | }); 84 | setPath(path); 85 | } 86 | } 87 | 88 | let chatMessageBubbleTimeout; 89 | function onPlayerChatMessage(value) { 90 | if (value.id === id) { 91 | setChatMessage(value.message); 92 | clearTimeout(chatMessageBubbleTimeout); 93 | setShowChatBubble(true); 94 | chatMessageBubbleTimeout = setTimeout(() => { 95 | setShowChatBubble(false); 96 | }, 3500); 97 | } 98 | } 99 | 100 | socket.on("playerMove", onPlayerMove); 101 | socket.on("playerDance", onPlayerDance); 102 | socket.on("playerChatMessage", onPlayerChatMessage); 103 | return () => { 104 | socket.off("playerDance", onPlayerDance); 105 | socket.off("playerMove", onPlayerMove); 106 | socket.off("playerChatMessage", onPlayerChatMessage); 107 | }; 108 | }, [id]); 109 | 110 | const [user] = useAtom(userAtom); 111 | 112 | useFrame((_state, delta) => { 113 | const hips = avatar.current.getObjectByName("Hips"); 114 | hips.position.set(0, hips.position.y, 0); 115 | if (path?.length && group.current.position.distanceTo(path[0]) > 0.1) { 116 | const direction = group.current.position 117 | .clone() 118 | .sub(path[0]) 119 | .normalize() 120 | .multiplyScalar(MOVEMENT_SPEED * delta); 121 | group.current.position.sub(direction); 122 | group.current.lookAt(path[0]); 123 | setAnimation("M_Walk_001"); 124 | setIsDancing(false); 125 | } else if (path?.length) { 126 | path.shift(); 127 | } else { 128 | if (isDancing) { 129 | setAnimation("M_Dances_001"); 130 | } else { 131 | setAnimation("M_Standing_Idle_001"); 132 | } 133 | } 134 | }); 135 | 136 | return ( 137 | 144 | 145 |
146 |

151 | {chatMessage} 152 |

153 |
154 | 155 | 173 | 174 | 175 |
176 | ); 177 | } 178 | 179 | useGLTF.preload( 180 | localStorage.getItem("avatarURL") || 181 | "https://models.readyplayer.me/64f0265b1db75f90dcfd9e2c.glb?meshlod=1&quality=medium" 182 | ); 183 | useGLTF.preload("/animations/M_Walk_001.glb"); 184 | useGLTF.preload("/animations/M_Standing_Idle_001.glb"); 185 | useGLTF.preload("/animations/M_Dances_001.glb"); 186 | useGLTF.preload("/animations/M_Standing_Expressions_001.glb"); 187 | -------------------------------------------------------------------------------- /client/src/components/Room.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | AccumulativeShadows, 3 | Grid, 4 | RandomizedLight, 5 | useCursor, 6 | } from "@react-three/drei"; 7 | 8 | import { useThree } from "@react-three/fiber"; 9 | import { atom, useAtom } from "jotai"; 10 | import { Suspense, useEffect, useMemo, useState } from "react"; 11 | import { useGrid } from "../hooks/useGrid"; 12 | import { Avatar } from "./Avatar"; 13 | import { Item } from "./Item"; 14 | import { Shop } from "./Shop"; 15 | import { charactersAtom, mapAtom, socket, userAtom } from "./SocketManager"; 16 | import { 17 | buildModeAtom, 18 | draggedItemAtom, 19 | draggedItemRotationAtom, 20 | shopModeAtom, 21 | } from "./UI"; 22 | 23 | export const roomItemsAtom = atom([]); 24 | 25 | export const Room = () => { 26 | const [buildMode] = useAtom(buildModeAtom); 27 | const [shopMode, setShopMode] = useAtom(shopModeAtom); 28 | const [characters] = useAtom(charactersAtom); 29 | const [map] = useAtom(mapAtom); 30 | const [items, setItems] = useAtom(roomItemsAtom); 31 | const [onFloor, setOnFloor] = useState(false); 32 | useCursor(onFloor); 33 | const { vector3ToGrid, gridToVector3 } = useGrid(); 34 | 35 | const scene = useThree((state) => state.scene); 36 | const [user] = useAtom(userAtom); 37 | 38 | useEffect(() => { 39 | setItems(map.items); 40 | }, [map]); 41 | 42 | const onPlaneClicked = (e) => { 43 | if (!buildMode) { 44 | const character = scene.getObjectByName(`character-${user}`); 45 | if (!character) { 46 | return; 47 | } 48 | socket.emit( 49 | "move", 50 | vector3ToGrid(character.position), 51 | vector3ToGrid(e.point) 52 | ); 53 | } else { 54 | if (draggedItem !== null) { 55 | if (canDrop) { 56 | setItems((prev) => { 57 | const newItems = [...prev]; 58 | delete newItems[draggedItem].tmp; 59 | newItems[draggedItem].gridPosition = vector3ToGrid(e.point); 60 | newItems[draggedItem].rotation = draggedItemRotation; 61 | return newItems; 62 | }); 63 | } 64 | setDraggedItem(null); 65 | } 66 | } 67 | }; 68 | 69 | const [draggedItem, setDraggedItem] = useAtom(draggedItemAtom); 70 | const [draggedItemRotation, setDraggedItemRotation] = useAtom( 71 | draggedItemRotationAtom 72 | ); 73 | const [dragPosition, setDragPosition] = useState([0, 0]); 74 | const [canDrop, setCanDrop] = useState(false); 75 | 76 | useEffect(() => { 77 | if (draggedItem === null) { 78 | setItems((prev) => prev.filter((item) => !item.tmp)); 79 | } 80 | }, [draggedItem]); 81 | 82 | useEffect(() => { 83 | if (draggedItem === null) { 84 | // FIXED: issue with 0 being falsy 85 | return; 86 | } 87 | const item = items[draggedItem]; 88 | const width = 89 | draggedItemRotation === 1 || draggedItemRotation === 3 90 | ? item.size[1] 91 | : item.size[0]; 92 | const height = 93 | draggedItemRotation === 1 || draggedItemRotation === 3 94 | ? item.size[0] 95 | : item.size[1]; 96 | 97 | let droppable = true; 98 | 99 | // check if item is in bounds 100 | if ( 101 | dragPosition[0] < 0 || 102 | dragPosition[0] + width > map.size[0] * map.gridDivision 103 | ) { 104 | droppable = false; 105 | } 106 | if ( 107 | dragPosition[1] < 0 || 108 | dragPosition[1] + height > map.size[1] * map.gridDivision 109 | ) { 110 | droppable = false; 111 | } 112 | // check if item is not colliding with other items 113 | if (!item.walkable && !item.wall) { 114 | items.forEach((otherItem, idx) => { 115 | // ignore self 116 | if (idx === draggedItem) { 117 | return; 118 | } 119 | 120 | // ignore wall & floor 121 | if (otherItem.walkable || otherItem.wall) { 122 | return; 123 | } 124 | 125 | // check item overlap 126 | const otherWidth = 127 | otherItem.rotation === 1 || otherItem.rotation === 3 128 | ? otherItem.size[1] 129 | : otherItem.size[0]; 130 | const otherHeight = 131 | otherItem.rotation === 1 || otherItem.rotation === 3 132 | ? otherItem.size[0] 133 | : otherItem.size[1]; 134 | if ( 135 | dragPosition[0] < otherItem.gridPosition[0] + otherWidth && 136 | dragPosition[0] + width > otherItem.gridPosition[0] && 137 | dragPosition[1] < otherItem.gridPosition[1] + otherHeight && 138 | dragPosition[1] + height > otherItem.gridPosition[1] 139 | ) { 140 | droppable = false; 141 | } 142 | }); 143 | } 144 | 145 | setCanDrop(droppable); 146 | }, [dragPosition, draggedItem, items, draggedItemRotation]); 147 | const state = useThree((state) => state); 148 | 149 | useEffect(() => { 150 | if (buildMode) { 151 | setItems(map?.items || []); 152 | } else { 153 | socket.emit("itemsUpdate", items); 154 | } 155 | }, [buildMode]); 156 | 157 | const onItemSelected = (item) => { 158 | setShopMode(false); 159 | 160 | setItems((prev) => [ 161 | ...prev, 162 | { 163 | ...item, 164 | gridPosition: [0, 0], 165 | tmp: true, 166 | }, 167 | ]); 168 | setDraggedItem(items.length); 169 | setDraggedItemRotation(item.rotation || 0); 170 | }; 171 | 172 | const accumulativeShadows = useMemo( 173 | () => ( 174 | 182 | 189 | 196 | 197 | ), 198 | [items] 199 | ); 200 | 201 | return ( 202 | <> 203 | {shopMode && } 204 | {!buildMode && !shopMode && accumulativeShadows} 205 | {!shopMode && 206 | (buildMode ? items : map.items).map((item, idx) => ( 207 | { 211 | if (buildMode) { 212 | setDraggedItem((prev) => (prev === null ? idx : prev)); 213 | setDraggedItemRotation(item.rotation || 0); 214 | } 215 | }} 216 | isDragging={draggedItem === idx} 217 | dragPosition={dragPosition} 218 | dragRotation={draggedItemRotation} 219 | canDrop={canDrop} 220 | /> 221 | ))} 222 | 223 | {!shopMode && ( 224 | setOnFloor(true)} 229 | onPointerLeave={() => setOnFloor(false)} 230 | onPointerMove={(e) => { 231 | if (!buildMode) { 232 | return; 233 | } 234 | const newPosition = vector3ToGrid(e.point); 235 | if ( 236 | !dragPosition || 237 | newPosition[0] !== dragPosition[0] || 238 | newPosition[1] !== dragPosition[1] 239 | ) { 240 | setDragPosition(newPosition); 241 | } 242 | }} 243 | position-x={map.size[0] / 2} 244 | position-z={map.size[1] / 2} 245 | receiveShadow 246 | > 247 | 248 | 249 | 250 | )} 251 | {(buildMode || shopMode) && ( 252 | 253 | )} 254 | {!buildMode && 255 | characters.map((character) => ( 256 | 257 | 258 | 266 | 267 | 268 | ))} 269 | 270 | ); 271 | }; 272 | -------------------------------------------------------------------------------- /server/default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "PARTY ROOM", 5 | "password": "WAWA", 6 | "items": [ 7 | { 8 | "name": "loungeSofaCorner", 9 | "size": [5, 5], 10 | "rotation": 3, 11 | "gridPosition": [0, 0] 12 | }, 13 | { 14 | "name": "bear", 15 | "size": [2, 1], 16 | "wall": true, 17 | "gridPosition": [2, 0], 18 | "rotation": 0 19 | }, 20 | { 21 | "name": "tableCoffeeGlassSquare", 22 | "size": [2, 2], 23 | "gridPosition": [5, 4], 24 | "rotation": 0 25 | }, 26 | { 27 | "name": "loungeSofa", 28 | "size": [5, 2], 29 | "rotation": 1, 30 | "gridPosition": [8, 2] 31 | }, 32 | { 33 | "name": "plant", 34 | "size": [1, 1], 35 | "gridPosition": [0, 6], 36 | "rotation": 0 37 | }, 38 | { 39 | "name": "chairCushion", 40 | "size": [1, 1], 41 | "rotation": 1, 42 | "gridPosition": [12, 4] 43 | }, 44 | { 45 | "name": "deskComputer", 46 | "size": [3, 2], 47 | "gridPosition": [10, 3], 48 | "rotation": 1 49 | }, 50 | { 51 | "name": "deskComputer", 52 | "size": [3, 2], 53 | "gridPosition": [3, 10], 54 | "rotation": 0 55 | }, 56 | { 57 | "name": "chairCushion", 58 | "size": [1, 1], 59 | "rotation": 0, 60 | "gridPosition": [4, 12] 61 | }, 62 | { 63 | "name": "lampSquareFloor", 64 | "size": [1, 1], 65 | "gridPosition": [13, 0], 66 | "rotation": 0 67 | }, 68 | { 69 | "name": "speaker", 70 | "size": [1, 1], 71 | "gridPosition": [7, 0], 72 | "rotation": 0 73 | }, 74 | { 75 | "name": "speakerSmall", 76 | "size": [1, 1], 77 | "rotation": 2, 78 | "gridPosition": [10, 6] 79 | }, 80 | { 81 | "name": "laptop", 82 | "size": [1, 1], 83 | "gridPosition": [6, 0], 84 | "rotation": 0 85 | }, 86 | { 87 | "name": "deskComputer", 88 | "size": [3, 2], 89 | "gridPosition": [8, 10], 90 | "rotation": 0 91 | }, 92 | { 93 | "name": "chairCushion", 94 | "size": [1, 1], 95 | "rotation": 0, 96 | "gridPosition": [9, 12] 97 | }, 98 | { 99 | "name": "speakerSmall", 100 | "size": [1, 1], 101 | "rotation": 3, 102 | "gridPosition": [0, 5] 103 | } 104 | ] 105 | }, 106 | { 107 | "id": 2, 108 | "name": "BATHROOM", 109 | "password": "WAWA", 110 | "items": [ 111 | { 112 | "name": "washer", 113 | "size": [2, 2], 114 | "gridPosition": [12, 0], 115 | "rotation": 0 116 | }, 117 | { 118 | "name": "toiletSquare", 119 | "size": [2, 2], 120 | "gridPosition": [6, 0], 121 | "rotation": 0 122 | }, 123 | { 124 | "name": "trashcan", 125 | "size": [1, 1], 126 | "gridPosition": [8, 0], 127 | "rotation": 0 128 | }, 129 | { 130 | "name": "bathroomCabinetDrawer", 131 | "size": [2, 2], 132 | "gridPosition": [4, 0], 133 | "rotation": 0 134 | }, 135 | { 136 | "name": "bathtub", 137 | "size": [4, 2], 138 | "gridPosition": [6, 7], 139 | "rotation": 0 140 | }, 141 | { 142 | "name": "bathroomMirror", 143 | "size": [2, 1], 144 | "wall": true, 145 | "gridPosition": [4, 0], 146 | "rotation": 0 147 | }, 148 | { 149 | "name": "bathroomCabinet", 150 | "size": [2, 1], 151 | "wall": true, 152 | "gridPosition": [12, 0], 153 | "rotation": 0 154 | }, 155 | { 156 | "name": "bathroomSink", 157 | "size": [2, 2], 158 | "gridPosition": [0, 7], 159 | "rotation": 1 160 | }, 161 | { 162 | "name": "toiletSquare", 163 | "size": [2, 2], 164 | "gridPosition": [0, 9], 165 | "rotation": 1 166 | }, 167 | { 168 | "name": "bathroomMirror", 169 | "size": [2, 1], 170 | "wall": true, 171 | "gridPosition": [0, 7], 172 | "rotation": 1 173 | }, 174 | { 175 | "name": "showerRound", 176 | "size": [2, 2], 177 | "gridPosition": [0, 0], 178 | "rotation": 0 179 | }, 180 | { 181 | "name": "rugRounded", 182 | "size": [6, 4], 183 | "walkable": true, 184 | "gridPosition": [5, 9], 185 | "rotation": 0 186 | }, 187 | { 188 | "name": "plant", 189 | "size": [1, 1], 190 | "gridPosition": [10, 8], 191 | "rotation": 0 192 | }, 193 | { 194 | "name": "plantSmall", 195 | "size": [1, 1], 196 | "gridPosition": [0, 11], 197 | "rotation": 0 198 | }, 199 | { 200 | "name": "bookcaseOpenLow", 201 | "size": [2, 1], 202 | "gridPosition": [5, 7], 203 | "rotation": 1 204 | }, 205 | { 206 | "name": "tableCoffeeGlassSquare", 207 | "size": [2, 2], 208 | "gridPosition": [0, 4], 209 | "rotation": 0 210 | } 211 | ] 212 | }, 213 | { 214 | "id": 3, 215 | "name": "KITCHEN", 216 | "password": "WAWA", 217 | "items": [ 218 | { 219 | "name": "plant", 220 | "size": [1, 1], 221 | "gridPosition": [1, 0], 222 | "rotation": 0 223 | }, 224 | { 225 | "name": "bookcaseOpenLow", 226 | "size": [2, 1], 227 | "gridPosition": [2, 0], 228 | "rotation": 0 229 | }, 230 | { 231 | "name": "tableCrossCloth", 232 | "size": [4, 2], 233 | "gridPosition": [4, 10], 234 | "rotation": 0 235 | }, 236 | { 237 | "name": "chairCushion", 238 | "size": [1, 1], 239 | "rotation": 2, 240 | "gridPosition": [6, 9] 241 | }, 242 | { 243 | "name": "chairCushion", 244 | "size": [1, 1], 245 | "rotation": 0, 246 | "gridPosition": [6, 12] 247 | }, 248 | { 249 | "name": "chairCushion", 250 | "size": [1, 1], 251 | "rotation": 0, 252 | "gridPosition": [4, 12] 253 | }, 254 | { 255 | "name": "chairCushion", 256 | "size": [1, 1], 257 | "rotation": 2, 258 | "gridPosition": [4, 9] 259 | }, 260 | { 261 | "name": "kitchenMicrowave", 262 | "size": [1, 1], 263 | "gridPosition": [5, 0], 264 | "rotation": 0 265 | }, 266 | { 267 | "name": "kitchenSink", 268 | "size": [2, 2], 269 | "gridPosition": [10, 0], 270 | "rotation": 0 271 | }, 272 | { 273 | "name": "dryer", 274 | "size": [2, 2], 275 | "gridPosition": [8, 0], 276 | "rotation": 0 277 | }, 278 | { 279 | "name": "kitchenBlender", 280 | "size": [1, 1], 281 | "gridPosition": [6, 0], 282 | "rotation": 1 283 | }, 284 | { 285 | "name": "kitchenCabinetCornerRound", 286 | "size": [2, 2], 287 | "gridPosition": [12, 4], 288 | "rotation": 0 289 | }, 290 | { 291 | "name": "kitchenCabinetCornerInner", 292 | "size": [2, 2], 293 | "gridPosition": [12, 0], 294 | "rotation": 0 295 | }, 296 | { 297 | "name": "kitchenCabinet", 298 | "size": [2, 2], 299 | "gridPosition": [12, 2], 300 | "rotation": 3 301 | }, 302 | { 303 | "name": "plantSmall", 304 | "size": [1, 1], 305 | "gridPosition": [0, 0], 306 | "rotation": 0 307 | }, 308 | { 309 | "name": "tableCrossCloth", 310 | "size": [4, 2], 311 | "gridPosition": [8, 10], 312 | "rotation": 0 313 | }, 314 | { 315 | "name": "benchCushionLow", 316 | "size": [2, 1], 317 | "gridPosition": [9, 12], 318 | "rotation": 0 319 | }, 320 | { 321 | "name": "benchCushionLow", 322 | "size": [2, 1], 323 | "gridPosition": [9, 9], 324 | "rotation": 0 325 | }, 326 | { 327 | "name": "kitchenFridge", 328 | "size": [2, 1], 329 | "rotation": 3, 330 | "gridPosition": [0, 3] 331 | }, 332 | { 333 | "name": "kitchenFridgeLarge", 334 | "size": [2, 1], 335 | "rotation": 1, 336 | "gridPosition": [0, 12] 337 | }, 338 | { 339 | "name": "kitchenStove", 340 | "size": [2, 2], 341 | "gridPosition": [0, 6], 342 | "rotation": 1 343 | }, 344 | { "name": "bench", "size": [2, 1], "rotation": 3, "gridPosition": [0, 9] } 345 | ] 346 | }, 347 | { 348 | "id": 4, 349 | "name": "COSY ROOM", 350 | "password": "WAWA", 351 | "items": [ 352 | { 353 | "name": "tableCoffee", 354 | "size": [4, 2], 355 | "gridPosition": [5, 4], 356 | "rotation": 0 357 | }, 358 | { 359 | "name": "rugRectangle", 360 | "size": [8, 4], 361 | "walkable": true, 362 | "gridPosition": [3, 3], 363 | "rotation": 0 364 | }, 365 | { 366 | "name": "plant", 367 | "size": [1, 1], 368 | "gridPosition": [4, 0], 369 | "rotation": 0 370 | }, 371 | { 372 | "name": "speaker", 373 | "size": [1, 1], 374 | "gridPosition": [9, 0], 375 | "rotation": 0 376 | }, 377 | { 378 | "name": "loungeChair", 379 | "size": [2, 2], 380 | "rotation": 2, 381 | "gridPosition": [6, 0] 382 | }, 383 | { 384 | "name": "plantSmall", 385 | "size": [1, 1], 386 | "gridPosition": [3, 0], 387 | "rotation": 0 388 | }, 389 | { 390 | "name": "plant", 391 | "size": [1, 1], 392 | "gridPosition": [1, 0], 393 | "rotation": 0 394 | }, 395 | { 396 | "name": "plantSmall", 397 | "size": [1, 1], 398 | "gridPosition": [0, 0], 399 | "rotation": 0 400 | }, 401 | { 402 | "name": "plantSmall", 403 | "size": [1, 1], 404 | "gridPosition": [1, 1], 405 | "rotation": 0 406 | }, 407 | { 408 | "name": "plant", 409 | "size": [1, 1], 410 | "gridPosition": [0, 1], 411 | "rotation": 0 412 | }, 413 | { 414 | "name": "plantSmall", 415 | "size": [1, 1], 416 | "gridPosition": [0, 2], 417 | "rotation": 0 418 | }, 419 | { 420 | "name": "televisionModern", 421 | "size": [4, 2], 422 | "rotation": 0, 423 | "gridPosition": [5, 9] 424 | }, 425 | { 426 | "name": "lampRoundFloor", 427 | "size": [1, 1], 428 | "gridPosition": [11, 0], 429 | "rotation": 0 430 | } 431 | ] 432 | } 433 | ] 434 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import pathfinding from "pathfinding"; 3 | import { Server } from "socket.io"; 4 | 5 | const origin = process.env.CLIENT_URL || "http://localhost:5173"; 6 | const io = new Server({ 7 | cors: { 8 | origin, 9 | }, 10 | }); 11 | 12 | io.listen(3000); 13 | 14 | console.log("Server started on port 3000, allowed cors origin: " + origin); 15 | 16 | // PATHFINDING UTILS 17 | 18 | const finder = new pathfinding.AStarFinder({ 19 | allowDiagonal: true, 20 | dontCrossCorners: true, 21 | }); 22 | 23 | const findPath = (room, start, end) => { 24 | const gridClone = room.grid.clone(); 25 | const path = finder.findPath(start[0], start[1], end[0], end[1], gridClone); 26 | return path; 27 | }; 28 | 29 | const updateGrid = (room) => { 30 | // RESET GRID FOR ROOM 31 | for (let x = 0; x < room.size[0] * room.gridDivision; x++) { 32 | for (let y = 0; y < room.size[1] * room.gridDivision; y++) { 33 | room.grid.setWalkableAt(x, y, true); 34 | } 35 | } 36 | 37 | room.items.forEach((item) => { 38 | if (item.walkable || item.wall) { 39 | return; 40 | } 41 | const width = 42 | item.rotation === 1 || item.rotation === 3 ? item.size[1] : item.size[0]; 43 | const height = 44 | item.rotation === 1 || item.rotation === 3 ? item.size[0] : item.size[1]; 45 | for (let x = 0; x < width; x++) { 46 | for (let y = 0; y < height; y++) { 47 | room.grid.setWalkableAt( 48 | item.gridPosition[0] + x, 49 | item.gridPosition[1] + y, 50 | false 51 | ); 52 | } 53 | } 54 | }); 55 | }; 56 | 57 | // ROOMS MANAGEMENT 58 | const rooms = []; 59 | 60 | const loadRooms = async () => { 61 | let data; 62 | try { 63 | data = fs.readFileSync("rooms.json", "utf8"); 64 | } catch (ex) { 65 | console.log("No rooms.json file found, using default file"); 66 | try { 67 | data = fs.readFileSync("default.json", "utf8"); 68 | } catch (ex) { 69 | console.log("No default.json file found, exiting"); 70 | process.exit(1); 71 | } 72 | } 73 | data = JSON.parse(data); 74 | data.forEach((roomItem) => { 75 | const room = { 76 | ...roomItem, 77 | size: [7, 7], // HARDCODED FOR SIMPLICITY PURPOSES 78 | gridDivision: 2, 79 | characters: [], 80 | }; 81 | room.grid = new pathfinding.Grid( 82 | room.size[0] * room.gridDivision, 83 | room.size[1] * room.gridDivision 84 | ); 85 | updateGrid(room); 86 | rooms.push(room); 87 | }); 88 | }; 89 | 90 | loadRooms(); 91 | 92 | // UTILS 93 | 94 | const generateRandomPosition = (room) => { 95 | // TO AVOID INFINITE LOOP WE LIMIT TO 100, BEST WOULD BE TO CHECK IF THERE IS ENOUGH SPACE LEFT 🤭 96 | for (let i = 0; i < 100; i++) { 97 | const x = Math.floor(Math.random() * room.size[0] * room.gridDivision); 98 | const y = Math.floor(Math.random() * room.size[1] * room.gridDivision); 99 | if (room.grid.isWalkableAt(x, y)) { 100 | return [x, y]; 101 | } 102 | } 103 | }; 104 | 105 | // SOCKET MANAGEMENT 106 | 107 | io.on("connection", (socket) => { 108 | try { 109 | let room = null; 110 | let character = null; 111 | 112 | socket.emit("welcome", { 113 | rooms: rooms.map((room) => ({ 114 | id: room.id, 115 | name: room.name, 116 | nbCharacters: room.characters.length, 117 | })), 118 | items, 119 | }); 120 | 121 | socket.on("joinRoom", (roomId, opts) => { 122 | room = rooms.find((room) => room.id === roomId); 123 | if (!room) { 124 | return; 125 | } 126 | socket.join(room.id); 127 | character = { 128 | id: socket.id, 129 | session: parseInt(Math.random() * 1000), 130 | position: generateRandomPosition(room), 131 | avatarUrl: opts.avatarUrl, 132 | }; 133 | room.characters.push(character); 134 | 135 | socket.emit("roomJoined", { 136 | map: { 137 | gridDivision: room.gridDivision, 138 | size: room.size, 139 | items: room.items, 140 | }, 141 | characters: room.characters, 142 | id: socket.id, 143 | }); 144 | onRoomUpdate(); 145 | }); 146 | 147 | const onRoomUpdate = () => { 148 | io.to(room.id).emit("characters", room.characters); 149 | io.emit( 150 | "rooms", 151 | rooms.map((room) => ({ 152 | id: room.id, 153 | name: room.name, 154 | nbCharacters: room.characters.length, 155 | })) 156 | ); 157 | }; 158 | 159 | socket.on("leaveRoom", () => { 160 | if (!room) { 161 | return; 162 | } 163 | socket.leave(room.id); 164 | room.characters.splice( 165 | room.characters.findIndex((character) => character.id === socket.id), 166 | 1 167 | ); 168 | onRoomUpdate(); 169 | room = null; 170 | }); 171 | 172 | socket.on("characterAvatarUpdate", (avatarUrl) => { 173 | character.avatarUrl = avatarUrl; 174 | io.to(room.id).emit("characters", room.characters); 175 | }); 176 | 177 | socket.on("move", (from, to) => { 178 | const path = findPath(room, from, to); 179 | if (!path) { 180 | return; 181 | } 182 | character.position = from; 183 | character.path = path; 184 | io.to(room.id).emit("playerMove", character); 185 | }); 186 | 187 | socket.on("dance", () => { 188 | io.to(room.id).emit("playerDance", { 189 | id: socket.id, 190 | }); 191 | }); 192 | 193 | socket.on("chatMessage", (message) => { 194 | io.to(room.id).emit("playerChatMessage", { 195 | id: socket.id, 196 | message, 197 | }); 198 | }); 199 | 200 | socket.on("passwordCheck", (password) => { 201 | if (password === room.password) { 202 | socket.emit("passwordCheckSuccess"); 203 | character.canUpdateRoom = true; 204 | } else { 205 | socket.emit("passwordCheckFail"); 206 | } 207 | }); 208 | 209 | socket.on("itemsUpdate", async (items) => { 210 | if (!character.canUpdateRoom) { 211 | return; 212 | } 213 | if (!items || items.length === 0) { 214 | return; // security 215 | } 216 | room.items = items; 217 | updateGrid(room); 218 | room.characters.forEach((character) => { 219 | character.path = []; 220 | character.position = generateRandomPosition(room); 221 | }); 222 | io.to(room.id).emit("mapUpdate", { 223 | map: { 224 | gridDivision: room.gridDivision, 225 | size: room.size, 226 | items: room.items, 227 | }, 228 | characters: room.characters, 229 | }); 230 | 231 | fs.writeFileSync("rooms.json", JSON.stringify(rooms, null, 2)); 232 | }); 233 | 234 | socket.on("disconnect", () => { 235 | console.log("User disconnected"); 236 | if (room) { 237 | room.characters.splice( 238 | room.characters.findIndex((character) => character.id === socket.id), 239 | 1 240 | ); 241 | onRoomUpdate(); 242 | room = null; 243 | } 244 | }); 245 | } catch (ex) { 246 | console.log(ex); // Big try catch to avoid crashing the server (best would be to handle all errors properly...) 247 | } 248 | }); 249 | 250 | // ROOMS 251 | 252 | // SHOP ITEMS 253 | const items = { 254 | washer: { 255 | name: "washer", 256 | size: [2, 2], 257 | }, 258 | toiletSquare: { 259 | name: "toiletSquare", 260 | size: [2, 2], 261 | }, 262 | trashcan: { 263 | name: "trashcan", 264 | size: [1, 1], 265 | }, 266 | bathroomCabinetDrawer: { 267 | name: "bathroomCabinetDrawer", 268 | size: [2, 2], 269 | }, 270 | bathtub: { 271 | name: "bathtub", 272 | size: [4, 2], 273 | }, 274 | bathroomMirror: { 275 | name: "bathroomMirror", 276 | size: [2, 1], 277 | wall: true, 278 | }, 279 | bathroomCabinet: { 280 | name: "bathroomCabinet", 281 | size: [2, 1], 282 | wall: true, 283 | }, 284 | bathroomSink: { 285 | name: "bathroomSink", 286 | size: [2, 2], 287 | }, 288 | showerRound: { 289 | name: "showerRound", 290 | size: [2, 2], 291 | }, 292 | tableCoffee: { 293 | name: "tableCoffee", 294 | size: [4, 2], 295 | }, 296 | loungeSofaCorner: { 297 | name: "loungeSofaCorner", 298 | size: [5, 5], 299 | rotation: 2, 300 | }, 301 | bear: { 302 | name: "bear", 303 | size: [2, 1], 304 | wall: true, 305 | }, 306 | loungeSofaOttoman: { 307 | name: "loungeSofaOttoman", 308 | size: [2, 2], 309 | }, 310 | tableCoffeeGlassSquare: { 311 | name: "tableCoffeeGlassSquare", 312 | size: [2, 2], 313 | }, 314 | loungeDesignSofaCorner: { 315 | name: "loungeDesignSofaCorner", 316 | size: [5, 5], 317 | rotation: 2, 318 | }, 319 | loungeDesignSofa: { 320 | name: "loungeDesignSofa", 321 | size: [5, 2], 322 | rotation: 2, 323 | }, 324 | loungeSofa: { 325 | name: "loungeSofa", 326 | size: [5, 2], 327 | rotation: 2, 328 | }, 329 | bookcaseOpenLow: { 330 | name: "bookcaseOpenLow", 331 | size: [2, 1], 332 | }, 333 | bookcaseClosedWide: { 334 | name: "bookcaseClosedWide", 335 | size: [3, 1], 336 | rotation: 2, 337 | }, 338 | bedSingle: { 339 | name: "bedSingle", 340 | size: [3, 6], 341 | rotation: 2, 342 | }, 343 | bench: { 344 | name: "bench", 345 | size: [2, 1], 346 | rotation: 2, 347 | }, 348 | bedDouble: { 349 | name: "bedDouble", 350 | size: [5, 5], 351 | rotation: 2, 352 | }, 353 | benchCushionLow: { 354 | name: "benchCushionLow", 355 | size: [2, 1], 356 | }, 357 | loungeChair: { 358 | name: "loungeChair", 359 | size: [2, 2], 360 | rotation: 2, 361 | }, 362 | cabinetBedDrawer: { 363 | name: "cabinetBedDrawer", 364 | size: [1, 1], 365 | rotation: 2, 366 | }, 367 | cabinetBedDrawerTable: { 368 | name: "cabinetBedDrawerTable", 369 | size: [1, 1], 370 | rotation: 2, 371 | }, 372 | table: { 373 | name: "table", 374 | size: [4, 2], 375 | }, 376 | tableCrossCloth: { 377 | name: "tableCrossCloth", 378 | size: [4, 2], 379 | }, 380 | plant: { 381 | name: "plant", 382 | size: [1, 1], 383 | }, 384 | plantSmall: { 385 | name: "plantSmall", 386 | size: [1, 1], 387 | }, 388 | rugRounded: { 389 | name: "rugRounded", 390 | size: [6, 4], 391 | walkable: true, 392 | }, 393 | rugRound: { 394 | name: "rugRound", 395 | size: [4, 4], 396 | walkable: true, 397 | }, 398 | rugSquare: { 399 | name: "rugSquare", 400 | size: [4, 4], 401 | walkable: true, 402 | }, 403 | rugRectangle: { 404 | name: "rugRectangle", 405 | size: [8, 4], 406 | walkable: true, 407 | }, 408 | televisionVintage: { 409 | name: "televisionVintage", 410 | size: [4, 2], 411 | rotation: 2, 412 | }, 413 | televisionModern: { 414 | name: "televisionModern", 415 | size: [4, 2], 416 | rotation: 2, 417 | }, 418 | kitchenFridge: { 419 | name: "kitchenFridge", 420 | size: [2, 1], 421 | rotation: 2, 422 | }, 423 | kitchenFridgeLarge: { 424 | name: "kitchenFridgeLarge", 425 | size: [2, 1], 426 | }, 427 | kitchenBar: { 428 | name: "kitchenBar", 429 | size: [2, 1], 430 | }, 431 | kitchenCabinetCornerRound: { 432 | name: "kitchenCabinetCornerRound", 433 | size: [2, 2], 434 | }, 435 | kitchenCabinetCornerInner: { 436 | name: "kitchenCabinetCornerInner", 437 | size: [2, 2], 438 | }, 439 | kitchenCabinet: { 440 | name: "kitchenCabinet", 441 | size: [2, 2], 442 | }, 443 | kitchenBlender: { 444 | name: "kitchenBlender", 445 | size: [1, 1], 446 | }, 447 | dryer: { 448 | name: "dryer", 449 | size: [2, 2], 450 | }, 451 | chairCushion: { 452 | name: "chairCushion", 453 | size: [1, 1], 454 | rotation: 2, 455 | }, 456 | chair: { 457 | name: "chair", 458 | size: [1, 1], 459 | rotation: 2, 460 | }, 461 | deskComputer: { 462 | name: "deskComputer", 463 | size: [3, 2], 464 | }, 465 | desk: { 466 | name: "desk", 467 | size: [3, 2], 468 | }, 469 | chairModernCushion: { 470 | name: "chairModernCushion", 471 | size: [1, 1], 472 | rotation: 2, 473 | }, 474 | chairModernFrameCushion: { 475 | name: "chairModernFrameCushion", 476 | size: [1, 1], 477 | rotation: 2, 478 | }, 479 | kitchenMicrowave: { 480 | name: "kitchenMicrowave", 481 | size: [1, 1], 482 | }, 483 | coatRackStanding: { 484 | name: "coatRackStanding", 485 | size: [1, 1], 486 | }, 487 | kitchenSink: { 488 | name: "kitchenSink", 489 | size: [2, 2], 490 | }, 491 | lampRoundFloor: { 492 | name: "lampRoundFloor", 493 | size: [1, 1], 494 | }, 495 | lampRoundTable: { 496 | name: "lampRoundTable", 497 | size: [1, 1], 498 | }, 499 | lampSquareFloor: { 500 | name: "lampSquareFloor", 501 | size: [1, 1], 502 | }, 503 | lampSquareTable: { 504 | name: "lampSquareTable", 505 | size: [1, 1], 506 | }, 507 | toaster: { 508 | name: "toaster", 509 | size: [1, 1], 510 | }, 511 | kitchenStove: { 512 | name: "kitchenStove", 513 | size: [2, 2], 514 | }, 515 | laptop: { 516 | name: "laptop", 517 | size: [1, 1], 518 | }, 519 | radio: { 520 | name: "radio", 521 | size: [1, 1], 522 | }, 523 | speaker: { 524 | name: "speaker", 525 | size: [1, 1], 526 | }, 527 | speakerSmall: { 528 | name: "speakerSmall", 529 | size: [1, 1], 530 | rotation: 2, 531 | }, 532 | stoolBar: { 533 | name: "stoolBar", 534 | size: [1, 1], 535 | }, 536 | stoolBarSquare: { 537 | name: "stoolBarSquare", 538 | size: [1, 1], 539 | }, 540 | }; 541 | -------------------------------------------------------------------------------- /client/src/components/UI.jsx: -------------------------------------------------------------------------------- 1 | import { atom, useAtom } from "jotai"; 2 | import { useEffect, useRef, useState } from "react"; 3 | 4 | import { AvatarCreator } from "@readyplayerme/react-avatar-creator"; 5 | import { motion } from "framer-motion"; 6 | import { roomItemsAtom } from "./Room"; 7 | import { roomIDAtom, socket } from "./SocketManager"; 8 | export const buildModeAtom = atom(false); 9 | export const shopModeAtom = atom(false); 10 | export const draggedItemAtom = atom(null); 11 | export const draggedItemRotationAtom = atom(0); 12 | 13 | export const avatarUrlAtom = atom( 14 | localStorage.getItem("avatarURL") || 15 | "https://models.readyplayer.me/64f0265b1db75f90dcfd9e2c.glb?meshlod=1&quality=medium" 16 | ); 17 | 18 | const PasswordInput = ({ onClose, onSuccess }) => { 19 | const [password, setPassword] = useState(""); 20 | const [error, setError] = useState(""); 21 | // TODO: To make things properly we should have a loading state 😊 22 | 23 | const checkPassword = () => { 24 | socket.emit("passwordCheck", password); 25 | }; 26 | 27 | useEffect(() => { 28 | socket.on("passwordCheckSuccess", () => { 29 | onSuccess(); 30 | onClose(); 31 | }); 32 | socket.on("passwordCheckFail", () => { 33 | setError("Wrong password"); 34 | }); 35 | return () => { 36 | socket.off("passwordCheckSuccess"); 37 | socket.off("passwordCheckFail"); 38 | }; 39 | }); 40 | 41 | return ( 42 |
43 |
47 |
48 |

Password

49 | setPassword(e.target.value)} 55 | /> 56 |
57 | 63 | {error &&

{error}

} 64 |
65 |
66 |
67 | ); 68 | }; 69 | 70 | export const UI = () => { 71 | const [buildMode, setBuildMode] = useAtom(buildModeAtom); 72 | const [shopMode, setShopMode] = useAtom(shopModeAtom); 73 | const [draggedItem, setDraggedItem] = useAtom(draggedItemAtom); 74 | const [draggedItemRotation, setDraggedItemRotation] = useAtom( 75 | draggedItemRotationAtom 76 | ); 77 | const [_roomItems, setRoomItems] = useAtom(roomItemsAtom); 78 | const [passwordMode, setPasswordMode] = useState(false); 79 | const [avatarMode, setAvatarMode] = useState(false); 80 | const [avatarUrl, setAvatarUrl] = useAtom(avatarUrlAtom); 81 | const [roomID, setRoomID] = useAtom(roomIDAtom); 82 | const [passwordCorrectForRoom, setPasswordCorrectForRoom] = useState(false); 83 | const leaveRoom = () => { 84 | socket.emit("leaveRoom"); 85 | setRoomID(null); 86 | setBuildMode(false); 87 | setShopMode(false); 88 | }; 89 | useEffect(() => { 90 | setPasswordCorrectForRoom(false); // PS: this is an ugly shortcut 91 | }, [roomID]); 92 | 93 | const ref = useRef(); 94 | const [chatMessage, setChatMessage] = useState(""); 95 | const sendChatMessage = () => { 96 | if (chatMessage.length > 0) { 97 | socket.emit("chatMessage", chatMessage); 98 | setChatMessage(""); 99 | } 100 | }; 101 | 102 | return ( 103 | <> 104 | 110 | {avatarMode && ( 111 | { 115 | let newAvatarUrl = 116 | event.data.url === avatarUrl.split("?")[0] 117 | ? event.data.url.split("?")[0] + "?" + new Date().getTime() 118 | : event.data.url; 119 | newAvatarUrl += 120 | (newAvatarUrl.includes("?") ? "&" : "?") + 121 | "meshlod=1&quality=medium"; 122 | setAvatarUrl(newAvatarUrl); 123 | localStorage.setItem("avatarURL", newAvatarUrl); 124 | if (roomID) { 125 | socket.emit("characterAvatarUpdate", newAvatarUrl); 126 | } 127 | setAvatarMode(false); 128 | }} 129 | /> 130 | )} 131 | {passwordMode && ( 132 | setPasswordMode(false)} 134 | onSuccess={() => { 135 | setBuildMode(true); 136 | setPasswordCorrectForRoom(true); 137 | }} 138 | /> 139 | )} 140 |
141 | {roomID && !shopMode && !buildMode && ( 142 |
143 | { 148 | if (e.key === "Enter") { 149 | sendChatMessage(); 150 | } 151 | }} 152 | value={chatMessage} 153 | onChange={(e) => setChatMessage(e.target.value)} 154 | /> 155 | 174 |
175 | )} 176 |
177 | {roomID && !shopMode && !buildMode && ( 178 | 184 | )} 185 | {/* BACK */} 186 | {(buildMode || shopMode) && draggedItem === null && ( 187 | 208 | )} 209 | {/* AVATAR */} 210 | {!buildMode && !shopMode && ( 211 | 230 | )} 231 | {/* DANCE */} 232 | {roomID && !buildMode && !shopMode && ( 233 | 252 | )} 253 | {/* BUILD */} 254 | {roomID && !buildMode && !shopMode && ( 255 | 280 | )} 281 | {/* SHOP */} 282 | {buildMode && !shopMode && draggedItem === null && ( 283 | 302 | )} 303 | 304 | {/* ROTATE */} 305 | {buildMode && !shopMode && draggedItem !== null && ( 306 | 329 | )} 330 | {/* CANCEL */} 331 | {buildMode && !shopMode && draggedItem !== null && ( 332 | 351 | )} 352 | {/* REMOVE ITEM */} 353 | {buildMode && !shopMode && draggedItem !== null && ( 354 | 380 | )} 381 |
382 |
383 |
384 | 385 | ); 386 | }; 387 | -------------------------------------------------------------------------------- /client/public/fonts/Inter_Bold.json: -------------------------------------------------------------------------------- 1 | {"glyphs":{"A":{"ha":1039,"x_min":33,"x_max":1005,"o":"m 262 0 l 33 0 l 382 1010 l 657 1010 l 1005 0 l 776 0 l 523 779 l 515 779 l 262 0 m 248 397 l 788 397 l 788 230 l 248 230 l 248 397 z "},"B":{"ha":918,"x_min":88,"x_max":867,"o":"m 88 0 l 88 1010 l 492 1010 q 678 977 604 1010 q 790 886 753 944 q 828 751 828 827 q 804 647 828 692 q 739 574 780 603 q 645 534 698 546 l 645 524 q 753 492 703 522 q 836 408 804 462 q 867 279 867 354 q 827 136 867 199 q 710 36 788 73 q 519 0 633 0 l 88 0 m 301 175 l 475 175 q 606 209 565 175 q 647 300 647 243 q 626 374 647 342 q 569 425 606 406 q 480 443 532 443 l 301 443 l 301 175 m 301 587 l 460 587 q 538 603 504 587 q 592 647 572 618 q 612 716 612 676 q 573 804 612 770 q 464 837 535 837 l 301 837 l 301 587 z "},"C":{"ha":1045,"x_min":70,"x_max":979,"o":"m 979 656 l 763 656 q 739 731 757 698 q 692 787 721 764 q 626 823 663 810 q 545 835 589 835 q 409 796 467 835 q 319 684 351 758 q 287 505 287 610 q 319 324 287 397 q 409 213 351 250 q 544 175 468 175 q 624 186 587 175 q 689 220 660 198 q 737 273 718 242 q 763 345 756 305 l 979 344 q 937 210 971 275 q 848 95 904 146 q 715 16 792 45 q 541 -14 638 -14 q 299 47 405 -14 q 131 224 193 109 q 70 505 70 340 q 132 786 70 670 q 301 963 194 902 q 541 1024 407 1024 q 704 999 628 1024 q 837 927 779 975 q 932 812 895 880 q 979 656 969 744 z "},"D":{"ha":1010,"x_min":88,"x_max":940,"o":"m 446 0 l 88 0 l 88 1010 l 449 1010 q 711 950 601 1010 q 881 776 821 889 q 940 506 940 663 q 881 235 940 348 q 710 61 821 121 q 446 0 600 0 m 301 183 l 437 183 q 597 217 532 183 q 694 322 661 251 q 727 506 727 393 q 694 689 727 618 q 597 794 661 760 q 437 827 532 827 l 301 827 l 301 183 z "},"E":{"ha":851,"x_min":88,"x_max":770,"o":"m 88 0 l 88 1010 l 768 1010 l 768 834 l 301 834 l 301 593 l 733 593 l 733 417 l 301 417 l 301 176 l 770 176 l 770 0 l 88 0 z "},"F":{"ha":812,"x_min":88,"x_max":757,"o":"m 88 0 l 88 1010 l 757 1010 l 757 834 l 301 834 l 301 593 l 712 593 l 712 417 l 301 417 l 301 0 l 88 0 z "},"G":{"ha":1056,"x_min":70,"x_max":984,"o":"m 758 684 q 728 747 747 720 q 683 795 710 775 q 622 825 656 814 q 546 835 587 835 q 410 797 469 835 q 319 685 352 758 q 287 506 287 612 q 319 327 287 400 q 409 214 351 253 q 548 175 468 175 q 672 201 620 175 q 751 274 724 227 q 778 386 778 322 l 822 380 l 561 380 l 561 541 l 984 541 l 984 413 q 928 185 984 280 q 773 38 872 89 q 547 -14 674 -14 q 297 49 405 -14 q 130 228 190 112 q 70 504 70 344 q 106 723 70 627 q 206 887 142 820 q 357 989 271 954 q 543 1024 442 1024 q 702 999 628 1024 q 834 929 776 974 q 928 821 891 883 q 975 684 964 759 l 758 684 z "},"H":{"ha":1036,"x_min":88,"x_max":948,"o":"m 88 0 l 88 1010 l 301 1010 l 301 593 l 735 593 l 735 1010 l 948 1010 l 948 0 l 735 0 l 735 417 l 301 417 l 301 0 l 88 0 z "},"I":{"ha":390,"x_min":88,"x_max":301,"o":"m 301 1010 l 301 0 l 88 0 l 88 1010 l 301 1010 z "},"J":{"ha":792,"x_min":32,"x_max":703,"o":"m 492 1010 l 703 1010 l 703 306 q 660 136 703 208 q 539 25 616 64 q 359 -14 461 -14 q 193 18 267 -14 q 75 117 119 51 q 33 284 32 183 l 245 284 q 262 216 246 244 q 305 172 277 187 q 369 157 332 157 q 436 174 409 157 q 478 224 464 191 q 492 306 492 257 l 492 1010 z "},"K":{"ha":958,"x_min":88,"x_max":939,"o":"m 88 0 l 88 1010 l 301 1010 l 301 565 l 315 565 l 678 1010 l 934 1010 l 559 558 l 939 0 l 683 0 l 406 415 l 301 287 l 301 0 l 88 0 z "},"L":{"ha":788,"x_min":88,"x_max":734,"o":"m 88 0 l 88 1010 l 301 1010 l 301 176 l 734 176 l 734 0 l 88 0 z "},"M":{"ha":1271,"x_min":88,"x_max":1183,"o":"m 88 1010 l 351 1010 l 629 331 l 641 331 l 919 1010 l 1183 1010 l 1183 0 l 976 0 l 976 657 l 967 657 l 706 5 l 565 5 l 303 660 l 295 660 l 295 0 l 88 0 l 88 1010 z "},"N":{"ha":1021,"x_min":88,"x_max":933,"o":"m 933 1010 l 933 0 l 748 0 l 309 636 l 301 636 l 301 0 l 88 0 l 88 1010 l 275 1010 l 711 375 l 720 375 l 720 1010 l 933 1010 z "},"O":{"ha":1086,"x_min":70,"x_max":1016,"o":"m 1016 505 q 954 224 1016 340 q 784 47 891 108 q 543 -14 677 -14 q 301 47 408 -14 q 132 224 194 109 q 70 505 70 340 q 132 786 70 670 q 301 963 194 902 q 543 1024 408 1024 q 784 963 677 1024 q 954 786 891 902 q 1016 505 1016 670 m 799 505 q 768 686 799 612 q 678 797 736 759 q 543 835 620 835 q 408 797 466 835 q 318 686 350 759 q 287 505 287 612 q 318 325 287 398 q 408 213 350 251 q 543 175 466 175 q 678 213 620 175 q 768 325 736 251 q 799 505 799 398 z "},"P":{"ha":900,"x_min":88,"x_max":848,"o":"m 88 0 l 88 1010 l 486 1010 q 682 966 601 1010 q 806 846 763 923 q 848 668 848 768 q 805 490 848 567 q 679 370 762 413 q 481 327 597 327 l 227 327 l 227 499 l 446 499 q 548 520 508 499 q 608 580 588 542 q 628 668 628 618 q 608 756 628 718 q 548 815 588 794 q 445 836 508 836 l 301 836 l 301 0 l 88 0 z "},"Q":{"ha":1087,"x_min":70,"x_max":1016,"o":"m 469 351 l 651 351 l 742 234 l 832 129 l 1001 -83 l 801 -83 l 685 60 l 625 145 l 469 351 m 1016 505 q 954 224 1016 340 q 784 47 891 108 q 543 -14 677 -14 q 301 47 408 -14 q 132 224 194 109 q 70 505 70 340 q 132 786 70 670 q 301 963 194 902 q 543 1024 408 1024 q 784 963 677 1024 q 954 786 891 902 q 1016 505 1016 670 m 799 505 q 768 686 799 612 q 678 797 736 759 q 543 835 620 835 q 408 797 466 835 q 318 686 350 759 q 287 505 287 612 q 318 325 287 398 q 408 213 350 251 q 543 175 466 175 q 678 213 620 175 q 768 325 736 251 q 799 505 799 398 z "},"R":{"ha":911,"x_min":88,"x_max":884,"o":"m 88 0 l 88 1010 l 486 1010 q 682 969 601 1010 q 806 854 763 929 q 848 680 848 780 q 805 507 848 580 q 679 397 762 435 q 481 358 597 358 l 214 358 l 214 530 l 446 530 q 548 546 508 530 q 608 597 588 563 q 628 680 628 630 q 608 765 628 730 q 548 818 588 799 q 445 836 507 836 l 301 836 l 301 0 l 88 0 m 633 460 l 884 0 l 649 0 l 403 460 l 633 460 z "},"S":{"ha":909,"x_min":55,"x_max":854,"o":"m 636 720 q 585 812 630 779 q 463 845 540 845 q 375 831 411 845 q 320 791 339 816 q 301 732 301 765 q 312 685 300 705 q 347 650 325 665 q 399 625 369 636 q 461 607 428 615 l 551 585 q 673 546 618 571 q 768 485 728 521 q 831 400 809 449 q 854 289 854 352 q 807 128 854 196 q 673 23 761 60 q 462 -14 585 -14 q 249 23 340 -14 q 108 134 159 61 q 55 317 58 208 l 262 317 q 291 233 265 266 q 361 182 317 199 q 460 165 404 165 q 554 181 514 165 q 617 224 594 196 q 639 289 639 253 q 619 346 639 323 q 561 386 599 369 q 467 415 522 402 l 357 443 q 155 540 229 474 q 82 718 81 606 q 131 878 81 810 q 267 985 181 947 q 465 1024 354 1024 q 661 985 577 1024 q 792 878 745 947 q 840 720 839 810 l 636 720 z "},"T":{"ha":928,"x_min":49,"x_max":879,"o":"m 49 834 l 49 1010 l 879 1010 l 879 834 l 570 834 l 570 0 l 359 0 l 359 834 l 49 834 z "},"U":{"ha":1013,"x_min":88,"x_max":925,"o":"m 712 1010 l 925 1010 l 925 354 q 873 161 925 244 q 726 32 820 78 q 507 -14 632 -14 q 287 32 381 -14 q 140 161 192 78 q 88 354 88 244 l 88 1010 l 301 1010 l 301 372 q 327 270 301 315 q 399 199 352 225 q 507 174 445 174 q 615 199 569 174 q 686 270 661 225 q 712 372 712 315 l 712 1010 z "},"V":{"ha":1039,"x_min":33,"x_max":1005,"o":"m 270 1010 l 514 243 l 524 243 l 768 1010 l 1005 1010 l 657 0 l 382 0 l 33 1010 l 270 1010 z "},"W":{"ha":1439,"x_min":26,"x_max":1413,"o":"m 315 0 l 26 1010 l 259 1010 l 427 308 l 435 308 l 619 1010 l 819 1010 l 1003 307 l 1012 307 l 1179 1010 l 1413 1010 l 1124 0 l 915 0 l 723 660 l 715 660 l 523 0 l 315 0 z "},"X":{"ha":992,"x_min":40,"x_max":952,"o":"m 288 1010 l 492 666 l 500 666 l 704 1010 l 945 1010 l 637 505 l 952 0 l 707 0 l 500 345 l 492 345 l 285 0 l 40 0 l 356 505 l 46 1010 l 288 1010 z "},"Y":{"ha":1006,"x_min":29,"x_max":978,"o":"m 29 1010 l 268 1010 l 498 575 l 508 575 l 738 1010 l 978 1010 l 609 357 l 609 0 l 397 0 l 397 357 l 29 1010 z "},"Z":{"ha":926,"x_min":76,"x_max":850,"o":"m 77 0 l 77 127 l 581 834 l 76 834 l 76 1010 l 849 1010 l 849 883 l 345 176 l 850 176 l 850 0 l 77 0 z "},"a":{"ha":806,"x_min":48,"x_max":727,"o":"m 300 -14 q 171 11 227 -14 q 81 87 114 36 q 48 212 48 137 q 72 318 48 275 q 135 387 95 361 q 226 426 175 413 q 333 445 277 439 q 439 458 399 452 q 498 476 480 464 q 516 513 516 489 l 516 516 q 487 588 516 563 q 404 614 458 614 q 314 589 347 614 q 269 527 280 564 l 75 543 q 133 662 90 612 q 245 740 177 713 q 405 767 314 767 q 526 753 468 767 q 629 707 584 738 q 700 627 674 676 q 727 511 727 579 l 727 0 l 527 0 l 527 105 l 521 105 q 472 43 503 70 q 399 1 442 16 q 300 -14 356 -14 m 360 131 q 442 149 406 131 q 498 199 477 168 q 518 271 518 231 l 518 351 q 491 340 508 345 q 453 330 474 334 q 410 323 432 326 q 372 317 389 320 q 307 300 335 312 q 264 268 280 288 q 249 218 249 248 q 280 153 249 176 q 360 131 312 131 z "},"b":{"ha":882,"x_min":88,"x_max":827,"o":"m 88 0 l 88 1010 l 298 1010 l 298 630 l 304 630 q 345 693 318 661 q 414 746 371 725 q 520 767 456 767 q 673 724 603 767 q 785 595 743 681 q 827 378 827 508 q 786 164 827 251 q 675 32 745 77 q 519 -12 606 -12 q 416 8 458 -12 q 346 59 373 28 q 304 121 319 90 l 295 121 l 295 0 l 88 0 m 293 379 q 312 261 293 311 q 366 183 331 211 q 453 155 402 155 q 540 183 504 155 q 594 262 575 212 q 612 379 612 313 q 594 494 612 444 q 540 572 576 544 q 453 600 505 600 q 366 573 401 600 q 312 496 331 546 q 293 379 293 446 z "},"c":{"ha":816,"x_min":53,"x_max":764,"o":"m 426 -15 q 226 35 310 -15 q 98 173 143 84 q 53 376 53 261 q 98 580 53 492 q 227 718 144 668 q 425 767 310 767 q 599 731 524 767 q 717 630 673 695 q 764 477 760 565 l 566 477 q 522 569 558 534 q 429 604 486 604 q 344 577 380 604 q 287 501 307 551 q 267 379 267 450 q 287 255 267 306 q 343 178 307 205 q 429 151 380 151 q 493 166 465 151 q 541 209 522 181 q 566 278 560 238 l 764 278 q 717 125 760 191 q 601 22 675 59 q 426 -15 527 -15 z "},"d":{"ha":882,"x_min":56,"x_max":794,"o":"m 363 -12 q 207 32 277 -12 q 96 164 137 77 q 56 378 56 251 q 98 595 56 508 q 209 724 140 681 q 363 767 279 767 q 469 746 426 767 q 538 693 511 725 q 578 630 564 661 l 584 630 l 584 1010 l 794 1010 l 794 0 l 587 0 l 587 121 l 578 121 q 536 59 563 90 q 467 8 509 28 q 363 -12 424 -12 m 430 155 q 516 183 480 155 q 570 261 551 211 q 589 379 589 311 q 570 496 589 446 q 516 573 551 546 q 430 600 480 600 q 342 572 378 600 q 289 494 307 544 q 270 379 270 444 q 289 262 270 313 q 343 183 307 212 q 430 155 378 155 z "},"e":{"ha":830,"x_min":53,"x_max":777,"o":"m 429 -15 q 228 33 312 -15 q 99 168 144 80 q 53 375 53 255 q 99 581 53 492 q 227 718 144 669 q 421 767 309 767 q 561 744 496 767 q 674 672 625 720 q 750 552 723 624 q 777 383 777 479 l 777 325 l 137 325 l 137 455 l 579 455 q 559 536 579 501 q 505 591 540 571 q 424 611 470 611 q 340 589 376 611 q 282 530 303 567 q 261 448 261 493 l 261 325 q 282 227 261 268 q 341 164 303 186 q 433 142 380 142 q 497 152 468 142 q 546 182 526 162 q 578 230 567 201 l 772 217 q 712 95 758 147 q 595 14 666 43 q 429 -15 523 -15 z "},"f":{"ha":536,"x_min":24,"x_max":528,"o":"m 491 758 l 491 600 l 24 600 l 24 758 l 491 758 m 131 0 l 131 812 q 163 949 131 895 q 252 1030 195 1003 q 379 1057 308 1057 q 468 1050 428 1057 q 528 1037 508 1043 l 490 879 q 460 886 478 883 q 423 890 442 890 q 359 868 377 890 q 340 808 340 847 l 340 0 l 131 0 z "},"g":{"ha":879,"x_min":56,"x_max":796,"o":"m 425 -300 q 250 -272 323 -300 q 135 -196 178 -244 q 79 -89 92 -148 l 273 -63 q 301 -105 282 -85 q 352 -137 321 -125 q 430 -149 384 -149 q 543 -116 499 -149 q 588 -4 588 -82 l 588 134 l 579 134 q 538 74 565 103 q 467 29 510 46 q 363 11 423 11 q 208 51 278 11 q 97 172 138 90 q 56 380 56 254 q 98 595 56 509 q 209 725 140 682 q 363 767 279 767 q 469 746 426 767 q 538 693 512 725 q 579 630 565 661 l 587 630 l 587 758 l 796 758 l 796 -7 q 748 -169 796 -104 q 617 -267 701 -234 q 425 -300 534 -300 m 430 169 q 516 194 480 169 q 570 267 551 219 q 589 381 589 315 q 570 497 589 448 q 516 573 551 546 q 430 600 480 600 q 342 572 378 600 q 289 495 307 545 q 270 381 270 446 q 289 268 270 316 q 343 195 307 220 q 430 169 378 169 z "},"h":{"ha":867,"x_min":84,"x_max":786,"o":"m 294 438 l 294 0 l 84 0 l 84 1010 l 288 1010 l 288 624 l 297 624 q 380 729 323 691 q 523 767 437 767 q 661 733 602 767 q 753 635 720 699 q 785 482 786 571 l 785 0 l 575 0 l 575 445 q 540 554 576 515 q 440 593 504 593 q 364 575 397 593 q 313 522 331 556 q 294 438 294 487 z "},"i":{"ha":378,"x_min":76,"x_max":303,"o":"m 84 0 l 84 758 l 294 758 l 294 0 l 84 0 m 189 855 q 109 887 143 855 q 76 962 76 918 q 109 1036 76 1005 q 189 1068 143 1068 q 270 1036 236 1068 q 303 962 303 1005 q 270 887 303 918 q 189 855 236 855 z "},"j":{"ha":378,"x_min":-36,"x_max":302,"o":"m 84 758 l 294 758 l 294 -37 q 259 -180 294 -125 q 161 -259 225 -234 q 7 -284 96 -284 q -14 -284 -4 -284 q -36 -283 -24 -284 l -36 -119 q -21 -120 -27 -119 q -8 -120 -15 -120 q 63 -98 43 -120 q 84 -34 84 -77 l 84 758 m 188 855 q 109 887 142 855 q 75 962 75 918 q 109 1036 75 1005 q 188 1068 142 1068 q 269 1036 236 1068 q 302 962 302 1005 q 269 887 302 918 q 188 855 236 855 z "},"k":{"ha":808,"x_min":84,"x_max":803,"o":"m 274 218 l 275 470 l 305 470 l 548 758 l 789 758 l 463 377 l 413 377 l 274 218 m 84 0 l 84 1010 l 294 1010 l 294 0 l 84 0 m 557 0 l 334 330 l 474 478 l 803 0 l 557 0 z "},"l":{"ha":378,"x_min":84,"x_max":294,"o":"m 294 1010 l 294 0 l 84 0 l 84 1010 l 294 1010 z "},"m":{"ha":1267,"x_min":84,"x_max":1185,"o":"m 84 0 l 84 758 l 284 758 l 284 624 l 293 624 q 372 729 317 690 q 504 767 427 767 q 637 729 582 767 q 710 624 691 690 l 718 624 q 802 728 741 689 q 946 767 863 767 q 1118 700 1052 767 q 1185 509 1185 633 l 1185 0 l 975 0 l 975 468 q 942 563 975 531 q 858 594 908 594 q 768 558 800 594 q 736 463 736 522 l 736 0 l 533 0 l 533 472 q 501 561 533 528 q 417 594 469 594 q 354 577 382 594 q 310 528 327 559 q 294 455 294 497 l 294 0 l 84 0 z "},"n":{"ha":864,"x_min":84,"x_max":782,"o":"m 294 438 l 294 0 l 84 0 l 84 758 l 284 758 l 284 624 l 293 624 q 377 729 318 690 q 521 767 436 767 q 658 733 600 767 q 750 635 717 698 q 782 482 782 571 l 782 0 l 572 0 l 572 445 q 537 554 573 514 q 437 593 501 593 q 363 575 395 593 q 312 522 330 556 q 294 438 294 487 z "},"o":{"ha":852,"x_min":53,"x_max":799,"o":"m 426 -15 q 228 34 311 -15 q 99 171 144 83 q 53 376 53 259 q 99 581 53 493 q 228 718 144 669 q 426 767 311 767 q 625 718 541 767 q 754 581 708 669 q 799 376 799 493 q 754 171 799 259 q 625 34 708 83 q 426 -15 541 -15 m 427 148 q 514 178 479 148 q 567 259 549 208 q 585 377 585 311 q 567 495 585 443 q 514 577 549 547 q 427 607 479 607 q 339 577 374 607 q 285 495 303 547 q 267 377 267 443 q 285 259 267 311 q 339 178 303 208 q 427 148 374 148 z "},"p":{"ha":878,"x_min":84,"x_max":823,"o":"m 84 -284 l 84 758 l 291 758 l 291 630 l 300 630 q 341 693 314 661 q 410 746 367 725 q 516 767 452 767 q 669 724 599 767 q 781 595 739 681 q 823 378 823 508 q 782 164 823 251 q 672 32 741 77 q 515 -12 602 -12 q 412 8 454 -12 q 342 59 369 28 q 300 121 315 90 l 294 121 l 294 -284 l 84 -284 m 290 379 q 308 261 290 311 q 363 183 327 211 q 449 155 398 155 q 536 183 500 155 q 590 262 571 212 q 608 379 608 313 q 590 494 608 444 q 536 572 572 544 q 449 600 501 600 q 362 573 398 600 q 308 496 327 546 q 290 379 290 446 z "},"q":{"ha":878,"x_min":56,"x_max":794,"o":"m 584 -284 l 584 121 l 578 121 q 536 59 563 90 q 467 8 509 28 q 363 -12 424 -12 q 207 32 277 -12 q 96 164 137 77 q 56 378 56 251 q 98 595 56 508 q 209 724 140 681 q 363 767 279 767 q 469 746 426 767 q 538 693 511 725 q 578 630 564 661 l 587 630 l 587 758 l 794 758 l 794 -284 l 584 -284 m 430 155 q 516 183 480 155 q 570 261 551 211 q 589 379 589 311 q 570 496 589 446 q 516 573 551 546 q 430 600 480 600 q 342 572 378 600 q 289 494 307 544 q 270 379 270 444 q 289 262 270 313 q 343 183 307 212 q 430 155 378 155 z "},"r":{"ha":568,"x_min":84,"x_max":544,"o":"m 84 0 l 84 758 l 288 758 l 288 625 l 295 625 q 365 732 316 696 q 477 768 414 768 q 511 766 493 768 q 544 761 530 764 l 544 575 q 503 582 529 579 q 455 586 476 586 q 372 566 408 586 q 315 510 336 546 q 294 429 294 475 l 294 0 l 84 0 z "},"s":{"ha":780,"x_min":50,"x_max":731,"o":"m 714 542 l 522 530 q 501 574 517 554 q 458 606 484 594 q 395 618 432 618 q 313 598 346 618 q 279 544 279 578 q 301 498 279 516 q 375 468 323 479 l 512 440 q 677 367 623 417 q 731 235 731 317 q 688 104 731 160 q 568 17 644 48 q 394 -15 493 -15 q 155 48 244 -15 q 50 219 66 111 l 257 230 q 302 161 266 184 q 395 137 338 137 q 484 158 450 137 q 518 214 517 180 q 494 261 517 242 q 421 289 470 279 l 290 315 q 125 392 179 337 q 71 532 71 447 q 110 657 71 605 q 222 739 150 710 q 392 767 294 767 q 618 707 535 767 q 714 542 700 646 z "},"t":{"ha":540,"x_min":31,"x_max":501,"o":"m 487 758 l 487 600 l 31 600 l 31 758 l 487 758 m 134 939 l 344 939 l 344 233 q 353 188 344 204 q 378 165 362 172 q 415 159 394 159 q 445 162 430 159 q 468 166 460 164 l 501 9 q 456 -2 485 4 q 387 -10 428 -9 q 254 10 311 -13 q 165 82 197 33 q 134 205 134 131 l 134 939 z "},"u":{"ha":864,"x_min":84,"x_max":780,"o":"m 570 323 l 570 758 l 780 758 l 780 0 l 578 0 l 578 138 l 570 138 q 485 31 545 71 q 340 -10 426 -10 q 207 25 264 -10 q 117 123 149 59 q 84 275 84 186 l 84 758 l 294 758 l 294 313 q 330 207 294 246 q 425 168 365 168 q 496 185 463 168 q 550 237 529 203 q 570 323 570 272 z "},"v":{"ha":814,"x_min":24,"x_max":790,"o":"m 790 758 l 525 0 l 289 0 l 24 758 l 246 758 l 403 216 l 411 216 l 568 758 l 790 758 z "},"w":{"ha":1181,"x_min":27,"x_max":1153,"o":"m 233 0 l 27 758 l 240 758 l 357 249 l 364 249 l 486 758 l 695 758 l 819 252 l 826 252 l 941 758 l 1153 758 l 947 0 l 725 0 l 595 476 l 585 476 l 455 0 l 233 0 z "},"x":{"ha":797,"x_min":35,"x_max":763,"o":"m 259 758 l 399 493 l 541 758 l 757 758 l 537 379 l 763 0 l 548 0 l 399 262 l 252 0 l 35 0 l 259 379 l 42 758 l 259 758 z "},"y":{"ha":814,"x_min":24,"x_max":790,"o":"m 214 -284 q 139 -278 174 -284 q 81 -262 104 -272 l 129 -106 q 196 -118 166 -117 q 247 -104 225 -119 q 283 -54 269 -89 l 295 -22 l 24 758 l 245 758 l 401 201 l 409 201 l 568 758 l 790 758 l 496 -82 q 438 -189 474 -143 q 347 -259 402 -234 q 214 -284 291 -284 z "},"z":{"ha":794,"x_min":80,"x_max":717,"o":"m 80 0 l 80 125 l 451 584 l 451 590 l 93 590 l 93 758 l 704 758 l 704 621 l 356 173 l 356 168 l 717 168 l 717 0 l 80 0 z "}},"familyName":"Inter","ascender":1345,"descender":-335,"underlinePosition":-229,"underlineThickness":95,"boundingBox":{"yMin":-444,"xMin":-1026,"yMax":1515,"xMax":3588},"resolution":1000,"original_font_information":{"format":0,"copyright":"Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)","fontFamily":"Inter","fontSubfamily":"Bold","uniqueID":"3.019;RSMS;Inter-Bold","fullName":"Inter Bold","version":"Version 3.019;git-0a5106e0b","postScriptName":"Inter-Bold","trademark":"Inter UI and Inter is a trademark of rsms.","manufacturer":"rsms","designer":"Rasmus Andersson","manufacturerURL":"https://rsms.me/","designerURL":"https://rsms.me/","licence":"This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: http://scripts.sil.org/OFL","licenceURL":"http://scripts.sil.org/OFL","compatibleFullName":"Inter","unknown1":"Open digits","unknown2":"Disambiguation","unknown3":"r curves into round neighbors","unknown4":"Disambiguation without slashed zero","unknown5":"Alternate one","unknown6":"Open four","unknown7":"Open six","unknown8":"Open nine","unknown9":"Lower-case L with tail","unknown10":"r with curved tail","unknown11":"Alternate German double s","unknown12":"Upper-case i with serif","unknown13":"Flat-top three","unknown14":"Captital G with spur","unknown15":"Single-storey a","unknown16":"Weight","unknown17":"Slant","unknown18":"Regular","unknown19":"Bold"},"cssFontWeight":"bold","cssFontStyle":"normal"} -------------------------------------------------------------------------------- /server/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@socket.io/component-emitter@~3.1.0": 6 | version "3.1.0" 7 | resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" 8 | integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== 9 | 10 | "@types/cookie@^0.4.1": 11 | version "0.4.1" 12 | resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" 13 | integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== 14 | 15 | "@types/cors@^2.8.12": 16 | version "2.8.13" 17 | resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" 18 | integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== 19 | dependencies: 20 | "@types/node" "*" 21 | 22 | "@types/node@*", "@types/node@>=10.0.0": 23 | version "20.4.6" 24 | resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.6.tgz#b66b66c9bb5d49b199f03399e341c9d6036e9e88" 25 | integrity sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA== 26 | 27 | abbrev@1: 28 | version "1.1.1" 29 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 30 | integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 31 | 32 | accepts@~1.3.4: 33 | version "1.3.8" 34 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" 35 | integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== 36 | dependencies: 37 | mime-types "~2.1.34" 38 | negotiator "0.6.3" 39 | 40 | anymatch@~3.1.2: 41 | version "3.1.3" 42 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 43 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 44 | dependencies: 45 | normalize-path "^3.0.0" 46 | picomatch "^2.0.4" 47 | 48 | balanced-match@^1.0.0: 49 | version "1.0.2" 50 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 51 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 52 | 53 | base64id@2.0.0, base64id@~2.0.0: 54 | version "2.0.0" 55 | resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" 56 | integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== 57 | 58 | binary-extensions@^2.0.0: 59 | version "2.2.0" 60 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 61 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 62 | 63 | brace-expansion@^1.1.7: 64 | version "1.1.11" 65 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 66 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 67 | dependencies: 68 | balanced-match "^1.0.0" 69 | concat-map "0.0.1" 70 | 71 | braces@~3.0.2: 72 | version "3.0.2" 73 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 74 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 75 | dependencies: 76 | fill-range "^7.0.1" 77 | 78 | chokidar@^3.5.2: 79 | version "3.5.3" 80 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 81 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 82 | dependencies: 83 | anymatch "~3.1.2" 84 | braces "~3.0.2" 85 | glob-parent "~5.1.2" 86 | is-binary-path "~2.1.0" 87 | is-glob "~4.0.1" 88 | normalize-path "~3.0.0" 89 | readdirp "~3.6.0" 90 | optionalDependencies: 91 | fsevents "~2.3.2" 92 | 93 | concat-map@0.0.1: 94 | version "0.0.1" 95 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 96 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 97 | 98 | cookie@~0.4.1: 99 | version "0.4.2" 100 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" 101 | integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== 102 | 103 | cors@~2.8.5: 104 | version "2.8.5" 105 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" 106 | integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== 107 | dependencies: 108 | object-assign "^4" 109 | vary "^1" 110 | 111 | debug@^3.2.7: 112 | version "3.2.7" 113 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" 114 | integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== 115 | dependencies: 116 | ms "^2.1.1" 117 | 118 | debug@~4.3.1, debug@~4.3.2: 119 | version "4.3.4" 120 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 121 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 122 | dependencies: 123 | ms "2.1.2" 124 | 125 | engine.io-parser@~5.2.1: 126 | version "5.2.1" 127 | resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.1.tgz#9f213c77512ff1a6cc0c7a86108a7ffceb16fcfb" 128 | integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ== 129 | 130 | engine.io@~6.5.2: 131 | version "6.5.2" 132 | resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.2.tgz#769348ced9d56bd47bd83d308ec1c3375e85937c" 133 | integrity sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA== 134 | dependencies: 135 | "@types/cookie" "^0.4.1" 136 | "@types/cors" "^2.8.12" 137 | "@types/node" ">=10.0.0" 138 | accepts "~1.3.4" 139 | base64id "2.0.0" 140 | cookie "~0.4.1" 141 | cors "~2.8.5" 142 | debug "~4.3.1" 143 | engine.io-parser "~5.2.1" 144 | ws "~8.11.0" 145 | 146 | fill-range@^7.0.1: 147 | version "7.0.1" 148 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 149 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 150 | dependencies: 151 | to-regex-range "^5.0.1" 152 | 153 | fsevents@~2.3.2: 154 | version "2.3.2" 155 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 156 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 157 | 158 | glob-parent@~5.1.2: 159 | version "5.1.2" 160 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 161 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 162 | dependencies: 163 | is-glob "^4.0.1" 164 | 165 | has-flag@^3.0.0: 166 | version "3.0.0" 167 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 168 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== 169 | 170 | heap@0.2.5: 171 | version "0.2.5" 172 | resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.5.tgz#713b65590ebcc40fcbeeaf55e851694092b39af1" 173 | integrity sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg== 174 | 175 | ignore-by-default@^1.0.1: 176 | version "1.0.1" 177 | resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" 178 | integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== 179 | 180 | is-binary-path@~2.1.0: 181 | version "2.1.0" 182 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 183 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 184 | dependencies: 185 | binary-extensions "^2.0.0" 186 | 187 | is-extglob@^2.1.1: 188 | version "2.1.1" 189 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 190 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 191 | 192 | is-glob@^4.0.1, is-glob@~4.0.1: 193 | version "4.0.3" 194 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 195 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 196 | dependencies: 197 | is-extglob "^2.1.1" 198 | 199 | is-number@^7.0.0: 200 | version "7.0.0" 201 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 202 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 203 | 204 | lru-cache@^6.0.0: 205 | version "6.0.0" 206 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 207 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 208 | dependencies: 209 | yallist "^4.0.0" 210 | 211 | mime-db@1.52.0: 212 | version "1.52.0" 213 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 214 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 215 | 216 | mime-types@~2.1.34: 217 | version "2.1.35" 218 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 219 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 220 | dependencies: 221 | mime-db "1.52.0" 222 | 223 | minimatch@^3.1.2: 224 | version "3.1.2" 225 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 226 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 227 | dependencies: 228 | brace-expansion "^1.1.7" 229 | 230 | ms@2.1.2: 231 | version "2.1.2" 232 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 233 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 234 | 235 | ms@^2.1.1: 236 | version "2.1.3" 237 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 238 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 239 | 240 | negotiator@0.6.3: 241 | version "0.6.3" 242 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" 243 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== 244 | 245 | nodemon@^3.0.1: 246 | version "3.0.1" 247 | resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.1.tgz#affe822a2c5f21354466b2fc8ae83277d27dadc7" 248 | integrity sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw== 249 | dependencies: 250 | chokidar "^3.5.2" 251 | debug "^3.2.7" 252 | ignore-by-default "^1.0.1" 253 | minimatch "^3.1.2" 254 | pstree.remy "^1.1.8" 255 | semver "^7.5.3" 256 | simple-update-notifier "^2.0.0" 257 | supports-color "^5.5.0" 258 | touch "^3.1.0" 259 | undefsafe "^2.0.5" 260 | 261 | nopt@~1.0.10: 262 | version "1.0.10" 263 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" 264 | integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== 265 | dependencies: 266 | abbrev "1" 267 | 268 | normalize-path@^3.0.0, normalize-path@~3.0.0: 269 | version "3.0.0" 270 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 271 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 272 | 273 | object-assign@^4: 274 | version "4.1.1" 275 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 276 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 277 | 278 | pathfinding@^0.4.18: 279 | version "0.4.18" 280 | resolved "https://registry.yarnpkg.com/pathfinding/-/pathfinding-0.4.18.tgz#a9990f6fa22b7ef196e5651b049165403a045fe8" 281 | integrity sha512-R0TGEQ9GRcFCDvAWlJAWC+KGJ9SLbW4c0nuZRcioVlXVTlw+F5RvXQ8SQgSqI9KXWC1ew95vgmIiyaWTlCe9Ag== 282 | dependencies: 283 | heap "0.2.5" 284 | 285 | picomatch@^2.0.4, picomatch@^2.2.1: 286 | version "2.3.1" 287 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 288 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 289 | 290 | pstree.remy@^1.1.8: 291 | version "1.1.8" 292 | resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" 293 | integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== 294 | 295 | readdirp@~3.6.0: 296 | version "3.6.0" 297 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 298 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 299 | dependencies: 300 | picomatch "^2.2.1" 301 | 302 | semver@^7.5.3: 303 | version "7.5.4" 304 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" 305 | integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== 306 | dependencies: 307 | lru-cache "^6.0.0" 308 | 309 | simple-update-notifier@^2.0.0: 310 | version "2.0.0" 311 | resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" 312 | integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== 313 | dependencies: 314 | semver "^7.5.3" 315 | 316 | socket.io-adapter@~2.5.2: 317 | version "2.5.2" 318 | resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" 319 | integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== 320 | dependencies: 321 | ws "~8.11.0" 322 | 323 | socket.io-parser@~4.2.4: 324 | version "4.2.4" 325 | resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" 326 | integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== 327 | dependencies: 328 | "@socket.io/component-emitter" "~3.1.0" 329 | debug "~4.3.1" 330 | 331 | socket.io@^4.7.2: 332 | version "4.7.2" 333 | resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.2.tgz#22557d76c3f3ca48f82e73d68b7add36a22df002" 334 | integrity sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw== 335 | dependencies: 336 | accepts "~1.3.4" 337 | base64id "~2.0.0" 338 | cors "~2.8.5" 339 | debug "~4.3.2" 340 | engine.io "~6.5.2" 341 | socket.io-adapter "~2.5.2" 342 | socket.io-parser "~4.2.4" 343 | 344 | supports-color@^5.5.0: 345 | version "5.5.0" 346 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 347 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 348 | dependencies: 349 | has-flag "^3.0.0" 350 | 351 | to-regex-range@^5.0.1: 352 | version "5.0.1" 353 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 354 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 355 | dependencies: 356 | is-number "^7.0.0" 357 | 358 | touch@^3.1.0: 359 | version "3.1.0" 360 | resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" 361 | integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== 362 | dependencies: 363 | nopt "~1.0.10" 364 | 365 | undefsafe@^2.0.5: 366 | version "2.0.5" 367 | resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" 368 | integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== 369 | 370 | vary@^1: 371 | version "1.1.2" 372 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 373 | integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== 374 | 375 | ws@~8.11.0: 376 | version "8.11.0" 377 | resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" 378 | integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== 379 | 380 | yallist@^4.0.0: 381 | version "4.0.0" 382 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 383 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 384 | --------------------------------------------------------------------------------