diff --git a/apps/server/src/network/SocketManager.ts b/apps/server/src/network/SocketManager.ts index 71bdb3c..39372ed 100644 --- a/apps/server/src/network/SocketManager.ts +++ b/apps/server/src/network/SocketManager.ts @@ -1,8 +1,13 @@ +/** + * SocketManager + * Socket.IO接続ハンドラの登録を初期化するマネージャ + */ import { Server } from "socket.io"; import { GameManager } from "@server/domains/game/GameManager"; import { RoomManager } from "@server/domains/room/RoomManager"; import { registerConnectionHandlers } from "./handlers/registerConnectionHandlers"; +/** Socket.IOの接続ハンドラ登録を統括する */ export class SocketManager { private io: Server; private gameManager: GameManager; @@ -15,6 +20,7 @@ } public initialize() { + // 接続時に必要な各ドメインハンドラを登録する registerConnectionHandlers({ io: this.io, gameManager: this.gameManager, diff --git a/apps/server/src/network/adapters/socketEmitters.ts b/apps/server/src/network/adapters/socketEmitters.ts index 32439b9..4895b90 100644 --- a/apps/server/src/network/adapters/socketEmitters.ts +++ b/apps/server/src/network/adapters/socketEmitters.ts @@ -1,3 +1,7 @@ +/** + * socketEmitters + * Socket.IOの送信処理を用途別に生成するアダプタ + */ import { Server, Socket } from "socket.io"; type EmitPayload = unknown; @@ -15,18 +19,21 @@ emit(event, payload); }; +/** ルーム単位の送信関数を生成する */ export const createEmitToRoom = (io: Server) => { return (roomId: string, event: string, payload?: EmitPayload) => { emitWithOptionalPayload((eventName, body) => io.to(roomId).emit(eventName, body), event, payload); }; }; +/** 単一ソケット向けの送信関数を生成する */ export const createEmitToSocket = (socket: Socket) => { return (event: string, payload?: EmitPayload) => { emitWithOptionalPayload((eventName, body) => socket.emit(eventName, body), event, payload); }; }; +/** 全接続向けの送信関数を生成する */ export const createEmitToAll = (io: Server) => { return (event: string, payload?: EmitPayload) => { emitWithOptionalPayload((eventName, body) => io.emit(eventName, body), event, payload); diff --git a/apps/server/src/network/bootstrap/boot.ts b/apps/server/src/network/bootstrap/boot.ts index 004c870..29ba1ff 100644 --- a/apps/server/src/network/bootstrap/boot.ts +++ b/apps/server/src/network/bootstrap/boot.ts @@ -1,10 +1,16 @@ +/** + * boot + * HTTPサーバにSocket.IOと各マネージャを接続して起動準備を行う + */ import type { Server as HttpServer } from "http"; import { GameManager } from "@server/domains/game/GameManager"; import { RoomManager } from "@server/domains/room/RoomManager"; import { SocketManager } from "../SocketManager"; import { createIo } from "./createIo"; +/** 通信基盤とドメインマネージャを初期化して接続ハンドラを有効化する */ export const boot = (httpServer: HttpServer) => { + // ネットワーク層とドメイン層の依存を構築する const io = createIo(httpServer); const gameManager = new GameManager(); const roomManager = new RoomManager(); diff --git a/apps/server/src/network/bootstrap/createHttpServer.ts b/apps/server/src/network/bootstrap/createHttpServer.ts index 3434920..f797925 100644 --- a/apps/server/src/network/bootstrap/createHttpServer.ts +++ b/apps/server/src/network/bootstrap/createHttpServer.ts @@ -1,13 +1,20 @@ +/** + * createHttpServer + * ヘルスチェック用途のHTTPサーバを生成する + */ import { createServer } from "http"; +/** ルートの疎通確認と未定義パス応答を提供するHTTPサーバを生成する */ export const createHttpServer = () => { return createServer((req, res) => { + // ヘルスチェック要求に成功応答を返す if (req.url === "/") { res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" }); res.end("ok"); return; } + // 未定義パスには404を返す res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }); res.end("not found"); }); diff --git a/apps/server/src/network/bootstrap/createIo.ts b/apps/server/src/network/bootstrap/createIo.ts index e77fb86..c492f28 100644 --- a/apps/server/src/network/bootstrap/createIo.ts +++ b/apps/server/src/network/bootstrap/createIo.ts @@ -1,7 +1,12 @@ +/** + * createIo + * 共有設定を用いてSocket.IOサーバを生成する + */ import { Server } from "socket.io"; import { config } from "@repo/shared"; import type { Server as HttpServer } from "http"; +/** CORS設定を適用したSocket.IOサーバを生成する */ export const createIo = (httpServer: HttpServer) => { return new Server(httpServer, { cors: { diff --git a/apps/server/src/network/handlers/CommonHandler.ts b/apps/server/src/network/handlers/CommonHandler.ts index ef0dc0e..cd6bb1e 100644 --- a/apps/server/src/network/handlers/CommonHandler.ts +++ b/apps/server/src/network/handlers/CommonHandler.ts @@ -1,3 +1,7 @@ +/** + * CommonHandler + * 各ハンドラで共有する送信関数群を組み立てる + */ import { Server, Socket } from "socket.io"; import { createEmitToAll, @@ -5,12 +9,14 @@ createEmitToSocket, } from "@server/network/adapters/socketEmitters"; +/** ハンドラで共通利用する送信コンテキスト */ export type CommonHandlerContext = { emitToAll: ReturnType; emitToRoom: ReturnType; emitToSocket: ReturnType; }; +/** 送信先別のエミッタをまとめた共通コンテキストを生成する */ export const createCommonHandlerContext = ( io: Server, socket: Socket diff --git a/apps/server/src/network/handlers/GameHandler.ts b/apps/server/src/network/handlers/GameHandler.ts index c6b1ef1..3cefc15 100644 --- a/apps/server/src/network/handlers/GameHandler.ts +++ b/apps/server/src/network/handlers/GameHandler.ts @@ -1,2 +1,8 @@ +/** + * GameHandler + * ゲーム関連ハンドラを外部公開する再エクスポート定義 + */ +/** ゲームイベント受信ハンドラ登録関数を再公開する */ export { registerGameHandlers } from "./game/registerGameHandlers"; +/** ゲーム切断処理ハンドラを再公開する */ export { handleGameDisconnect } from "./game/handleGameDisconnect"; diff --git a/apps/server/src/network/handlers/RoomHandler.ts b/apps/server/src/network/handlers/RoomHandler.ts index dcd3d5e..4ff4894 100644 --- a/apps/server/src/network/handlers/RoomHandler.ts +++ b/apps/server/src/network/handlers/RoomHandler.ts @@ -1,2 +1,8 @@ +/** + * RoomHandler + * ルーム関連ハンドラを外部公開する再エクスポート定義 + */ +/** ルームイベント受信ハンドラ登録関数を再公開する */ export { registerRoomHandlers } from "./room/registerRoomHandlers"; +/** ルーム切断処理ハンドラを再公開する */ export { handleRoomDisconnect } from "./room/handleRoomDisconnect"; diff --git a/apps/server/src/network/handlers/game/createGameEventPublisher.ts b/apps/server/src/network/handlers/game/createGameEventPublisher.ts index f8db10a..c8b1927 100644 --- a/apps/server/src/network/handlers/game/createGameEventPublisher.ts +++ b/apps/server/src/network/handlers/game/createGameEventPublisher.ts @@ -1,3 +1,7 @@ +/** + * createGameEventPublisher + * ゲーム系ユースケースから利用する送信関数群を生成する + */ import { Server } from "socket.io"; import { protocol } from "@repo/shared"; import type { gridMapTypes, playerTypes, roomTypes } from "@repo/shared"; @@ -12,6 +16,7 @@ type UpdatePlayerPayload = playerTypes.PlayerData; type MapCellUpdatesPayload = gridMapTypes.CellUpdate[]; +/** ゲーム進行中イベントの送信インターフェース */ export type GameEventPublisher = { publishPongToSocket: (payload: PongPayload) => void; publishUpdatePlayerToRoom: (roomId: RoomId, playerData: UpdatePlayerPayload) => void; @@ -22,10 +27,12 @@ publishGameStartToSocket: (payload: GameStartPayload) => void; }; +/** 切断時に配信するゲームイベントの送信インターフェース */ export type GameDisconnectPublisher = { publishPlayerRemovedToAll: (removedPlayerId: SocketId) => void; }; +/** 共通送信コンテキストからゲームイベント送信関数群を生成する */ export const createGameEventPublisher = (common: CommonHandlerContext): GameEventPublisher => { return { publishPongToSocket: (payload: PongPayload) => { @@ -52,6 +59,7 @@ }; }; +/** ゲーム切断時の送信関数群を生成する */ export const createGameDisconnectPublisher = (io: Server): GameDisconnectPublisher => { const emitToAll = createEmitToAll(io); diff --git a/apps/server/src/network/handlers/game/handleGameDisconnect.ts b/apps/server/src/network/handlers/game/handleGameDisconnect.ts index ca3b231..3cb2c11 100644 --- a/apps/server/src/network/handlers/game/handleGameDisconnect.ts +++ b/apps/server/src/network/handlers/game/handleGameDisconnect.ts @@ -1,8 +1,13 @@ +/** + * handleGameDisconnect + * ゲーム切断ユースケースを呼び出してプレイヤー離脱を配信する + */ import { Server } from "socket.io"; import { GameManager } from "@server/domains/game/GameManager"; import { disconnectUseCase } from "@server/domains/game/application/useCases/disconnectUseCase"; import { createGameDisconnectPublisher } from "./createGameEventPublisher"; +/** 切断したプレイヤーをゲーム管理から除外し通知する */ export const handleGameDisconnect = ( io: Server, gameManager: GameManager, diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index d457550..29d3511 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -1,3 +1,7 @@ +/** + * registerGameHandlers + * ゲーム関連イベントの受信ハンドラを登録する + */ import { Server, Socket } from "socket.io"; import { GameManager } from "@server/domains/game/GameManager"; import { RoomManager } from "@server/domains/room/RoomManager"; @@ -12,6 +16,7 @@ import { logEvent } from "@server/logging/logEvent"; import { isMovePayload, isPingPayload } from "@server/network/validation/socketPayloadValidators"; +/** ゲームイベントの購読とユースケース呼び出しを設定する */ export const registerGameHandlers = ( io: Server, socket: Socket, @@ -21,6 +26,7 @@ const common = createCommonHandlerContext(io, socket); const gamePublisher = createGameEventPublisher(common); + // 遅延計測用のPINGを検証しPONGを返す socket.on(protocol.SocketEvents.PING, (clientTime: unknown) => { if (!isPingPayload(clientTime)) { logEvent("Network", { @@ -37,6 +43,7 @@ }); }); + // オーナー開始要求に応じてゲーム進行ユースケースを起動する socket.on(protocol.SocketEvents.START_GAME, () => { startGameUseCase({ ownerId: socket.id, @@ -49,6 +56,7 @@ }); }); + // 参加者の準備完了通知を受けて現在状態を返す socket.on(protocol.SocketEvents.READY_FOR_GAME, () => { const roomId = Array.from(socket.rooms).find((room) => room !== socket.id); @@ -61,6 +69,7 @@ }); }); + // 移動入力を検証しプレイヤー移動ユースケースへ連携する socket.on(protocol.SocketEvents.MOVE, (data: unknown) => { if (!isMovePayload(data)) { logEvent("Network", { diff --git a/apps/server/src/network/handlers/registerConnectionHandlers.ts b/apps/server/src/network/handlers/registerConnectionHandlers.ts index e75f3c6..690c439 100644 --- a/apps/server/src/network/handlers/registerConnectionHandlers.ts +++ b/apps/server/src/network/handlers/registerConnectionHandlers.ts @@ -1,3 +1,7 @@ +/** + * registerConnectionHandlers + * 接続時にルームとゲームの各ハンドラを登録する + */ import { Server, Socket } from "socket.io"; import { GameManager } from "@server/domains/game/GameManager"; import { RoomManager } from "@server/domains/room/RoomManager"; @@ -12,12 +16,14 @@ roomManager: RoomManager; }; +/** ソケット接続と切断イベントに対する共通ハンドラを登録する */ export const registerConnectionHandlers = ({ io, gameManager, roomManager, }: RegisterConnectionHandlersParams) => { io.on(protocol.SocketEvents.CONNECT, (socket: Socket) => { + // 接続ログを記録してドメイン別ハンドラを登録する logEvent("Network", { event: "CONNECT", result: "connected", @@ -28,6 +34,7 @@ registerGameHandlers(io, socket, gameManager, roomManager); socket.on(protocol.SocketEvents.DISCONNECT, () => { + // 切断ログ記録後にドメイン別の後処理を実行する logEvent("Network", { event: "DISCONNECT", result: "disconnected", diff --git a/apps/server/src/network/handlers/room/createRoomEventPublisher.ts b/apps/server/src/network/handlers/room/createRoomEventPublisher.ts index 49798e7..3184664 100644 --- a/apps/server/src/network/handlers/room/createRoomEventPublisher.ts +++ b/apps/server/src/network/handlers/room/createRoomEventPublisher.ts @@ -1,3 +1,7 @@ +/** + * createRoomEventPublisher + * ルーム系ユースケースから利用する送信関数を生成する + */ import { Server } from "socket.io"; import { protocol } from "@repo/shared"; import { createEmitToRoom } from "@server/network/adapters/socketEmitters"; @@ -7,10 +11,12 @@ type RoomId = roomTypes.Room["roomId"]; type RoomUpdatePayload = roomTypes.Room; +/** ルーム更新イベントの送信インターフェース */ export type RoomEventPublisher = { publishRoomUpdate: (roomId: RoomId, room: RoomUpdatePayload) => void; }; +/** 共通送信コンテキストからルームイベント送信関数を生成する */ export const createRoomEventPublisher = ( common: CommonHandlerContext ): RoomEventPublisher => { @@ -21,6 +27,7 @@ }; }; +/** 切断時のルーム更新送信関数を生成する */ export const createRoomDisconnectPublisher = (io: Server): RoomEventPublisher => { const emitToRoom = createEmitToRoom(io); diff --git a/apps/server/src/network/handlers/room/handleRoomDisconnect.ts b/apps/server/src/network/handlers/room/handleRoomDisconnect.ts index 66d9bb1..c711d20 100644 --- a/apps/server/src/network/handlers/room/handleRoomDisconnect.ts +++ b/apps/server/src/network/handlers/room/handleRoomDisconnect.ts @@ -1,8 +1,13 @@ +/** + * handleRoomDisconnect + * ルーム切断ユースケースを呼び出してルーム状態更新を配信する + */ import { Server, Socket } from "socket.io"; import { RoomManager } from "@server/domains/room/RoomManager"; import { roomDisconnectUseCase } from "@server/domains/room/application/useCases/roomDisconnectUseCase"; import { createRoomDisconnectPublisher } from "./createRoomEventPublisher"; +/** 切断ソケットのルーム離脱処理を実行して更新通知する */ export const handleRoomDisconnect = ( io: Server, socket: Socket, diff --git a/apps/server/src/network/handlers/room/registerRoomHandlers.ts b/apps/server/src/network/handlers/room/registerRoomHandlers.ts index 2d92107..0c3dd03 100644 --- a/apps/server/src/network/handlers/room/registerRoomHandlers.ts +++ b/apps/server/src/network/handlers/room/registerRoomHandlers.ts @@ -1,3 +1,7 @@ +/** + * registerRoomHandlers + * ルーム参加イベントの受信ハンドラを登録する + */ import { Server, Socket } from "socket.io"; import { RoomManager } from "@server/domains/room/RoomManager"; import { protocol } from "@repo/shared"; @@ -8,6 +12,7 @@ import { isJoinRoomPayload } from "@server/network/validation/socketPayloadValidators"; import { logEvent } from "@server/logging/logEvent"; +/** ルーム参加イベントを検証して参加ユースケースへ連携する */ export const registerRoomHandlers = ( io: Server, socket: Socket, @@ -16,6 +21,7 @@ const common = createCommonHandlerContext(io, socket); const roomPublisher = createRoomEventPublisher(common); + // 参加要求のペイロード検証と参加処理を実行する socket.on(protocol.SocketEvents.JOIN_ROOM, (data: unknown) => { if (!isJoinRoomPayload(data)) { logEvent("Network", { diff --git a/apps/server/src/network/validation/socketPayloadValidators.ts b/apps/server/src/network/validation/socketPayloadValidators.ts index fffc822..beba0ee 100644 --- a/apps/server/src/network/validation/socketPayloadValidators.ts +++ b/apps/server/src/network/validation/socketPayloadValidators.ts @@ -1,3 +1,7 @@ +/** + * socketPayloadValidators + * ソケット受信ペイロードの型ガードを提供する + */ import type { playerTypes, roomTypes } from "@repo/shared"; const isFiniteNumber = (value: unknown): value is number => { @@ -8,10 +12,12 @@ return typeof value === "string" && value.trim().length > 0; }; +/** PINGイベントのペイロードが数値であるか判定する */ export const isPingPayload = (value: unknown): value is number => { return isFiniteNumber(value); }; +/** MOVEイベントのペイロードが移動座標であるか判定する */ export const isMovePayload = (value: unknown): value is playerTypes.MovePayload => { if (typeof value !== "object" || value === null) { return false; @@ -21,6 +27,7 @@ return isFiniteNumber(candidate.x) && isFiniteNumber(candidate.y); }; +/** JOIN_ROOMイベントのペイロードが参加情報であるか判定する */ export const isJoinRoomPayload = (value: unknown): value is roomTypes.JoinRoomPayload => { if (typeof value !== "object" || value === null) { return false;