diff --git a/apps/server/src/handlers/GameHandler.ts b/apps/server/src/handlers/GameHandler.ts new file mode 100644 index 0000000..5d25f82 --- /dev/null +++ b/apps/server/src/handlers/GameHandler.ts @@ -0,0 +1,52 @@ +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/types/room"; +import type { MovePayload } from "@repo/shared/src/types/payloads"; + +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; + + // 同ルーム全プレイヤーのゲーム管理登録 + room.players.forEach(p => { + gameManager.addPlayer(p.id); + }); + + // ルーム全員向けゲーム開始通知 + io.to(room.roomId).emit(SocketEvents.GAME_START); + } + }); + + // 画面準備完了通知受信時初期データ返却 + socket.on(SocketEvents.READY_FOR_GAME, () => { + const allPlayers = gameManager.getAllPlayers(); + socket.emit(SocketEvents.CURRENT_PLAYERS, allPlayers); + }); + + // ゲームプレイ中イベント群 + socket.on(SocketEvents.MOVE, (data: MovePayload) => { + gameManager.movePlayer(socket.id, data.x, data.y); + + const updatedPlayer = gameManager.getPlayer(socket.id); + if (updatedPlayer) { + const myRooms = Array.from(socket.rooms).filter(r => r !== socket.id); + const targetRoom = myRooms.length > 0 ? myRooms[0] : null; + + if (targetRoom) { + io.to(targetRoom).emit(SocketEvents.UPDATE_PLAYER, { + id: socket.id, + x: updatedPlayer.x, + y: updatedPlayer.y + }); + } + } + }); + +}; \ No newline at end of file diff --git a/apps/server/src/handlers/RoomHandler.ts b/apps/server/src/handlers/RoomHandler.ts new file mode 100644 index 0000000..c0fec72 --- /dev/null +++ b/apps/server/src/handlers/RoomHandler.ts @@ -0,0 +1,20 @@ +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/types/room"; + +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); + }); + +}; \ No newline at end of file diff --git a/apps/server/src/managers/RoomManager.ts b/apps/server/src/managers/RoomManager.ts new file mode 100644 index 0000000..3229016 --- /dev/null +++ b/apps/server/src/managers/RoomManager.ts @@ -0,0 +1,66 @@ +import { Room, RoomStatus, RoomMember } from "@repo/shared/src/types/room"; +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 552ed96..ee6eb3e 100644 --- a/apps/server/src/network/SocketManager.ts +++ b/apps/server/src/network/SocketManager.ts @@ -1,135 +1,44 @@ import { Server, Socket } from "socket.io"; import { GameManager } from "../managers/GameManager.js"; -import { Room, RoomStatus, RoomMember, JoinRoomPayload } from "@repo/shared/src/types/room"; +import { RoomManager } from "../managers/RoomManager.js"; import { SocketEvents } from "@repo/shared/src/protocol/events"; -import { GAME_CONFIG } from "@repo/shared/src/config/gameConfig"; -import type { MovePayload } from "@repo/shared/src/types/payloads"; +import { registerRoomHandlers } from "../handlers/RoomHandler.js"; +import { registerGameHandlers } from "../handlers/GameHandler.js"; export class SocketManager { private io: Server; private gameManager: GameManager; - - // サーバーメモリ上ルーム情報テーブル - private rooms: Map = new Map(); + private roomManager: RoomManager; constructor(io: Server, gameManager: GameManager) { this.io = io; this.gameManager = gameManager; + this.roomManager = new RoomManager(); // 新設したRoomManagerをインスタンス化 } public initialize() { this.io.on(SocketEvents.CONNECT, (socket: Socket) => { console.log(`✅ User connected: ${socket.id}`); - // ロビー・ルーム関連イベント群 - - socket.on(SocketEvents.JOIN_ROOM, (data: JoinRoomPayload) => { - const { roomId, playerName } = data; - - socket.join(roomId); - - let room = this.rooms.get(roomId); - if (!room) { - room = { - roomId: roomId, - ownerId: socket.id, - players: [], - status: RoomStatus.WAITING, - maxPlayers: GAME_CONFIG.MAX_PLAYERS_PER_ROOM - }; - this.rooms.set(roomId, room); - } - - // 参加プレイヤー情報ルーム追加 - const newPlayer: RoomMember = { - id: socket.id, - name: playerName, - isOwner: room.ownerId === socket.id, - isReady: false - }; - room.players.push(newPlayer); - - // ルーム内全員向け最新状態配信 - this.io.to(roomId).emit(SocketEvents.ROOM_UPDATE, room); - }); - - // ゲーム開始要求処理 - socket.on(SocketEvents.START_GAME, () => { - for (const [roomId, room] of this.rooms.entries()) { - if (room.ownerId === socket.id) { - room.status = RoomStatus.PLAYING; - - // 同ルーム全プレイヤーのゲーム管理登録 - room.players.forEach(p => { - this.gameManager.addPlayer(p.id); - }); - - // ルーム全員向けゲーム開始通知 - this.io.to(roomId).emit(SocketEvents.GAME_START); - - // 初期プレイヤー一覧送信タイミング分離方針 - break; - } - } - }); - - // 画面準備完了通知受信時初期データ返却 - socket.on(SocketEvents.READY_FOR_GAME, () => { - // ゲーム管理中全プレイヤー情報取得 - const allPlayers = this.gameManager.getAllPlayers(); - - // 通知元ソケット限定初期データ送信 - socket.emit(SocketEvents.CURRENT_PLAYERS, allPlayers); - }); - - // ゲームプレイ中イベント群 - - socket.on(SocketEvents.MOVE, (data: MovePayload) => { - // サーバー側プレイヤー座標更新 - this.gameManager.movePlayer(socket.id, data.x, data.y); - - const updatedPlayer = this.gameManager.getPlayer(socket.id); - if (updatedPlayer) { - // 同一ルーム参加者限定座標更新配信 - const myRooms = Array.from(socket.rooms).filter(r => r !== socket.id); - const targetRoom = myRooms.length > 0 ? myRooms[0] : null; - - if (targetRoom) { - this.io.to(targetRoom).emit(SocketEvents.UPDATE_PLAYER, { - id: socket.id, - x: updatedPlayer.x, - y: updatedPlayer.y - }); - } - } - }); + // 各ハンドラに処理を委譲(ルーティング) + registerRoomHandlers(this.io, socket, this.roomManager); + registerGameHandlers(this.io, socket, this.gameManager, this.roomManager); // 切断時イベント群 - socket.on(SocketEvents.DISCONNECT, () => { console.log(`❌ User disconnected: ${socket.id}`); + + // 1. ゲームからの除外処理 this.gameManager.removePlayer(socket.id); this.io.emit(SocketEvents.REMOVE_PLAYER, socket.id); - // 参加中ルームからの切断プレイヤー除外処理 - for (const [roomId, room] of this.rooms.entries()) { - const playerIndex = room.players.findIndex(p => p.id === socket.id); - if (playerIndex !== -1) { - room.players.splice(playerIndex, 1); - - if (room.players.length === 0) { - // 空ルーム削除分岐 - this.rooms.delete(roomId); - } else { - // オーナー切断時所有権移譲処理 - if (room.ownerId === socket.id) { - room.ownerId = room.players[0].id; - room.players[0].isOwner = true; - } - this.io.to(roomId).emit(SocketEvents.ROOM_UPDATE, room); - } - } - } + // 2. ルームからの除外処理 + const updatedRooms = this.roomManager.removePlayer(socket.id); + + // 更新があったルーム(オーナー変更など)にのみ通知を飛ばす + updatedRooms.forEach(room => { + this.io.to(room.roomId).emit(SocketEvents.ROOM_UPDATE, room); + }); }); });