From ff6eb30f966df6b0f9d8927db9aa666ff225f4b2 Mon Sep 17 00:00:00 2001 From: HP <*bang*> Date: Fri, 16 Jan 2026 21:12:54 -0500 Subject: [PATCH] added the games route and two test game sub-routes for colyseus testing. --- package-lock.json | 184 +++++++++++++++++++++ package.json | 1 + src/app/games/page.tsx | 80 +++++++++ src/app/games/tix-tax/page.tsx | 100 +++++++++++ src/app/games/visions-of-reality/page.tsx | 192 ++++++++++++++++++++++ src/app/page.tsx | 11 +- src/env.d.ts | 12 ++ src/paths.ts | 5 + 8 files changed, 580 insertions(+), 5 deletions(-) create mode 100644 src/app/games/page.tsx create mode 100644 src/app/games/tix-tax/page.tsx create mode 100644 src/app/games/visions-of-reality/page.tsx create mode 100644 src/env.d.ts diff --git a/package-lock.json b/package-lock.json index fdd2bd1..88e4bf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@mui/material": "^7.3.7", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.5.0", + "colyseus.js": "^0.16.22", "next": "16.1.1", "pg": "^8.17.0", "react": "19.2.3", @@ -270,6 +271,34 @@ "node": ">=6.9.0" } }, + "node_modules/@colyseus/httpie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@colyseus/httpie/-/httpie-2.0.1.tgz", + "integrity": "sha512-JvABMZzPLiyrUsVj3ElXGORRDTu+NKzXHWd1uV1R1SThAKMm06cVW6bOyADARD65bs8JJoHNNbUkW8KoRvRDzA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@colyseus/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@colyseus/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-MuwPFhizFKC3zmGfy0fpo+kcnZdNdnQHFVjw81v4WXHCelDeCX8yNRVtuEm8kGlHqq7qiASLC0pu0RPqYOhxXg==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/@colyseus/schema": { + "version": "3.0.76", + "resolved": "https://registry.npmjs.org/@colyseus/schema/-/schema-3.0.76.tgz", + "integrity": "sha512-i+ceBZyhB7lTn5+BoG/xxYfzW4dKKyLOywsGKVgXHe9fD905AS/Lk180jd1bICEJhebGeiRXEQ2YUPl/xwFg2g==", + "license": "MIT", + "bin": { + "schema-codegen": "bin/schema-codegen", + "schema-debug": "bin/schema-debug" + } + }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", @@ -1196,6 +1225,84 @@ "three": ">= 0.159.0" } }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@mui/core-downloads-tracker": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.7.tgz", @@ -3043,6 +3150,25 @@ "dev": true, "license": "MIT" }, + "node_modules/colyseus.js": { + "version": "0.16.22", + "resolved": "https://registry.npmjs.org/colyseus.js/-/colyseus.js-0.16.22.tgz", + "integrity": "sha512-xyiajukHvlwOtcziVbXZWmz7yBH3EImovYrGPAe2kVkdubLVYmOjskJuXh2VLlO8XGjyhmNwig9ELz18sTUo9g==", + "license": "MIT", + "dependencies": { + "@colyseus/httpie": "^2.0.0", + "@colyseus/msgpackr": "^1.11.2", + "@colyseus/schema": "^3.0.0", + "tslib": "^2.1.0", + "ws": "^8.13.0" + }, + "engines": { + "node": ">= 12.x" + }, + "funding": { + "url": "https://github.com/sponsors/endel" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5254,6 +5380,28 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -5348,6 +5496,21 @@ } } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -7162,6 +7325,27 @@ "node": ">=0.10.0" } }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index ada4acd..6f8cd6f 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@mui/material": "^7.3.7", "@react-three/drei": "^10.7.7", "@react-three/fiber": "^9.5.0", + "colyseus.js": "^0.16.22", "next": "16.1.1", "pg": "^8.17.0", "react": "19.2.3", diff --git a/src/app/games/page.tsx b/src/app/games/page.tsx new file mode 100644 index 0000000..9514785 --- /dev/null +++ b/src/app/games/page.tsx @@ -0,0 +1,80 @@ +"use client" +import React from "react" +import { + Box, + Card, + Container, + List, + ListItemButton, + ListItemText, + Stack, + Typography, +} from "@mui/material"; +import { useRouter } from "next/navigation"; +import { paths } from "@/paths"; + +const navItems = [ + { label: "Visions Of Reality", path: paths.games.vor }, + { label: "Tix-Tax", path: paths.games.tixtax }, +]; + +export default function VisionsOfReality() { + const router = useRouter(); + return ( + + + + + + Some online games available to you. + + + Choose a one to continue. + + + + + {navItems.map((item) => ( + router.push(item.path)} + sx={{ + borderRadius: 2, + border: "1px solid rgba(255,255,255,0.16)", + backgroundColor: "rgba(0,0,0,0.18)", + transition: "transform 120ms ease, background-color 120ms ease", + "&:hover": { + backgroundColor: "rgba(255,255,255,0.12)", + transform: "translateX(-4px)", + }, + "&:active": { + transform: "translateX(-2px) scale(0.99)", + }, + }} + > + + + ))} + + + + ) +} diff --git a/src/app/games/tix-tax/page.tsx b/src/app/games/tix-tax/page.tsx new file mode 100644 index 0000000..68b6c74 --- /dev/null +++ b/src/app/games/tix-tax/page.tsx @@ -0,0 +1,100 @@ +'use client' + +import React from "react" +import { Client, Room } from "colyseus.js" +import { Box, Button } from "@mui/material"; + + +interface TixTaxLike{ + playerOne: { name: string; coins: number }, + playerTwo: { name: string; coins: number } + +} + +export default function Page() { + const [client] = React.useState(() => new Client("http://prospera:2567")); + const [room, setRoom] = React.useState>(); + const [state, setState] = React.useState(undefined); + + const handleFindMatch = async () => { + const room = await client.joinOrCreate("tix_tax") + setRoom(room); + room.onStateChange((state) => { + setState(state) + }) + + room.onLeave(() => { + }) + } + return ( + <> + + Welcome to Tix Tax + + + {state ? : <> + + } + + +
+ {Array.from({ length: 11 }, (_, index) => ( +
{index + 1}
+ ))} +
+ + ) +} + +function TixTax({ state }: { state: TixTaxLike} ){ + return ( + <> + + Player one Coins {state.playerOne.coins} + + + {Array.from({ length: 3 }, (_, i) => ( + + {Array.from({ length: 3 }, (_, j) => ( + + + + ))} + + + ))} + + + ) +} diff --git a/src/app/games/visions-of-reality/page.tsx b/src/app/games/visions-of-reality/page.tsx new file mode 100644 index 0000000..2838971 --- /dev/null +++ b/src/app/games/visions-of-reality/page.tsx @@ -0,0 +1,192 @@ +"use client" +import React from "react" +import { Client, Room } from "colyseus.js" +import { Button } from "@mui/material" + +type RealityLike = { + barracks?: Record + entities?: Record + hero?: unknown +} + +type GameStateLike = { + realities?: Record | Map + home_reality_index?: number + target_reality_index?: number +} + +const ROOM_NAME = "main_game"; + +function toRealityEntries(realities: GameStateLike["realities"]): Array<[string, RealityLike]> { + if (!realities) return [] + if (typeof (realities as Map).entries === "function") { + return Array.from((realities as Map).entries()) + } + return Object.entries(realities as Record) +} + +export default function VisionsOfReality() { + const [client] = React.useState(() => new Client(process.env.SERVER_URL)) + const [room, setRoom] = React.useState | null>(null) + const [status, setStatus] = React.useState("disconnected") + const [tick, setTick] = React.useState(0) + const [homeRealityIndex, setHomeRealityIndex] = React.useState(null) + const [targetRealityIndex, setTargetRealityIndex] = React.useState(null) + const [viewRealityIndex, setViewRealityIndex] = React.useState(null) + + React.useEffect(() => { + return () => { + room?.leave() + } + }, [room]) + + const connect = async () => { + setStatus("connecting") + try { + const joined = await client.joinOrCreate(ROOM_NAME) + setRoom(joined) + setStatus("connected") + setHomeRealityIndex(joined.state?.home_reality_index ?? null) + setTargetRealityIndex(joined.state?.target_reality_index ?? null) + setViewRealityIndex(joined.state?.home_reality_index ?? null) + + joined.onStateChange((state) => { + setHomeRealityIndex(state.home_reality_index ?? null) + setTargetRealityIndex(state.target_reality_index ?? null) + setViewRealityIndex((prev) => (prev === null ? state.home_reality_index ?? null : prev)) + setTick((t) => t + 1) + }) + + joined.onLeave(() => { + setStatus("disconnected") + setRoom(null) + }) + } catch (err) { + console.error("Failed to join room", err) + setStatus("error") + } + } + + const disconnect = async () => { + await room?.leave() + setRoom(null) + setStatus("disconnected") + } + + const realities = toRealityEntries(room?.state?.realities) + const currentReality = + viewRealityIndex !== null + ? realities.find(([id]) => Number(id) === viewRealityIndex)?.[1] + : undefined + + const sendTarget = (index: number) => { + if (!room) return + setTargetRealityIndex(index) + room.send("set_target_reality_index", { index }) + } + + return ( +
+
+ + +
status: {status}
+
tick: {tick}
+
home: {homeRealityIndex ?? "?"}
+
view: {viewRealityIndex ?? "?"}
+
target: {targetRealityIndex ?? "?"}
+
+ {Array.from({ length: 9 }) + .map((_, index) => index) + .filter((index) => index !== 4) + .filter((index) => index !== homeRealityIndex) + .map((index) => { + const isTarget = targetRealityIndex === index + const isViewing = viewRealityIndex === index + return ( + + ) + })} +
+
+ +
+
+ Reality {viewRealityIndex ?? "?"} {viewRealityIndex === homeRealityIndex ? "(home)" : ""} +
+ +
+ {Array.from({ length: 225 }).map((__, cellIndex) => ( +
+ ))} +
+ +
+
barracks: {currentReality?.barracks ? Object.keys(currentReality.barracks).length : 0}
+
entities: {currentReality?.entities ? Object.keys(currentReality.entities).length : 0}
+
hero: {currentReality?.hero ? "yes" : "no"}
+
+
+
+ ) +} diff --git a/src/app/page.tsx b/src/app/page.tsx index 4fa3d05..9fb11df 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -16,7 +16,7 @@ import { paths } from "@/paths"; const navItems = [ { label: "Products", path: paths.products.home }, - { label: "Pricing", path: "/pricing" }, + { label: "Games", path: paths.games.home }, { label: "About", path: "/about" }, { label: "Contact", path: "/contact" }, ]; @@ -48,7 +48,6 @@ export default function Home() { zIndex: 0, }} /> - + + Tartarus +
- - Tartarus -
diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..24149f5 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,12 @@ +export interface Env { + + DB_STRING?: string; + SERVER_URL?: string; + TIX_TAX_ROOM_NAME?: string; +} + +declare global { + namespace NodeJS{ + interface ProcessEnv extends Env {} + } +} diff --git a/src/paths.ts b/src/paths.ts index ca7619f..dfbc62b 100644 --- a/src/paths.ts +++ b/src/paths.ts @@ -3,6 +3,11 @@ export const paths = { products: { home: '/products', }, + games: { + home: "/games", + vor: "/games/visions-of-reality", + tixtax: "/games/tix-tax" + }, auth: { signIn: '/auth/sign-in', resetPassword: '/auth/reset-password',