diff --git a/apps/client/src/scenes/game/application/GameNetworkSync.ts b/apps/client/src/scenes/game/application/GameNetworkSync.ts index 6e7422a..0bf4f13 100644 --- a/apps/client/src/scenes/game/application/GameNetworkSync.ts +++ b/apps/client/src/scenes/game/application/GameNetworkSync.ts @@ -49,8 +49,9 @@ } }; - private handleUpdatePlayers = (serverPlayers: playerTypes.PlayerData[]) => { - serverPlayers.forEach((playerData) => { + private handleUpdatePlayers = (changedPlayers: playerTypes.PlayerData[]) => { + // UPDATE_PLAYERS は差分のみ届くため,対象IDだけ上書き更新する + changedPlayers.forEach((playerData) => { if (playerData.id === this.myId) return; const target = this.players[playerData.id]; diff --git a/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts b/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts index 1a3514e..e00cf54 100644 --- a/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts +++ b/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts @@ -20,8 +20,8 @@ public movePlayer(id: string, x: number, y: number): void { const roomId = this.playerToRoom.get(id); if (!roomId) { - logEvent("GameSessionService", { - event: "MOVE", + logEvent("GamePlayerOperationService", { + event: "PLAYER_MOVE", result: "ignored_player_not_in_session", socketId: id, }); @@ -34,8 +34,8 @@ public removePlayer(id: string): void { const roomId = this.playerToRoom.get(id); if (!roomId) { - logEvent("GameSessionService", { - event: "REMOVE_PLAYER", + logEvent("GamePlayerOperationService", { + event: "PLAYER_REMOVE", result: "ignored_player_not_in_session", socketId: id, }); @@ -61,8 +61,8 @@ session.dispose(); this.sessions.delete(roomId); this.roomToPlayers.delete(roomId); - logEvent("GameSessionService", { - event: "REMOVE_PLAYER", + logEvent("GamePlayerOperationService", { + event: "PLAYER_REMOVE", result: "session_disposed_empty_room", roomId, socketId: id, diff --git a/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts b/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts index 7e648fc..52261b6 100644 --- a/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts +++ b/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts @@ -34,8 +34,8 @@ onGameEnd: () => void ) { if (this.sessions.has(roomId)) { - logEvent("GameSessionService", { - event: "START_GAME_LOOP", + logEvent("GameSessionLifecycleService", { + event: "SESSION_START", result: "ignored_already_running", roomId, }); @@ -58,8 +58,8 @@ onGameEnd(); }); - logEvent("GameSessionService", { - event: "START_GAME_LOOP", + logEvent("GameSessionLifecycleService", { + event: "SESSION_START", result: "started", roomId, playerCount: playerIds.length, diff --git a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts index 9f95b56..c3a31d2 100644 --- a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts @@ -31,7 +31,9 @@ roomId, playerIds, (tickData) => { - output.publishUpdatePlayersToRoom(roomId, tickData.players); + if (tickData.players.length > 0) { + output.publishUpdatePlayersToRoom(roomId, tickData.players); + } if (tickData.cellUpdates.length > 0) { output.publishMapCellUpdatesToRoom(roomId, tickData.cellUpdates); diff --git a/apps/server/src/domains/game/loop/GameLoop.ts b/apps/server/src/domains/game/loop/GameLoop.ts index 889a66a..18aa1d5 100644 --- a/apps/server/src/domains/game/loop/GameLoop.ts +++ b/apps/server/src/domains/game/loop/GameLoop.ts @@ -29,6 +29,7 @@ private endMonotonicTimeMs: number = 0; private nextTickAtMs: number = 0; private readonly maxCatchUpTicks: number = 3; + private lastSentPlayers: Map = new Map(); constructor( private roomId: string, @@ -47,6 +48,7 @@ this.startMonotonicTimeMs = nowMs; this.endMonotonicTimeMs = nowMs + config.GAME_CONFIG.GAME_DURATION_SEC * 1000; this.nextTickAtMs = nowMs + this.tickRate; + this.lastSentPlayers.clear(); this.isRunning = true; this.scheduleNextTick(); @@ -101,7 +103,7 @@ } private processSingleTick(): void { - const playersData: TickData["players"] = []; + const changedPlayers: TickData["players"] = []; // 1. 各プレイヤーの座標処理とマス塗りの判定 this.players.forEach((player) => { @@ -112,12 +114,31 @@ } // 送信用のプレイヤーデータを構築 - playersData.push({ + const playerData = { id: player.id, x: player.x, y: player.y, teamId: player.teamId, - }); + }; + + const lastSentPlayer = this.lastSentPlayers.get(player.id); + const isChanged = + !lastSentPlayer || + lastSentPlayer.x !== playerData.x || + lastSentPlayer.y !== playerData.y || + lastSentPlayer.teamId !== playerData.teamId; + + if (isChanged) { + changedPlayers.push(playerData); + this.lastSentPlayers.set(player.id, playerData); + } + }); + + // ルームから離脱したプレイヤーの送信状態をクリーンアップする + Array.from(this.lastSentPlayers.keys()).forEach((playerId) => { + if (!this.players.has(playerId)) { + this.lastSentPlayers.delete(playerId); + } }); // 2. マスの差分(Diff)を取得 @@ -125,7 +146,7 @@ // 3. 通信層(GameHandler)へデータを渡す this.onTick({ - players: playersData, + players: changedPlayers, cellUpdates: cellUpdates, }); } @@ -134,6 +155,7 @@ if (!this.isRunning) return; this.isRunning = false; + this.lastSentPlayers.clear(); if (this.loopId) { clearTimeout(this.loopId);