diff --git a/apps/server/src/domains/game/GameHandler.ts b/apps/server/src/domains/game/GameHandler.ts new file mode 100644 index 0000000..7340835 --- /dev/null +++ b/apps/server/src/domains/game/GameHandler.ts @@ -0,0 +1,54 @@ +import { Server, Socket } from "socket.io"; +import { GameManager } from "./GameManager"; +import { RoomManager } from "../room/RoomManager"; +import { SocketEvents } from "@repo/shared/src/protocol/events"; +import { RoomStatus } from "@repo/shared/src/domains/room/room.type"; +import type { MovePayload } from "@repo/shared/src/domains/player/player.type"; + +export const registerGameHandlers = (io: Server, socket: Socket, gameManager: GameManager, roomManager: RoomManager) => { + + // ゲーム開始要求処理 + socket.on(SocketEvents.START_GAME, () => { + const room = roomManager.getRoomByOwnerId(socket.id); + + if (room) { + room.status = RoomStatus.PLAYING; + + const playerIds = room.players.map((p: { id: string }) => p.id); + + // 同ルーム全プレイヤーのゲーム管理登録 + room.players.forEach((p: { id: string }) => { + gameManager.addPlayer(p.id); + }); + + // ルーム全員向けゲーム開始通知 + io.to(room.roomId).emit(SocketEvents.GAME_START); + + // 20Hzのゲームループを開始 + gameManager.startGameLoop(room.roomId, io, playerIds); + } + }); + + // 画面準備完了通知受信時初期データ返却 + socket.on(SocketEvents.READY_FOR_GAME, () => { + const allPlayers = gameManager.getAllPlayers(); + socket.emit(SocketEvents.CURRENT_PLAYERS, allPlayers); + }); + + // ゲームプレイ中イベント群 + socket.on(SocketEvents.MOVE, (data: MovePayload) => { + // 【変更】座標の更新のみ行い、即時の送信(io.to...emit)は全て削除 + gameManager.movePlayer(socket.id, data.x, data.y); + }); + +}; + +/** + * 切断時のゲームクリーンアップ処理 + */ +export const handleGameDisconnect = (io: Server, gameManager: GameManager, playerId: string) => { + // ゲームからの除外処理 + gameManager.removePlayer(playerId); + // 全体にプレイヤー削除を通知 + io.emit(SocketEvents.REMOVE_PLAYER, playerId); +}; \ No newline at end of file diff --git a/apps/server/src/domains/game/GameManager.ts b/apps/server/src/domains/game/GameManager.ts new file mode 100644 index 0000000..dc71ff2 --- /dev/null +++ b/apps/server/src/domains/game/GameManager.ts @@ -0,0 +1,143 @@ +import { Player } from "./entities/Player.js"; +import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; +import { MapStore } from "./states/MapStore"; +import { getGridIndexFromPosition } from "@repo/shared/src/domains/gridMap/gridMap.logic"; +import type { CellUpdate } from "@repo/shared/src/domains/gridMap/gridMap.type"; +import { SocketEvents } from "@repo/shared/src/protocol/events" +import { Server } from "socket.io"; + +// プレイヤー集合の生成・更新・参照管理クラス +export class GameManager { + private players: Map; + private mapStore: MapStore; + private gameLoops: Map; + + constructor() { + this.players = new Map(); + this.mapStore = new MapStore(); + this.gameLoops = new Map(); + } + + // 新規プレイヤー登録と初期位置設定処理 + addPlayer(id: string): Player { + const player = new Player(id); + + // 初期スポーン位置 + player.x = GAME_CONFIG.MAP_WIDTH / 2; + player.y = GAME_CONFIG.MAP_HEIGHT / 2; + + this.players.set(id, player); + return player; + } + + // プレイヤー登録解除処理 + removePlayer(id: string) { + this.players.delete(id); + } + + // 指定IDプレイヤー参照取得 + getPlayer(id: string) { + return this.players.get(id); + } + + // 指定プレイヤー座標更新処理 + movePlayer(id: string, x: number, y: number) { + const player = this.players.get(id); + if (player) { + + // 受信移動要求ログ + console.log(`Move Request -> ID:${id.slice(0,4)} x:${Math.round(x)} y:${Math.round(y)}`); + + // 無効座標データ防御チェック + if (typeof x !== "number" || typeof y !== "number" || isNaN(x) || isNaN(y)) { + console.log("⚠️ 無効なデータなので無視しました"); + return; + } + + // クライアント送信絶対座標の直接反映 + player.x = x; + player.y = y; + } + } + + /** + * 20Hz固定のゲームループを開始する + * @param roomId ルームID + * @param io WebSocketサーバーインスタンス + * @param playerIds このルームに参加しているプレイヤーのIDリスト + */ + startGameLoop(roomId: string, io: Server, playerIds: string[]) { + // 既にループが回っている場合は何もしない + if (this.gameLoops.has(roomId)) return; + + // gameConfigから20Hz(50ms)の定数を取得 + const tickRate = GAME_CONFIG.PLAYER_POSITION_UPDATE_MS; + + const loopId = setInterval(() => { + // 1. 各プレイヤーの処理 + playerIds.forEach(id => { + const player = this.players.get(id); + if (!player) return; + + // マス塗りの判定 + const gridIndex = getGridIndexFromPosition(player.x, player.y); + if (gridIndex !== null) { + this.mapStore.paintCell(gridIndex, player.teamId); + } + + // 【追加】ここで各プレイヤーの最新座標をクライアントに送信する! + io.to(roomId).emit(SocketEvents.UPDATE_PLAYER, { + id: player.id, + x: player.x, + y: player.y, + teamId: player.teamId + }); + }); + + // 2. マスの差分(Diff)を取得 + const cellUpdates = this.mapStore.getAndClearUpdates(); + + // 3. 差分があれば、ルーム内の全員に一斉送信 + if (cellUpdates.length > 0) { + io.to(roomId).emit(SocketEvents.UPDATE_MAP_CELLS, cellUpdates); + } + + // 4. 今後、プレイヤーの座標データ(UPDATE_PLAYER)もここで一括送信するように変更できます + + }, tickRate); + + // ループIDを保存 + this.gameLoops.set(roomId, loopId); + console.log(`[GameLoop] Started for room: ${roomId} at ${tickRate}ms`); + } + + /** + * ゲームループを停止する + */ + stopGameLoop(roomId: string) { + const loopId = this.gameLoops.get(roomId); + if (loopId) { + clearInterval(loopId); + this.gameLoops.delete(roomId); + console.log(`[GameLoop] Stopped for room: ${roomId}`); + } + } + + // 登録中全プレイヤー配列取得 + getAllPlayers() { + return Array.from(this.players.values()); + } + + // 【一時的】移動したプレイヤーの足元を塗り、差分を返すメソッド + public paintAndGetUpdates(playerId: string): CellUpdate[] { + const player = this.players.get(playerId); + if (!player) return []; + + const gridIndex = getGridIndexFromPosition(player.x, player.y); + if (gridIndex !== null) { + this.mapStore.paintCell(gridIndex, player.teamId); + } + + return this.mapStore.getAndClearUpdates(); + } +} \ No newline at end of file diff --git a/apps/server/src/domains/game/entities/Player.ts b/apps/server/src/domains/game/entities/Player.ts new file mode 100644 index 0000000..edc7976 --- /dev/null +++ b/apps/server/src/domains/game/entities/Player.ts @@ -0,0 +1,18 @@ +import type { PlayerData } from "@repo/shared/src/domains/player/player.type"; +import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; + +// サーバー側保持プレイヤー状態モデル +export class Player implements PlayerData { + public id: string; + public x: number = 0; + public y: number = 0; + public teamId: number; + + constructor(id: string) { + this.id = id; + + // GAME_CONFIGからチーム数を動的に取得して割り当て + const teamCount = GAME_CONFIG.TEAM_COLORS.length; + this.teamId = Math.floor(Math.random() * teamCount); + } +} \ No newline at end of file diff --git a/apps/server/src/domains/game/states/MapStore.ts b/apps/server/src/domains/game/states/MapStore.ts new file mode 100644 index 0000000..4f6f2be --- /dev/null +++ b/apps/server/src/domains/game/states/MapStore.ts @@ -0,0 +1,36 @@ +// apps/server/src/states/MapStore.ts +import type { CellUpdate } from "@repo/shared/src/domains/gridMap/gridMap.type"; +import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; + +export class MapStore { + // 全マスの現在の色(teamId)を保持 + private gridColors: number[]; + // 次回の送信ループで送る差分リスト + private pendingUpdates: CellUpdate[]; + + constructor() { + // 初期状態は -1 (無色) などで初期化 + const totalCells = GAME_CONFIG.GRID_COLS * GAME_CONFIG.GRID_ROWS; + this.gridColors = new Array(totalCells).fill(-1); + this.pendingUpdates = []; + } + + /** + * マスを塗り、色が変化した場合のみ差分キューに追加する + */ + public paintCell(index: number, teamId: number): void { + if (this.gridColors[index] !== teamId) { + this.gridColors[index] = teamId; + this.pendingUpdates.push({ index, teamId }); + } + } + + /** + * 溜まっている差分を取得し、キューをクリアする(ループ送信時に使用) + */ + public getAndClearUpdates(): CellUpdate[] { + const updates = [...this.pendingUpdates]; + this.pendingUpdates = []; + return updates; + } +} \ No newline at end of file diff --git a/apps/server/src/domains/room/RoomHandler.ts b/apps/server/src/domains/room/RoomHandler.ts new file mode 100644 index 0000000..d210e01 --- /dev/null +++ b/apps/server/src/domains/room/RoomHandler.ts @@ -0,0 +1,33 @@ +import { Server, Socket } from "socket.io"; +import { RoomManager } from "./RoomManager"; +import { SocketEvents } from "@repo/shared/src/protocol/events"; +import type { JoinRoomPayload } from "@repo/shared/src/domains/room/room.type"; + +export const registerRoomHandlers = (io: Server, socket: Socket, roomManager: RoomManager) => { + + socket.on(SocketEvents.JOIN_ROOM, (data: JoinRoomPayload) => { + const { roomId, playerName } = data; + + socket.join(roomId); + + // RoomManagerにデータ操作を依頼 + const room = roomManager.addPlayerToRoom(roomId, socket.id, playerName); + + // ルーム内全員向け最新状態配信 + io.to(roomId).emit(SocketEvents.ROOM_UPDATE, room); + }); + +}; + +/** + * 切断時のルームクリーンアップ処理 + */ +export const handleRoomDisconnect = (io: Server, socket: Socket, roomManager: RoomManager) => { + // ルームからの除外処理 + const updatedRooms = roomManager.removePlayer(socket.id); + + // 更新があったルーム(オーナー変更など)にのみ通知を飛ばす + updatedRooms.forEach(room => { + io.to(room.roomId).emit(SocketEvents.ROOM_UPDATE, room); + }); +}; \ No newline at end of file diff --git a/apps/server/src/domains/room/RoomManager.ts b/apps/server/src/domains/room/RoomManager.ts new file mode 100644 index 0000000..3be6e62 --- /dev/null +++ b/apps/server/src/domains/room/RoomManager.ts @@ -0,0 +1,66 @@ +import { Room, RoomStatus, RoomMember } from "@repo/shared/src/domains/room/room.type"; +import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; + +export class RoomManager { + private rooms: Map = new Map(); + + // ルームにプレイヤーを追加(なければ作成) + public addPlayerToRoom(roomId: string, socketId: string, playerName: string): Room { + let room = this.rooms.get(roomId); + if (!room) { + room = { + roomId: roomId, + ownerId: socketId, + players: [], + status: RoomStatus.WAITING, + maxPlayers: GAME_CONFIG.MAX_PLAYERS_PER_ROOM + }; + this.rooms.set(roomId, room); + } + + const newPlayer: RoomMember = { + id: socketId, + name: playerName, + isOwner: room.ownerId === socketId, + isReady: false + }; + room.players.push(newPlayer); + + return room; + } + + // プレイヤーをルームから削除し、更新があったルームの配列を返す + public removePlayer(socketId: string): Room[] { + const updatedRooms: Room[] = []; + + for (const [roomId, room] of this.rooms.entries()) { + const playerIndex = room.players.findIndex(p => p.id === socketId); + if (playerIndex !== -1) { + room.players.splice(playerIndex, 1); + + if (room.players.length === 0) { + // 空ルーム削除 + this.rooms.delete(roomId); + } else { + // オーナー切断時所有権移譲処理 + if (room.ownerId === socketId) { + room.ownerId = room.players[0].id; + room.players[0].isOwner = true; + } + updatedRooms.push(room); + } + } + } + return updatedRooms; + } + + // オーナーIDからルームを取得 + public getRoomByOwnerId(ownerId: string): Room | undefined { + for (const room of this.rooms.values()) { + if (room.ownerId === ownerId) { + return room; + } + } + return undefined; + } +} \ No newline at end of file diff --git a/apps/server/src/entities/Player.ts b/apps/server/src/entities/Player.ts deleted file mode 100644 index edc7976..0000000 --- a/apps/server/src/entities/Player.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { PlayerData } from "@repo/shared/src/domains/player/player.type"; -import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; - -// サーバー側保持プレイヤー状態モデル -export class Player implements PlayerData { - public id: string; - public x: number = 0; - public y: number = 0; - public teamId: number; - - constructor(id: string) { - this.id = id; - - // GAME_CONFIGからチーム数を動的に取得して割り当て - const teamCount = GAME_CONFIG.TEAM_COLORS.length; - this.teamId = Math.floor(Math.random() * teamCount); - } -} \ No newline at end of file diff --git a/apps/server/src/handlers/GameHandler.ts b/apps/server/src/handlers/GameHandler.ts deleted file mode 100644 index e2a524a..0000000 --- a/apps/server/src/handlers/GameHandler.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Server, Socket } from "socket.io"; -import { GameManager } from "../managers/GameManager.js"; -import { RoomManager } from "../managers/RoomManager.js"; -import { SocketEvents } from "@repo/shared/src/protocol/events"; -import { RoomStatus } from "@repo/shared/src/domains/room/room.type"; -import type { MovePayload } from "@repo/shared/src/domains/player/player.type"; - -export const registerGameHandlers = (io: Server, socket: Socket, gameManager: GameManager, roomManager: RoomManager) => { - - // ゲーム開始要求処理 - socket.on(SocketEvents.START_GAME, () => { - const room = roomManager.getRoomByOwnerId(socket.id); - - if (room) { - room.status = RoomStatus.PLAYING; - - const playerIds = room.players.map((p: { id: string }) => p.id); - - // 同ルーム全プレイヤーのゲーム管理登録 - room.players.forEach((p: { id: string }) => { - gameManager.addPlayer(p.id); - }); - - // ルーム全員向けゲーム開始通知 - io.to(room.roomId).emit(SocketEvents.GAME_START); - - // 20Hzのゲームループを開始 - gameManager.startGameLoop(room.roomId, io, playerIds); - } - }); - - // 画面準備完了通知受信時初期データ返却 - socket.on(SocketEvents.READY_FOR_GAME, () => { - const allPlayers = gameManager.getAllPlayers(); - socket.emit(SocketEvents.CURRENT_PLAYERS, allPlayers); - }); - - // ゲームプレイ中イベント群 - socket.on(SocketEvents.MOVE, (data: MovePayload) => { - // 【変更】座標の更新のみ行い、即時の送信(io.to...emit)は全て削除 - gameManager.movePlayer(socket.id, data.x, data.y); - }); - -}; - -/** - * 切断時のゲームクリーンアップ処理 - */ -export const handleGameDisconnect = (io: Server, gameManager: GameManager, playerId: string) => { - // ゲームからの除外処理 - gameManager.removePlayer(playerId); - // 全体にプレイヤー削除を通知 - io.emit(SocketEvents.REMOVE_PLAYER, playerId); -}; \ No newline at end of file diff --git a/apps/server/src/handlers/RoomHandler.ts b/apps/server/src/handlers/RoomHandler.ts deleted file mode 100644 index e3f0e23..0000000 --- a/apps/server/src/handlers/RoomHandler.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Server, Socket } from "socket.io"; -import { RoomManager } from "../managers/RoomManager.js"; -import { SocketEvents } from "@repo/shared/src/protocol/events"; -import type { JoinRoomPayload } from "@repo/shared/src/domains/room/room.type"; - -export const registerRoomHandlers = (io: Server, socket: Socket, roomManager: RoomManager) => { - - socket.on(SocketEvents.JOIN_ROOM, (data: JoinRoomPayload) => { - const { roomId, playerName } = data; - - socket.join(roomId); - - // RoomManagerにデータ操作を依頼 - const room = roomManager.addPlayerToRoom(roomId, socket.id, playerName); - - // ルーム内全員向け最新状態配信 - io.to(roomId).emit(SocketEvents.ROOM_UPDATE, room); - }); - -}; - -/** - * 切断時のルームクリーンアップ処理 - */ -export const handleRoomDisconnect = (io: Server, socket: Socket, roomManager: RoomManager) => { - // ルームからの除外処理 - const updatedRooms = roomManager.removePlayer(socket.id); - - // 更新があったルーム(オーナー変更など)にのみ通知を飛ばす - updatedRooms.forEach(room => { - io.to(room.roomId).emit(SocketEvents.ROOM_UPDATE, room); - }); -}; \ No newline at end of file diff --git a/apps/server/src/index.ts b/apps/server/src/index.ts index 314f10e..7859289 100644 --- a/apps/server/src/index.ts +++ b/apps/server/src/index.ts @@ -1,7 +1,7 @@ import { Server } from "socket.io"; import { createServer } from "http"; -import { GameManager } from "./managers/GameManager.js"; -import { SocketManager } from "./network/SocketManager.js"; +import { GameManager } from "./domains/game/GameManager"; +import { SocketManager } from "./network/SocketManager"; // サーバー待受ポート const PORT = 3000; diff --git a/apps/server/src/managers/GameManager.ts b/apps/server/src/managers/GameManager.ts deleted file mode 100644 index c3b8bed..0000000 --- a/apps/server/src/managers/GameManager.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { Player } from "../entities/Player.js"; -import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; -import { MapStore } from "../states/MapStore"; -import { getGridIndexFromPosition } from "@repo/shared/src/domains/gridMap/gridMap.logic"; -import type { CellUpdate } from "@repo/shared/src/domains/gridMap/gridMap.type"; -import { SocketEvents } from "@repo/shared/src/protocol/events" -import { Server } from "socket.io"; - -// プレイヤー集合の生成・更新・参照管理クラス -export class GameManager { - private players: Map; - private mapStore: MapStore; - private gameLoops: Map; - - constructor() { - this.players = new Map(); - this.mapStore = new MapStore(); - this.gameLoops = new Map(); - } - - // 新規プレイヤー登録と初期位置設定処理 - addPlayer(id: string): Player { - const player = new Player(id); - - // 初期スポーン位置 - player.x = GAME_CONFIG.MAP_WIDTH / 2; - player.y = GAME_CONFIG.MAP_HEIGHT / 2; - - this.players.set(id, player); - return player; - } - - // プレイヤー登録解除処理 - removePlayer(id: string) { - this.players.delete(id); - } - - // 指定IDプレイヤー参照取得 - getPlayer(id: string) { - return this.players.get(id); - } - - // 指定プレイヤー座標更新処理 - movePlayer(id: string, x: number, y: number) { - const player = this.players.get(id); - if (player) { - - // 受信移動要求ログ - console.log(`Move Request -> ID:${id.slice(0,4)} x:${Math.round(x)} y:${Math.round(y)}`); - - // 無効座標データ防御チェック - if (typeof x !== "number" || typeof y !== "number" || isNaN(x) || isNaN(y)) { - console.log("⚠️ 無効なデータなので無視しました"); - return; - } - - // クライアント送信絶対座標の直接反映 - player.x = x; - player.y = y; - } - } - - /** - * 20Hz固定のゲームループを開始する - * @param roomId ルームID - * @param io WebSocketサーバーインスタンス - * @param playerIds このルームに参加しているプレイヤーのIDリスト - */ - startGameLoop(roomId: string, io: Server, playerIds: string[]) { - // 既にループが回っている場合は何もしない - if (this.gameLoops.has(roomId)) return; - - // gameConfigから20Hz(50ms)の定数を取得 - const tickRate = GAME_CONFIG.PLAYER_POSITION_UPDATE_MS; - - const loopId = setInterval(() => { - // 1. 各プレイヤーの処理 - playerIds.forEach(id => { - const player = this.players.get(id); - if (!player) return; - - // マス塗りの判定 - const gridIndex = getGridIndexFromPosition(player.x, player.y); - if (gridIndex !== null) { - this.mapStore.paintCell(gridIndex, player.teamId); - } - - // 【追加】ここで各プレイヤーの最新座標をクライアントに送信する! - io.to(roomId).emit(SocketEvents.UPDATE_PLAYER, { - id: player.id, - x: player.x, - y: player.y, - teamId: player.teamId - }); - }); - - // 2. マスの差分(Diff)を取得 - const cellUpdates = this.mapStore.getAndClearUpdates(); - - // 3. 差分があれば、ルーム内の全員に一斉送信 - if (cellUpdates.length > 0) { - io.to(roomId).emit(SocketEvents.UPDATE_MAP_CELLS, cellUpdates); - } - - // 4. 今後、プレイヤーの座標データ(UPDATE_PLAYER)もここで一括送信するように変更できます - - }, tickRate); - - // ループIDを保存 - this.gameLoops.set(roomId, loopId); - console.log(`[GameLoop] Started for room: ${roomId} at ${tickRate}ms`); - } - - /** - * ゲームループを停止する - */ - stopGameLoop(roomId: string) { - const loopId = this.gameLoops.get(roomId); - if (loopId) { - clearInterval(loopId); - this.gameLoops.delete(roomId); - console.log(`[GameLoop] Stopped for room: ${roomId}`); - } - } - - // 登録中全プレイヤー配列取得 - getAllPlayers() { - return Array.from(this.players.values()); - } - - // 【一時的】移動したプレイヤーの足元を塗り、差分を返すメソッド - public paintAndGetUpdates(playerId: string): CellUpdate[] { - const player = this.players.get(playerId); - if (!player) return []; - - const gridIndex = getGridIndexFromPosition(player.x, player.y); - if (gridIndex !== null) { - this.mapStore.paintCell(gridIndex, player.teamId); - } - - return this.mapStore.getAndClearUpdates(); - } -} \ No newline at end of file diff --git a/apps/server/src/managers/RoomManager.ts b/apps/server/src/managers/RoomManager.ts deleted file mode 100644 index 3be6e62..0000000 --- a/apps/server/src/managers/RoomManager.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Room, RoomStatus, RoomMember } from "@repo/shared/src/domains/room/room.type"; -import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; - -export class RoomManager { - private rooms: Map = new Map(); - - // ルームにプレイヤーを追加(なければ作成) - public addPlayerToRoom(roomId: string, socketId: string, playerName: string): Room { - let room = this.rooms.get(roomId); - if (!room) { - room = { - roomId: roomId, - ownerId: socketId, - players: [], - status: RoomStatus.WAITING, - maxPlayers: GAME_CONFIG.MAX_PLAYERS_PER_ROOM - }; - this.rooms.set(roomId, room); - } - - const newPlayer: RoomMember = { - id: socketId, - name: playerName, - isOwner: room.ownerId === socketId, - isReady: false - }; - room.players.push(newPlayer); - - return room; - } - - // プレイヤーをルームから削除し、更新があったルームの配列を返す - public removePlayer(socketId: string): Room[] { - const updatedRooms: Room[] = []; - - for (const [roomId, room] of this.rooms.entries()) { - const playerIndex = room.players.findIndex(p => p.id === socketId); - if (playerIndex !== -1) { - room.players.splice(playerIndex, 1); - - if (room.players.length === 0) { - // 空ルーム削除 - this.rooms.delete(roomId); - } else { - // オーナー切断時所有権移譲処理 - if (room.ownerId === socketId) { - room.ownerId = room.players[0].id; - room.players[0].isOwner = true; - } - updatedRooms.push(room); - } - } - } - return updatedRooms; - } - - // オーナーIDからルームを取得 - public getRoomByOwnerId(ownerId: string): Room | undefined { - for (const room of this.rooms.values()) { - if (room.ownerId === ownerId) { - return room; - } - } - return undefined; - } -} \ No newline at end of file diff --git a/apps/server/src/network/SocketManager.ts b/apps/server/src/network/SocketManager.ts index 791be78..5a4850a 100644 --- a/apps/server/src/network/SocketManager.ts +++ b/apps/server/src/network/SocketManager.ts @@ -1,9 +1,9 @@ import { Server, Socket } from "socket.io"; -import { GameManager } from "../managers/GameManager.js"; -import { RoomManager } from "../managers/RoomManager.js"; +import { GameManager } from "../domains/game/GameManager"; +import { RoomManager } from "../domains/room/RoomManager"; import { SocketEvents } from "@repo/shared/src/protocol/events"; -import { registerRoomHandlers, handleRoomDisconnect } from "../handlers/RoomHandler.js"; -import { registerGameHandlers, handleGameDisconnect } from "../handlers/GameHandler.js"; +import { registerRoomHandlers, handleRoomDisconnect } from "../domains/room/RoomHandler"; +import { registerGameHandlers, handleGameDisconnect } from "../domains/game/GameHandler"; export class SocketManager { private io: Server; diff --git a/apps/server/src/states/MapStore.ts b/apps/server/src/states/MapStore.ts deleted file mode 100644 index 4f6f2be..0000000 --- a/apps/server/src/states/MapStore.ts +++ /dev/null @@ -1,36 +0,0 @@ -// apps/server/src/states/MapStore.ts -import type { CellUpdate } from "@repo/shared/src/domains/gridMap/gridMap.type"; -import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; - -export class MapStore { - // 全マスの現在の色(teamId)を保持 - private gridColors: number[]; - // 次回の送信ループで送る差分リスト - private pendingUpdates: CellUpdate[]; - - constructor() { - // 初期状態は -1 (無色) などで初期化 - const totalCells = GAME_CONFIG.GRID_COLS * GAME_CONFIG.GRID_ROWS; - this.gridColors = new Array(totalCells).fill(-1); - this.pendingUpdates = []; - } - - /** - * マスを塗り、色が変化した場合のみ差分キューに追加する - */ - public paintCell(index: number, teamId: number): void { - if (this.gridColors[index] !== teamId) { - this.gridColors[index] = teamId; - this.pendingUpdates.push({ index, teamId }); - } - } - - /** - * 溜まっている差分を取得し、キューをクリアする(ループ送信時に使用) - */ - public getAndClearUpdates(): CellUpdate[] { - const updates = [...this.pendingUpdates]; - this.pendingUpdates = []; - return updates; - } -} \ No newline at end of file diff --git "a/docs/01_Env/ENV_01_\347\222\260\345\242\203\346\247\213\347\257\211\343\203\273\346\212\200\350\241\223\343\202\271\343\202\277\343\203\203\343\202\257.txt" "b/docs/01_Env/ENV_01_\347\222\260\345\242\203\346\247\213\347\257\211\343\203\273\346\212\200\350\241\223\343\202\271\343\202\277\343\203\203\343\202\257.txt" index ef06a45..f00455c 100644 --- "a/docs/01_Env/ENV_01_\347\222\260\345\242\203\346\247\213\347\257\211\343\203\273\346\212\200\350\241\223\343\202\271\343\202\277\343\203\203\343\202\257.txt" +++ "b/docs/01_Env/ENV_01_\347\222\260\345\242\203\346\247\213\347\257\211\343\203\273\346\212\200\350\241\223\343\202\271\343\202\277\343\203\203\343\202\257.txt" @@ -59,6 +59,7 @@ │ │ ├── handlers/ # 処理ハンドラ (GameHandler, RoomHandler) │ │ ├── managers/ # 管理クラス (GameManager, RoomManager) │ │ ├── network/ # WebSocket処理 (SocketManager) + │ │ ├── states/ # 状態ストア (MapStore) │ │ └── index.ts # エントリーポイント │ ├── tsconfig.json │ └── package.json