-
+
+
+
+ KOD POKOJU:{" "}
+ {room.roomId}
{isPaused &&
}
- >
+
);
}
diff --git a/client/src/pages/intro.tsx b/client/src/pages/intro.tsx
index a68d6c2..47d33c9 100644
--- a/client/src/pages/intro.tsx
+++ b/client/src/pages/intro.tsx
@@ -34,10 +34,13 @@ export function Intro() {
setStatus("loading");
try {
- await connect(name.trim());
+ await connect({
+ playerName: name.trim(),
+ mode: "create",
+ });
await navigate("/game");
} catch {
- setErrorMessage("Nie udało się dołaczyć do gry. Spróbuj ponownie.");
+ setErrorMessage("Nie udało się dołączyć do gry. Spróbuj ponownie.");
setStatus("error");
}
}, [connect, name, navigate]);
diff --git a/client/src/pages/lobby.tsx b/client/src/pages/lobby.tsx
new file mode 100644
index 0000000..fc9482a
--- /dev/null
+++ b/client/src/pages/lobby.tsx
@@ -0,0 +1,64 @@
+import { useEffect, useState } from "react";
+import { Navigate, useNavigate } from "react-router-dom";
+
+import { useRoom } from "../lib/use-room";
+
+export function Lobby() {
+ const { room, isConnected } = useRoom();
+ const navigate = useNavigate();
+ const [players, setPlayers] = useState
([]);
+
+ useEffect(() => {
+ if (!room) return;
+
+ const handleStateChange = (state: any) => {
+ if (state.playerState?.players) {
+ setPlayers(Array.from(state.playerState.players.values()));
+ }
+
+ if (state.gameStarted) {
+ navigate("/game");
+ }
+ };
+
+ room.onStateChange(handleStateChange);
+
+ handleStateChange(room.state);
+
+ return () => {
+ room.onStateChange.remove(handleStateChange);
+ };
+ }, [room, navigate]);
+
+ if (!isConnected || !room) return ;
+
+ const myPlayer = players.find((p) => p.sessionId === room.sessionId);
+
+ return (
+
+
Pokój: {room.roomId}
+
+
+ {players.map((p) => (
+ -
+ {p.name || "Anonim"} - Postać:{" "}
+ {p.index === 0 ? "Sol" : "Vron"} -
+ {p.ready ? " ✅ GOTOWY" : " ❌ CZEKA"}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/client/src/pages/start.tsx b/client/src/pages/start.tsx
new file mode 100644
index 0000000..7f244ee
--- /dev/null
+++ b/client/src/pages/start.tsx
@@ -0,0 +1,187 @@
+import { useCallback, useEffect, useState } from "react";
+import { useNavigate } from "react-router-dom";
+
+import { Button } from "../components/button";
+import { CustomInput } from "../components/custom-input";
+import { ErrorContainer } from "../components/error-container";
+import { IntroContainer } from "../components/intro-container";
+import { TitleHeader } from "../components/title-header";
+import { useRoom } from "../lib/use-room";
+
+export function Start() {
+ const navigate = useNavigate();
+ const { connect, disconnect } = useRoom();
+
+ const [status, setStatus] = useState<
+ "idle" | "loading" | "error" | "success" | "reconnecting"
+ >(() => {
+ return localStorage.getItem("reconnection") === null
+ ? "idle"
+ : "reconnecting";
+ });
+
+ const [name, setName] = useState("");
+ const [mode, setMode] = useState<"join" | "create">("create");
+ const [roomCode, setRoomCode] = useState("");
+ const [isPrivate, setIsPrivate] = useState(false);
+
+ const [countdown, setCountdown] = useState(3);
+ const [errorMessage, setErrorMessage] = useState("");
+
+ const handlePlay = useCallback(async () => {
+ if (name.trim() === "") {
+ setErrorMessage("Nazwa gracza nie może być pusta.");
+ setStatus("error");
+ return;
+ }
+
+ setStatus("loading");
+ try {
+ await connect({
+ playerName: name.trim(),
+ mode,
+ roomCode: mode === "join" ? roomCode.trim() : undefined,
+ isPrivate: mode === "create" ? isPrivate : undefined,
+ });
+ await navigate("/lobby");
+ } catch {
+ setErrorMessage("Nie udało się dołaczyć do gry. Spróbuj ponownie.");
+ setStatus("error");
+ }
+ }, [connect, isPrivate, mode, name, navigate, roomCode]);
+
+ useEffect(() => {
+ if (status !== "reconnecting") {
+ return;
+ }
+
+ let remaining = 3;
+ setCountdown(remaining);
+
+ const timerId = window.setInterval(() => {
+ remaining -= 1;
+ setCountdown(remaining);
+
+ if (remaining > 0) {
+ return;
+ }
+
+ window.clearInterval(timerId);
+
+ const cachedReconnection = localStorage.getItem("reconnection");
+ if (cachedReconnection === null) {
+ setStatus("idle");
+ } else {
+ void navigate("/lobby");
+ }
+ }, 1000);
+
+ return () => {
+ window.clearInterval(timerId);
+ };
+ }, [status, navigate]);
+
+ useEffect(() => {
+ if (status === "reconnecting") {
+ return;
+ }
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === "Enter" && status !== "loading") {
+ void handlePlay();
+ }
+ };
+
+ window.addEventListener("keydown", handleKeyDown);
+
+ return () => {
+ window.removeEventListener("keydown", handleKeyDown);
+ };
+ }, [handlePlay, status]);
+
+ const handleCancelReconnection = async () => {
+ localStorage.removeItem("reconnection");
+ await disconnect();
+ setStatus("idle");
+ setCountdown(3);
+ };
+
+ return (
+
+
+
+ {status === "reconnecting" ? (
+
+
+ Próba ponownego połączenia za {countdown}...
+
+
+
+
+ ) : (
+ <>
+
+
+
+
+ {
+ setName(value.toUpperCase());
+ }}
+ disabled={status === "loading"}
+ />
+ {mode === "join" ? (
+ {
+ setRoomCode(error.target.value);
+ }}
+ />
+ ) : (
+
+ )}
+
+
+ >
+ )}
+
+ {status === "error" && }
+
+ );
+}
diff --git a/package-lock.json b/package-lock.json
index f6cba4b..233a7a5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -661,6 +661,25 @@
"@colyseus/ws-transport": "0.17.x"
}
},
+ "node_modules/@colyseus/uwebsockets-transport": {
+ "version": "0.17.20",
+ "resolved": "https://registry.npmjs.org/@colyseus/uwebsockets-transport/-/uwebsockets-transport-0.17.20.tgz",
+ "integrity": "sha512-274nZvtkaNyJ2Cs6w6onepKZLACtrHDtu5WbM5Krp5M4ScvR9k3icrqtQgffaBdo/5nVMKPuOn4U51Ylmn/dWw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.57.0"
+ },
+ "peerDependencies": {
+ "@colyseus/core": "0.17.x",
+ "uwebsockets-express": "^1.4.1 || ^2.0.1"
+ },
+ "peerDependenciesMeta": {
+ "uwebsockets-express": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@colyseus/ws-transport": {
"version": "0.17.13",
"resolved": "https://registry.npmjs.org/@colyseus/ws-transport/-/ws-transport-0.17.13.tgz",
@@ -1066,6 +1085,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1082,6 +1102,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1098,6 +1119,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1114,6 +1136,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1130,6 +1153,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1146,6 +1170,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1162,6 +1187,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1178,6 +1204,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1194,6 +1221,7 @@
"cpu": [
"arm"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1210,6 +1238,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1226,6 +1255,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1242,6 +1272,7 @@
"cpu": [
"loong64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1258,6 +1289,7 @@
"cpu": [
"mips64el"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1274,6 +1306,7 @@
"cpu": [
"ppc64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1290,6 +1323,7 @@
"cpu": [
"riscv64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1306,6 +1340,7 @@
"cpu": [
"s390x"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1322,6 +1357,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1338,6 +1374,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1354,6 +1391,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1370,6 +1408,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1386,6 +1425,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1402,6 +1442,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1418,6 +1459,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1434,6 +1476,7 @@
"cpu": [
"arm64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1450,6 +1493,7 @@
"cpu": [
"ia32"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1466,6 +1510,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2390,6 +2435,7 @@
"os": [
"android"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2406,6 +2452,7 @@
"os": [
"darwin"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2422,6 +2469,7 @@
"os": [
"darwin"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2438,6 +2486,7 @@
"os": [
"freebsd"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2454,6 +2503,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2470,6 +2520,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2486,6 +2537,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2502,6 +2554,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2518,6 +2571,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2534,6 +2588,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2550,6 +2605,7 @@
"os": [
"linux"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2566,6 +2622,7 @@
"os": [
"openharmony"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2579,6 +2636,7 @@
],
"license": "MIT",
"optional": true,
+ "peer": true,
"dependencies": {
"@emnapi/core": "1.10.0",
"@emnapi/runtime": "1.10.0",
@@ -2600,6 +2658,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -2616,6 +2675,7 @@
"os": [
"win32"
],
+ "peer": true,
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
@@ -4590,6 +4650,40 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/colyseus": {
+ "version": "0.17.10",
+ "resolved": "https://registry.npmjs.org/colyseus/-/colyseus-0.17.10.tgz",
+ "integrity": "sha512-RiVNY7LaLfI0vgVYdoc2GTPAYO6aumHWDw0xvbQuml4SA6naYGaAZI33R0vWzvVYtWFQIsGhTdkRDtp/N9FauQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@colyseus/auth": "^0.17.9",
+ "@colyseus/core": "^0.17.42",
+ "@colyseus/monitor": "^0.17.8",
+ "@colyseus/playground": "^0.17.12",
+ "@colyseus/redis-driver": "^0.17.7",
+ "@colyseus/redis-presence": "^0.17.7",
+ "@colyseus/tools": "^0.17.19",
+ "@colyseus/ws-transport": "^0.17.13"
+ },
+ "engines": {
+ "node": ">= 20.x"
+ },
+ "peerDependencies": {
+ "@colyseus/auth": "0.17.x",
+ "@colyseus/core": "0.17.x",
+ "@colyseus/redis-driver": "0.17.x",
+ "@colyseus/redis-presence": "0.17.x",
+ "@colyseus/schema": "^4.0.7",
+ "@colyseus/uwebsockets-transport": "0.17.x",
+ "@colyseus/ws-transport": "0.17.x",
+ "vite": ">=6.0.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
"node_modules/colyseus.js": {
"version": "0.16.22",
"resolved": "https://registry.npmjs.org/colyseus.js/-/colyseus.js-0.16.22.tgz",
@@ -12131,6 +12225,12 @@
"uuid": "dist/bin/uuid"
}
},
+ "node_modules/uWebSockets.js": {
+ "version": "20.57.0",
+ "resolved": "git+ssh://git@github.com/uNetworking/uWebSockets.js.git#fcfc622a4286909593b7f390056d89e0ca3b56b9",
+ "license": "Apache-2.0",
+ "peer": true
+ },
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -12749,6 +12849,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
+ "@colyseus/core": "^0.17.43",
"@colyseus/monitor": "^0.17.8",
"@colyseus/playground": "^0.17.12",
"@colyseus/tools": "^0.17.19",
@@ -12782,40 +12883,6 @@
"undici-types": "~7.16.0"
}
},
- "server/node_modules/colyseus": {
- "version": "0.17.10",
- "resolved": "https://registry.npmjs.org/colyseus/-/colyseus-0.17.10.tgz",
- "integrity": "sha512-RiVNY7LaLfI0vgVYdoc2GTPAYO6aumHWDw0xvbQuml4SA6naYGaAZI33R0vWzvVYtWFQIsGhTdkRDtp/N9FauQ==",
- "license": "MIT",
- "dependencies": {
- "@colyseus/auth": "^0.17.9",
- "@colyseus/core": "^0.17.42",
- "@colyseus/monitor": "^0.17.8",
- "@colyseus/playground": "^0.17.12",
- "@colyseus/redis-driver": "^0.17.7",
- "@colyseus/redis-presence": "^0.17.7",
- "@colyseus/tools": "^0.17.19",
- "@colyseus/ws-transport": "^0.17.13"
- },
- "engines": {
- "node": ">= 20.x"
- },
- "peerDependencies": {
- "@colyseus/auth": "0.17.x",
- "@colyseus/core": "0.17.x",
- "@colyseus/redis-driver": "0.17.x",
- "@colyseus/redis-presence": "0.17.x",
- "@colyseus/schema": "^4.0.7",
- "@colyseus/uwebsockets-transport": "0.17.x",
- "@colyseus/ws-transport": "0.17.x",
- "vite": ">=6.0.0"
- },
- "peerDependenciesMeta": {
- "vite": {
- "optional": true
- }
- }
- },
"server/node_modules/undici-types": {
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
diff --git a/server/package.json b/server/package.json
index 89af7b4..49478d0 100644
--- a/server/package.json
+++ b/server/package.json
@@ -20,6 +20,7 @@
"test": "mocha -r tsx test/MyRoom_test.ts --exit --timeout 15000"
},
"dependencies": {
+ "@colyseus/core": "^0.17.43",
"@colyseus/monitor": "^0.17.8",
"@colyseus/playground": "^0.17.12",
"@colyseus/tools": "^0.17.19",
diff --git a/server/src/app.config.ts b/server/src/app.config.ts
index b0f93b6..d6a9e6d 100644
--- a/server/src/app.config.ts
+++ b/server/src/app.config.ts
@@ -27,6 +27,7 @@ export default config({
* Define your room handlers:
*/
gameServer.define("game_room", GameRoom);
+ // .filterBy(['isPrivate']);
},
initializeExpress: (app) => {
diff --git a/server/src/rooms/GameRoom.ts b/server/src/rooms/GameRoom.ts
index 7a5ff85..9e2f19c 100644
--- a/server/src/rooms/GameRoom.ts
+++ b/server/src/rooms/GameRoom.ts
@@ -11,7 +11,7 @@ import { RoomState } from "./schema/RoomState";
// import room from "./json/examples/room3.json";
export class GameRoom extends Room<{ state: RoomState }> {
- maxClients = 4;
+ maxClients = 2;
state = new RoomState();
private roomData: any = fallbackRoom;
@@ -19,9 +19,33 @@ export class GameRoom extends Room<{ state: RoomState }> {
async onCreate(options: any) {
this.roomData = await getRoomForGame(options?.levelSlug);
this.maxClients = this.roomData.maxClients ?? this.maxClients;
- this.state.loadRoomFromJson(this.roomData);
+
+ this.setMetadata({
+ isPrivate: !!options.isPrivate,
+ });
+
+ if (options.isPrivate) {
+ this.setPrivate(true);
+ }
+
+ this.onMessage("toggle_ready", (client) => {
+ const player = this.state.playerState.players.get(client.sessionId);
+ if (player) {
+ player.ready = !player.ready;
+ }
+
+ if (this.clients.length >= 1) {
+ const allReady = Array.from(
+ this.state.playerState.players.values(),
+ ).every((p) => p.ready);
+ if (allReady) {
+ this.startGame();
+ }
+ }
+ });
+
this.onMessage("move", (client, message) => {
- if (this.state.isPaused) return;
+ if (!this.state.gameStarted || this.state.isPaused) return;
const player = this.state.playerState.players.get(client.sessionId);
if (!player) return;
@@ -72,7 +96,7 @@ export class GameRoom extends Room<{ state: RoomState }> {
});
this.setSimulationInterval((deltaTime) => {
- if (this.state.isPaused) return;
+ if (!this.state.gameStarted || this.state.isPaused) return;
const result = this.state.updateLasers(deltaTime);
if (result.length > 0) {
@@ -133,21 +157,38 @@ export class GameRoom extends Room<{ state: RoomState }> {
});
}
- onJoin(client: Client, options: any) {
- this.state.spawnNewPlayer(client.sessionId, options.name);
- const player = this.state.playerState.players.get(client.sessionId);
+ private startGame() {
+ this.state.gameStarted = true;
+ this.state.loadRoomFromJson(this.roomData);
- this.broadcast("onAddPlayer", {
- sessionId: client.sessionId,
- playerName: player.name,
- position: player.position,
- index: player.index,
+ this.clients.forEach((client) => {
+ const p = this.state.playerState.players.get(client.sessionId);
+ if (p) {
+ const startPos =
+ this.state.startingPositions[
+ p.index % this.state.startingPositions.length
+ ];
+ if (startPos) {
+ p.position.x = startPos.x;
+ p.position.y = startPos.y;
+ }
+ }
});
+ }
+
+ onJoin(client: Client, options: any) {
+ const nickname = options.name;
+
+ this.state.spawnNewPlayer(client.sessionId, nickname);
+
+ const player = this.state.playerState.players.get(client.sessionId);
- console.log(client.sessionId, "joined!");
+ console.log(
+ `Gracz ${player.name} dołączył jako ${player.index === 0 ? "Sol" : "Vron"}`,
+ );
}
- async onLeave(client: Client, code?: number) {
+ async onLeave(client: Client, code: number) {
if (code !== CloseCode.CONSENTED) {
try {
// allow disconnected client to reconnect into this room until 20 seconds
diff --git a/server/src/rooms/schema/Player.ts b/server/src/rooms/schema/Player.ts
index a9575ff..dfc0f23 100644
--- a/server/src/rooms/schema/Player.ts
+++ b/server/src/rooms/schema/Player.ts
@@ -6,5 +6,6 @@ export class Player extends Schema {
@type("string") sessionId: string;
@type("number") index: number;
@type("string") name: string;
- @type(Position) position: Position;
+ @type(Position) position: Position = new Position();
+ @type("boolean") ready: boolean = false;
}
diff --git a/server/src/rooms/schema/PlayerState.ts b/server/src/rooms/schema/PlayerState.ts
index 4d4acc8..9322a46 100644
--- a/server/src/rooms/schema/PlayerState.ts
+++ b/server/src/rooms/schema/PlayerState.ts
@@ -67,4 +67,9 @@ export class PlayerState extends Schema {
getSessionIdByName(name: string): string | null {
return this.playerSessionIds.get(name) || null;
}
+
+ toggleReady(sessionId: string) {
+ const player = this.players.get(sessionId);
+ player.ready = !player.ready;
+ }
}
diff --git a/server/src/rooms/schema/RoomState.ts b/server/src/rooms/schema/RoomState.ts
index 0675038..ebf2abe 100644
--- a/server/src/rooms/schema/RoomState.ts
+++ b/server/src/rooms/schema/RoomState.ts
@@ -15,6 +15,7 @@ export class RoomState extends Schema {
@type(["string"]) grid = new ArraySchema();
@type("number") width: number = 10;
@type("number") height: number = 7;
+ @type("boolean") gameStarted: boolean = false;
@type("boolean") isPaused: boolean = false;
@type([Position]) startingPositions = new ArraySchema();
@@ -303,10 +304,14 @@ export class RoomState extends Schema {
spawnNewPlayer(sessionId: string, name: string = null) {
this.playerState.createPlayer(sessionId, name);
const player = this.playerState.players.get(sessionId);
- const startingPos =
- this.startingPositions[player.index % this.startingPositions.length];
- player.position.x = startingPos.x;
- player.position.y = startingPos.y;
+ if (this.startingPositions && this.startingPositions.length > 0) {
+ const startingPos =
+ this.startingPositions[player.index % this.startingPositions.length];
+ if (startingPos) {
+ player.position.x = startingPos.x;
+ player.position.y = startingPos.y;
+ }
+ }
}
despawnPlayer(sessionId: string) {
diff --git a/server/test/GameRoom_test.ts b/server/test/GameRoom_test.ts
index b6af803..0376b19 100644
--- a/server/test/GameRoom_test.ts
+++ b/server/test/GameRoom_test.ts
@@ -50,22 +50,25 @@ describe("GameRoom (room1)", () => {
it("connects a client and syncs room1 state (size, player spawn)", async () => {
const room = await colyseus.createRoom("game_room", {});
- const client = await colyseus.connectTo(room, { name: "TestPlayer" });
- registerNoopMessageHandlers(client);
+ const c1 = await colyseus.connectTo(room, { name: "TestPlayer" });
+ const c2 = await colyseus.connectTo(room, { name: "Bot" });
+ registerNoopMessageHandlers(c1);
assert.strictEqual(
- client.sessionId,
+ c1.sessionId,
room.clients[0].sessionId,
"Client sessionId matches the first connected client in the room",
);
+ c1.send("toggle_ready");
+ c2.send("toggle_ready");
await room.waitForNextPatch();
const s = room.state;
assert.strictEqual(s.width, 10);
assert.strictEqual(s.height, 8);
- const player = s.playerState.players.get(client.sessionId);
+ const player = s.playerState.players.get(c1.sessionId);
assert.ok(player, "Player exists after join");
assert.strictEqual(player.position.x, 1);
assert.strictEqual(player.position.y, 2);
@@ -78,6 +81,9 @@ describe("GameRoom (room1)", () => {
const c2 = await colyseus.connectTo(room, { name: "Bravo" });
registerNoopMessageHandlers(c2);
+ c1.send("toggle_ready");
+ c2.send("toggle_ready");
+
await room.waitForNextPatch();
const s = room.state;
@@ -97,20 +103,24 @@ describe("GameRoom (room1)", () => {
it("moves the player after a move message (down from spawn)", async () => {
const room = await colyseus.createRoom("game_room", {});
- const client = await colyseus.connectTo(room, { name: "Mover" });
- registerNoopMessageHandlers(client);
+ const c1 = await colyseus.connectTo(room, { name: "Mover" });
+ const c2 = await colyseus.connectTo(room, { name: "Bot" });
+ registerNoopMessageHandlers(c1);
+
+ c1.send("toggle_ready");
+ c2.send("toggle_ready");
await room.waitForNextPatch();
- const before = room.state.playerState.players.get(client.sessionId);
+ const before = room.state.playerState.players.get(c1.sessionId);
assert.deepStrictEqual(
{ x: before.position.x, y: before.position.y },
{ x: 1, y: 2 },
);
- client.send("move", { direction: "down" });
+ c1.send("move", { direction: "down" });
await room.waitForNextPatch();
- const after = room.state.playerState.players.get(client.sessionId);
+ const after = room.state.playerState.players.get(c1.sessionId);
assert.deepStrictEqual(
{ x: after.position.x, y: after.position.y },
{ x: 1, y: 3 },