diff --git a/apps/server/src/application/coordinators/startGameCoordinator.ts b/apps/server/src/application/coordinators/startGameCoordinator.ts index cddf474..c8edccb 100644 --- a/apps/server/src/application/coordinators/startGameCoordinator.ts +++ b/apps/server/src/application/coordinators/startGameCoordinator.ts @@ -8,7 +8,8 @@ type StartGameRoomPort, } from "@server/domains/game/application/ports/gameUseCasePorts"; import { startGameUseCase } from "@server/domains/game/application/useCases/startGameUseCase"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameUseCaseLogEvents, logResults, logScopes } from "@server/logging/index"; import { roomConsts } from "@repo/shared"; type StartGameCoordinatorParams = { @@ -33,18 +34,18 @@ }: StartGameCoordinatorParams) => { const room = roomManager.getRoomByOwnerId(ownerId); if (!room) { - logEvent("GameUseCase", { - event: "START_GAME", - result: "ignored_no_room", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.START_GAME, + result: logResults.IGNORED_NO_ROOM, socketId: ownerId, }); return; } if (room.status === roomConsts.RoomPhase.PLAYING) { - logEvent("GameUseCase", { - event: "START_GAME", - result: "ignored_already_playing", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.START_GAME, + result: logResults.IGNORED_ALREADY_PLAYING, roomId: room.roomId, socketId: ownerId, }); @@ -53,18 +54,18 @@ const updatedRoom = roomManager.markRoomPlaying(room.roomId); if (!updatedRoom) { - logEvent("GameUseCase", { - event: "START_GAME", - result: "ignored_room_not_found", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.START_GAME, + result: logResults.IGNORED_ROOM_NOT_FOUND, roomId: room.roomId, socketId: ownerId, }); return; } - logEvent("GameUseCase", { - event: "START_GAME", - result: "accepted", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.START_GAME, + result: logResults.ACCEPTED, roomId: updatedRoom.roomId, socketId: ownerId, totalPlayers: updatedRoom.players.length, diff --git a/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts b/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts index 9874b80..a5437d3 100644 --- a/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts +++ b/apps/server/src/domains/game/application/services/GamePlayerOperationService.ts @@ -2,7 +2,8 @@ * GamePlayerOperationService * ゲームセッション内のプレイヤー移動と離脱操作を管理する */ -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameDomainLogEvents, logResults, logScopes } from "@server/logging/index"; import { GameRoomSession } from "./GameRoomSession"; type SessionStore = Map; @@ -20,9 +21,9 @@ public movePlayer(id: string, x: number, y: number): void { const roomId = this.playerToRoom.get(id); if (!roomId) { - logEvent("GamePlayerOperationService", { - event: "PLAYER_MOVE", - result: "ignored_player_not_in_session", + logEvent(logScopes.GAME_PLAYER_OPERATION_SERVICE, { + event: gameDomainLogEvents.PLAYER_MOVE, + result: logResults.IGNORED_PLAYER_NOT_IN_SESSION, socketId: id, }); return; @@ -34,9 +35,9 @@ public removePlayer(id: string): void { const roomId = this.playerToRoom.get(id); if (!roomId) { - logEvent("GamePlayerOperationService", { - event: "PLAYER_REMOVE", - result: "ignored_player_not_in_session", + logEvent(logScopes.GAME_PLAYER_OPERATION_SERVICE, { + event: gameDomainLogEvents.PLAYER_REMOVE, + result: logResults.IGNORED_PLAYER_NOT_IN_SESSION, socketId: id, }); return; @@ -56,9 +57,9 @@ session.dispose(); this.sessions.delete(roomId); this.roomToPlayers.delete(roomId); - logEvent("GamePlayerOperationService", { - event: "PLAYER_REMOVE", - result: "session_disposed_empty_room", + logEvent(logScopes.GAME_PLAYER_OPERATION_SERVICE, { + event: gameDomainLogEvents.PLAYER_REMOVE, + result: logResults.SESSION_DISPOSED_EMPTY_ROOM, roomId, socketId: id, }); diff --git a/apps/server/src/domains/game/application/services/GameRoomSession.ts b/apps/server/src/domains/game/application/services/GameRoomSession.ts index efe7e8e..bc77b27 100644 --- a/apps/server/src/domains/game/application/services/GameRoomSession.ts +++ b/apps/server/src/domains/game/application/services/GameRoomSession.ts @@ -2,7 +2,8 @@ * GameRoomSession * 1ルーム分のゲーム進行状態とゲームループ実行を管理する */ -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameDomainLogEvents, logResults, logScopes } from "@server/logging/index"; import type { gameTypes } from "@repo/shared"; import { GameLoop } from "../../loop/GameLoop"; import { Player } from "../../entities/player/Player.js"; @@ -58,9 +59,9 @@ public movePlayer(id: string, x: number, y: number): void { const player = this.players.get(id); if (!player) { - logEvent("GameRoomSession", { - event: "MOVE", - result: "ignored_player_not_found", + logEvent(logScopes.GAME_ROOM_SESSION, { + event: gameDomainLogEvents.MOVE, + result: logResults.IGNORED_PLAYER_NOT_FOUND, roomId: this.roomId, socketId: id, }); @@ -68,9 +69,9 @@ } if (!isValidPosition(x, y)) { - logEvent("GameRoomSession", { - event: "MOVE", - result: "ignored_invalid_payload", + logEvent(logScopes.GAME_ROOM_SESSION, { + event: gameDomainLogEvents.MOVE, + result: logResults.IGNORED_INVALID_PAYLOAD, roomId: this.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 7cc15e3..8eb3670 100644 --- a/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts +++ b/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts @@ -4,7 +4,8 @@ */ import { config } from "@repo/shared"; import type { gameTypes } from "@repo/shared"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameDomainLogEvents, logResults, logScopes } from "@server/logging/index"; import { GameRoomSession } from "./GameRoomSession"; type SessionStore = Map; @@ -34,9 +35,9 @@ onGameEnd: () => void ) { if (this.sessions.has(roomId)) { - logEvent("GameSessionLifecycleService", { - event: "SESSION_START", - result: "ignored_already_running", + logEvent(logScopes.GAME_SESSION_LIFECYCLE_SERVICE, { + event: gameDomainLogEvents.SESSION_START, + result: logResults.IGNORED_ALREADY_RUNNING, roomId, }); return; @@ -58,9 +59,9 @@ onGameEnd(); }); - logEvent("GameSessionLifecycleService", { - event: "SESSION_START", - result: "started", + logEvent(logScopes.GAME_SESSION_LIFECYCLE_SERVICE, { + event: gameDomainLogEvents.SESSION_START, + result: logResults.STARTED, roomId, playerCount: playerIds.length, }); diff --git a/apps/server/src/domains/game/application/useCases/disconnectUseCase.ts b/apps/server/src/domains/game/application/useCases/disconnectUseCase.ts index bb6ca19..c762160 100644 --- a/apps/server/src/domains/game/application/useCases/disconnectUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/disconnectUseCase.ts @@ -3,7 +3,8 @@ * 切断したプレイヤーをゲーム状態から除外し,必要に応じて通知を行う */ import type { DisconnectPlayerPort, GameOutputPort } from "../ports/gameUseCasePorts"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameUseCaseLogEvents, logResults, logScopes } from "@server/logging/index"; type DisconnectUseCaseParams = { gameManager: DisconnectPlayerPort; @@ -25,9 +26,9 @@ output.publishPlayerRemovedToRoom(roomId, playerId); } - logEvent("GameUseCase", { - event: "DISCONNECT", - result: "player_removed", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.DISCONNECT, + result: logResults.PLAYER_REMOVED, socketId: playerId, }); }; diff --git a/apps/server/src/domains/game/application/useCases/readyForGameUseCase.ts b/apps/server/src/domains/game/application/useCases/readyForGameUseCase.ts index f4696ad..6961ae2 100644 --- a/apps/server/src/domains/game/application/useCases/readyForGameUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/readyForGameUseCase.ts @@ -4,7 +4,8 @@ */ import type { ReadyForGamePort } from "../ports/gameUseCasePorts"; import type { GameOutputPort } from "../ports/gameUseCasePorts"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameUseCaseLogEvents, logResults, logScopes } from "@server/logging/index"; type ReadyForGameUseCaseParams = { socketId: string; @@ -22,9 +23,9 @@ }: ReadyForGameUseCaseParams) => { if (!roomId) { output.publishCurrentPlayersToSocket([]); - logEvent("GameUseCase", { - event: "READY_FOR_GAME", - result: "ignored_missing_room", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.READY_FOR_GAME, + result: logResults.IGNORED_MISSING_ROOM, socketId, }); return; @@ -33,9 +34,9 @@ const roomPlayers = gameManager.getRoomPlayers(roomId); output.publishCurrentPlayersToSocket(roomPlayers); - logEvent("GameUseCase", { - event: "READY_FOR_GAME", - result: "received", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.READY_FOR_GAME, + result: logResults.RECEIVED, socketId, roomId, totalPlayers: roomPlayers.length, @@ -47,9 +48,9 @@ } output.publishGameStartToSocket({ startTime }); - logEvent("GameUseCase", { - event: "GAME_START", - result: "emitted", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.GAME_START, + result: logResults.EMITTED, socketId, roomId, startTime, diff --git a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts index d6e871f..e7bfd81 100644 --- a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts @@ -3,7 +3,8 @@ * ルーム内プレイヤーでゲームセッションを開始し,進行イベントを通知する */ import type { GameOutputPort, StartGamePort } from "../ports/gameUseCasePorts"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameUseCaseLogEvents, logResults, logScopes } from "@server/logging/index"; type StartGameUseCaseParams = { roomId: string; @@ -40,9 +41,9 @@ } }, () => { - logEvent("GameUseCase", { - event: "GAME_END", - result: "emitted", + logEvent(logScopes.GAME_USE_CASE, { + event: gameUseCaseLogEvents.GAME_END, + result: logResults.EMITTED, roomId, reason: "duration_elapsed", }); diff --git a/apps/server/src/domains/game/loop/GameLoop.ts b/apps/server/src/domains/game/loop/GameLoop.ts index 0ddb36e..425395b 100644 --- a/apps/server/src/domains/game/loop/GameLoop.ts +++ b/apps/server/src/domains/game/loop/GameLoop.ts @@ -7,7 +7,8 @@ import { getPlayerGridIndex } from "../entities/player/playerPosition.js"; import { config } from "@repo/shared"; import type { gameTypes } from "@repo/shared"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { gameDomainLogEvents, logResults, logScopes } from "@server/logging/index"; /** ルーム内ゲーム進行を定周期で実行するループ管理クラス */ export class GameLoop { @@ -40,9 +41,9 @@ this.isRunning = true; this.scheduleNextTick(); - logEvent("GameLoop", { - event: "GAME_LOOP", - result: "started", + logEvent(logScopes.GAME_LOOP, { + event: gameDomainLogEvents.GAME_LOOP, + result: logResults.STARTED, roomId: this.roomId, tickRate: this.tickRate, }); @@ -150,9 +151,9 @@ this.loopId = null; } - logEvent("GameLoop", { - event: "GAME_LOOP", - result: "stopped", + logEvent(logScopes.GAME_LOOP, { + event: gameDomainLogEvents.GAME_LOOP, + result: logResults.STOPPED, roomId: this.roomId, elapsedMs: Math.max(0, Math.round(performance.now() - this.startMonotonicTimeMs)), }); diff --git a/apps/server/src/domains/room/application/services/RoomExitService.ts b/apps/server/src/domains/room/application/services/RoomExitService.ts index 36696e3..15a3e64 100644 --- a/apps/server/src/domains/room/application/services/RoomExitService.ts +++ b/apps/server/src/domains/room/application/services/RoomExitService.ts @@ -3,7 +3,8 @@ * ルーム退出処理とオーナー移譲処理を担うサービス */ import type { roomTypes } from "@repo/shared"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes, roomDomainLogEvents } from "@server/logging/index"; /** 退出要求に応じてプレイヤー削除とルーム整理を行うサービス */ export class RoomExitService { @@ -19,9 +20,9 @@ } room.players.splice(playerIndex, 1); - logEvent("RoomExitService", { - event: "PLAYER_LEAVE", - result: "removed", + logEvent(logScopes.ROOM_EXIT_SERVICE, { + event: roomDomainLogEvents.PLAYER_LEAVE, + result: logResults.REMOVED, roomId, socketId, totalPlayers: room.players.length, @@ -29,9 +30,9 @@ if (room.players.length === 0) { this.rooms.delete(roomId); - logEvent("RoomExitService", { - event: "ROOM_DELETE", - result: "deleted", + logEvent(logScopes.ROOM_EXIT_SERVICE, { + event: roomDomainLogEvents.ROOM_DELETE, + result: logResults.DELETED, roomId, socketId, }); @@ -41,9 +42,9 @@ if (room.ownerId === socketId) { room.ownerId = room.players[0].id; room.players[0].isOwner = true; - logEvent("RoomExitService", { - event: "OWNER_TRANSFER", - result: "transferred", + logEvent(logScopes.ROOM_EXIT_SERVICE, { + event: roomDomainLogEvents.OWNER_TRANSFER, + result: logResults.TRANSFERRED, roomId, socketId, newOwnerId: room.ownerId, diff --git a/apps/server/src/domains/room/application/services/RoomJoinService.ts b/apps/server/src/domains/room/application/services/RoomJoinService.ts index d096dcc..0a06116 100644 --- a/apps/server/src/domains/room/application/services/RoomJoinService.ts +++ b/apps/server/src/domains/room/application/services/RoomJoinService.ts @@ -4,7 +4,8 @@ */ import { config, roomConsts } from "@repo/shared"; import type { roomTypes } from "@repo/shared"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes, roomDomainLogEvents } from "@server/logging/index"; import type { JoinRoomResult } from "../ports/roomUseCasePorts"; /** 参加要求に応じてルーム作成と参加者追加を行うサービス */ @@ -22,9 +23,9 @@ maxPlayers: config.GAME_CONFIG.MAX_PLAYERS_PER_ROOM, }; this.rooms.set(roomId, room); - logEvent("RoomJoinService", { - event: "ROOM_CREATE", - result: "created", + logEvent(logScopes.ROOM_JOIN_SERVICE, { + event: roomDomainLogEvents.ROOM_CREATE, + result: logResults.CREATED, roomId, socketId, ownerId: socketId, @@ -34,9 +35,9 @@ // 同一ソケットの重複参加を防止する const alreadyJoined = room.players.some((player) => player.id === socketId); if (alreadyJoined) { - logEvent("RoomJoinService", { - event: "PLAYER_JOIN", - result: "ignored_duplicate", + logEvent(logScopes.ROOM_JOIN_SERVICE, { + event: roomDomainLogEvents.PLAYER_JOIN, + result: logResults.IGNORED_DUPLICATE, roomId, socketId, totalPlayers: room.players.length, @@ -46,9 +47,9 @@ // ルーム満員時の参加を拒否する if (room.players.length >= room.maxPlayers) { - logEvent("RoomJoinService", { - event: "PLAYER_JOIN", - result: "ignored_room_full", + logEvent(logScopes.ROOM_JOIN_SERVICE, { + event: roomDomainLogEvents.PLAYER_JOIN, + result: logResults.IGNORED_ROOM_FULL, roomId, socketId, maxPlayers: room.maxPlayers, @@ -65,9 +66,9 @@ }; room.players.push(newPlayer); - logEvent("RoomJoinService", { - event: "PLAYER_JOIN", - result: "joined", + logEvent(logScopes.ROOM_JOIN_SERVICE, { + event: roomDomainLogEvents.PLAYER_JOIN, + result: logResults.JOINED, roomId, socketId, playerName, diff --git a/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts b/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts index 4f9a1fd..d1ddd59 100644 --- a/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts +++ b/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts @@ -8,7 +8,8 @@ JoinRoomResult, RoomOutputPort, } from "../ports/roomUseCasePorts"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes, roomUseCaseLogEvents } from "@server/logging/index"; type JoinRoomUseCaseParams = { roomManager: JoinRoomPort; @@ -25,9 +26,9 @@ output, }: JoinRoomUseCaseParams): JoinRoomResult => { const { roomId, playerName } = data; - logEvent("RoomUseCase", { - event: "JOIN_ROOM", - result: "received", + logEvent(logScopes.ROOM_USE_CASE, { + event: roomUseCaseLogEvents.JOIN_ROOM, + result: logResults.RECEIVED, roomId, socketId, playerName, @@ -40,9 +41,9 @@ reason: joinResult.status, }); - logEvent("RoomUseCase", { - event: "JOIN_ROOM", - result: "rejected", + logEvent(logScopes.ROOM_USE_CASE, { + event: roomUseCaseLogEvents.JOIN_ROOM, + result: logResults.REJECTED, reason: joinResult.status, roomId, socketId, diff --git a/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts b/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts index 18b7aaf..f534d14 100644 --- a/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts +++ b/apps/server/src/domains/room/application/useCases/roomDisconnectUseCase.ts @@ -4,7 +4,8 @@ */ import type { DisconnectRoomPort } from "../ports/roomUseCasePorts"; import type { RoomOutputPort } from "../ports/roomUseCasePorts"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes, roomUseCaseLogEvents } from "@server/logging/index"; type RoomDisconnectUseCaseParams = { roomManager: DisconnectRoomPort; @@ -19,18 +20,18 @@ output, }: RoomDisconnectUseCaseParams) => { const updatedRooms = roomManager.removePlayer(socketId); - logEvent("RoomUseCase", { - event: "DISCONNECT", - result: "processed", + logEvent(logScopes.ROOM_USE_CASE, { + event: roomUseCaseLogEvents.DISCONNECT, + result: logResults.PROCESSED, socketId, updatedRoomCount: updatedRooms.length, }); updatedRooms.forEach((room) => { output.publishRoomUpdateToRoom(room.roomId, room); - logEvent("RoomUseCase", { - event: "ROOM_UPDATE", - result: "emitted", + logEvent(logScopes.ROOM_USE_CASE, { + event: roomUseCaseLogEvents.ROOM_UPDATE, + result: logResults.EMITTED, roomId: room.roomId, socketId, ownerId: room.ownerId, diff --git a/apps/server/src/logging/constants/eventNames.ts b/apps/server/src/logging/constants/eventNames.ts new file mode 100644 index 0000000..fdeeb92 --- /dev/null +++ b/apps/server/src/logging/constants/eventNames.ts @@ -0,0 +1,39 @@ +/** + * eventNames + * ログ出力で利用するイベント名定数群を提供する + */ +import { protocol } from "@repo/shared"; + +/** GameUseCaseログで利用するイベント名定数 */ +export const gameUseCaseLogEvents = { + START_GAME: protocol.SocketEvents.START_GAME, + READY_FOR_GAME: protocol.SocketEvents.READY_FOR_GAME, + GAME_START: protocol.SocketEvents.GAME_START, + GAME_END: protocol.SocketEvents.GAME_END, + DISCONNECT: protocol.SocketEvents.DISCONNECT, +} as const; + +/** RoomUseCaseログで利用するイベント名定数 */ +export const roomUseCaseLogEvents = { + JOIN_ROOM: protocol.SocketEvents.JOIN_ROOM, + DISCONNECT: protocol.SocketEvents.DISCONNECT, + ROOM_UPDATE: protocol.SocketEvents.ROOM_UPDATE, +} as const; + +/** Gameドメインサービスとループログで利用するイベント名定数 */ +export const gameDomainLogEvents = { + MOVE: protocol.SocketEvents.MOVE, + SESSION_START: "SESSION_START", + PLAYER_MOVE: "PLAYER_MOVE", + PLAYER_REMOVE: "PLAYER_REMOVE", + GAME_LOOP: "GAME_LOOP", +} as const; + +/** Roomドメインサービスログで利用するイベント名定数 */ +export const roomDomainLogEvents = { + ROOM_CREATE: "ROOM_CREATE", + PLAYER_JOIN: "PLAYER_JOIN", + PLAYER_LEAVE: "PLAYER_LEAVE", + ROOM_DELETE: "ROOM_DELETE", + OWNER_TRANSFER: "OWNER_TRANSFER", +} as const; diff --git a/apps/server/src/logging/constants/results.ts b/apps/server/src/logging/constants/results.ts new file mode 100644 index 0000000..f5ff65f --- /dev/null +++ b/apps/server/src/logging/constants/results.ts @@ -0,0 +1,36 @@ +/** + * results + * ログ出力で利用する結果値定数を提供する + */ + +/** ログ出力で利用する結果値の共通定数 */ +export const logResults = { + ACCEPTED: "accepted", + CONNECTED: "connected", + CREATED: "created", + DELETED: "deleted", + DISCONNECTED: "disconnected", + EMITTED: "emitted", + IGNORED_ALREADY_PLAYING: "ignored_already_playing", + IGNORED_ALREADY_RUNNING: "ignored_already_running", + IGNORED_DUPLICATE: "ignored_duplicate", + IGNORED_INVALID_PAYLOAD: "ignored_invalid_payload", + IGNORED_MISSING_ROOM: "ignored_missing_room", + IGNORED_NO_ROOM: "ignored_no_room", + IGNORED_PLAYER_NOT_FOUND: "ignored_player_not_found", + IGNORED_PLAYER_NOT_IN_SESSION: "ignored_player_not_in_session", + IGNORED_ROOM_FULL: "ignored_room_full", + IGNORED_ROOM_NOT_FOUND: "ignored_room_not_found", + JOINED: "joined", + PLAYER_REMOVED: "player_removed", + PROCESSED: "processed", + RECEIVED: "received", + REJECTED: "rejected", + REJECTED_DUPLICATE: "rejected_duplicate", + REJECTED_ROOM_FULL: "rejected_room_full", + REMOVED: "removed", + SESSION_DISPOSED_EMPTY_ROOM: "session_disposed_empty_room", + STARTED: "started", + STOPPED: "stopped", + TRANSFERRED: "transferred", +} as const; diff --git a/apps/server/src/logging/constants/scopes.ts b/apps/server/src/logging/constants/scopes.ts new file mode 100644 index 0000000..ca01c98 --- /dev/null +++ b/apps/server/src/logging/constants/scopes.ts @@ -0,0 +1,20 @@ +/** + * scopes + * ログ出力で利用するスコープ名定数と型を提供する + */ + +/** ログ出力で利用するスコープ名の共通定数 */ +export const logScopes = { + NETWORK: "Network", + GAME_USE_CASE: "GameUseCase", + ROOM_USE_CASE: "RoomUseCase", + GAME_LOOP: "GameLoop", + GAME_ROOM_SESSION: "GameRoomSession", + GAME_PLAYER_OPERATION_SERVICE: "GamePlayerOperationService", + GAME_SESSION_LIFECYCLE_SERVICE: "GameSessionLifecycleService", + ROOM_JOIN_SERVICE: "RoomJoinService", + ROOM_EXIT_SERVICE: "RoomExitService", +} as const; + +/** ログ出力で利用するスコープ名型 */ +export type LogScope = (typeof logScopes)[keyof typeof logScopes]; diff --git a/apps/server/src/logging/contracts/payloadByScope.ts b/apps/server/src/logging/contracts/payloadByScope.ts new file mode 100644 index 0000000..8bb3fb5 --- /dev/null +++ b/apps/server/src/logging/contracts/payloadByScope.ts @@ -0,0 +1,258 @@ +/** + * payloadByScope + * スコープごとのログペイロード型契約を提供する + */ +import { protocol } from "@repo/shared"; +import { gameDomainLogEvents, gameUseCaseLogEvents, roomDomainLogEvents, roomUseCaseLogEvents } from "../constants/eventNames"; +import { logResults } from "../constants/results"; +import { logScopes, type LogScope } from "../constants/scopes"; + +/** Network接続確立ログの契約 */ +type NetworkConnectLogPayload = { + event: typeof protocol.SocketEvents.CONNECT; + result: typeof logResults.CONNECTED; + socketId: string; +}; + +/** Network切断ログの契約 */ +type NetworkDisconnectLogPayload = { + event: typeof protocol.SocketEvents.DISCONNECT; + result: typeof logResults.DISCONNECTED; + socketId: string; +}; + +/** Network参加要求ログの契約 */ +type NetworkJoinRoomLogPayload = { + event: typeof protocol.SocketEvents.JOIN_ROOM; + result: + | typeof logResults.REJECTED_ROOM_FULL + | typeof logResults.REJECTED_DUPLICATE + | typeof logResults.IGNORED_INVALID_PAYLOAD; + socketId: string; + roomId?: string; +}; + +/** NetworkのPING不正ペイロードログ契約 */ +type NetworkPingLogPayload = { + event: typeof protocol.SocketEvents.PING; + result: typeof logResults.IGNORED_INVALID_PAYLOAD; + socketId: string; +}; + +/** NetworkのMOVE不正ペイロードログ契約 */ +type NetworkMoveLogPayload = { + event: typeof protocol.SocketEvents.MOVE; + result: typeof logResults.IGNORED_INVALID_PAYLOAD; + socketId: string; +}; + +/** Networkスコープのログ契約ユニオン */ +type NetworkLogPayload = + | NetworkConnectLogPayload + | NetworkDisconnectLogPayload + | NetworkJoinRoomLogPayload + | NetworkPingLogPayload + | NetworkMoveLogPayload; + +/** GameUseCaseのSTART_GAMEログ契約 */ +type GameUseCaseStartGameLogPayload = { + event: typeof gameUseCaseLogEvents.START_GAME; + result: + | typeof logResults.IGNORED_NO_ROOM + | typeof logResults.IGNORED_ALREADY_PLAYING + | typeof logResults.IGNORED_ROOM_NOT_FOUND + | typeof logResults.ACCEPTED; + socketId: string; + roomId?: string; +}; + +/** GameUseCaseのREADY_FOR_GAMEログ契約 */ +type GameUseCaseReadyForGameLogPayload = { + event: typeof gameUseCaseLogEvents.READY_FOR_GAME; + result: + | typeof logResults.IGNORED_MISSING_ROOM + | typeof logResults.RECEIVED; + socketId: string; + roomId?: string; +}; + +/** GameUseCaseのGAME_STARTログ契約 */ +type GameUseCaseGameStartLogPayload = { + event: typeof gameUseCaseLogEvents.GAME_START; + result: typeof logResults.EMITTED; + socketId: string; + roomId: string; +}; + +/** GameUseCaseのGAME_ENDログ契約 */ +type GameUseCaseGameEndLogPayload = { + event: typeof gameUseCaseLogEvents.GAME_END; + result: typeof logResults.EMITTED; + roomId: string; +}; + +/** GameUseCaseのDISCONNECTログ契約 */ +type GameUseCaseDisconnectLogPayload = { + event: typeof gameUseCaseLogEvents.DISCONNECT; + result: typeof logResults.PLAYER_REMOVED; + socketId: string; +}; + +/** GameUseCaseスコープのログ契約ユニオン */ +type GameUseCaseLogPayload = + | GameUseCaseStartGameLogPayload + | GameUseCaseReadyForGameLogPayload + | GameUseCaseGameStartLogPayload + | GameUseCaseGameEndLogPayload + | GameUseCaseDisconnectLogPayload; + +/** RoomUseCaseのJOIN_ROOMログ契約 */ +type RoomUseCaseJoinRoomLogPayload = { + event: typeof roomUseCaseLogEvents.JOIN_ROOM; + result: + | typeof logResults.RECEIVED + | typeof logResults.REJECTED; + socketId: string; + roomId: string; +}; + +/** RoomUseCaseのDISCONNECTログ契約 */ +type RoomUseCaseDisconnectLogPayload = { + event: typeof roomUseCaseLogEvents.DISCONNECT; + result: typeof logResults.PROCESSED; + socketId: string; +}; + +/** RoomUseCaseのROOM_UPDATEログ契約 */ +type RoomUseCaseRoomUpdateLogPayload = { + event: typeof roomUseCaseLogEvents.ROOM_UPDATE; + result: typeof logResults.EMITTED; + socketId: string; + roomId: string; +}; + +/** RoomUseCaseスコープのログ契約ユニオン */ +type RoomUseCaseLogPayload = + | RoomUseCaseJoinRoomLogPayload + | RoomUseCaseDisconnectLogPayload + | RoomUseCaseRoomUpdateLogPayload; + +/** GamePlayerOperationServiceのPLAYER_MOVEログ契約 */ +type GamePlayerOperationServiceMoveLogPayload = { + event: typeof gameDomainLogEvents.PLAYER_MOVE; + result: typeof logResults.IGNORED_PLAYER_NOT_IN_SESSION; + socketId: string; +}; + +/** GamePlayerOperationServiceのPLAYER_REMOVEログ契約 */ +type GamePlayerOperationServiceRemoveLogPayload = { + event: typeof gameDomainLogEvents.PLAYER_REMOVE; + result: + | typeof logResults.IGNORED_PLAYER_NOT_IN_SESSION + | typeof logResults.SESSION_DISPOSED_EMPTY_ROOM; + socketId: string; + roomId?: string; +}; + +/** GamePlayerOperationServiceスコープのログ契約ユニオン */ +type GamePlayerOperationServiceLogPayload = + | GamePlayerOperationServiceMoveLogPayload + | GamePlayerOperationServiceRemoveLogPayload; + +/** GameLoopスコープのログ契約 */ +type GameLoopLogPayload = { + event: typeof gameDomainLogEvents.GAME_LOOP; + result: + | typeof logResults.STARTED + | typeof logResults.STOPPED; + roomId: string; +}; + +/** GameRoomSessionスコープのログ契約 */ +type GameRoomSessionLogPayload = { + event: typeof gameDomainLogEvents.MOVE; + result: + | typeof logResults.IGNORED_PLAYER_NOT_FOUND + | typeof logResults.IGNORED_INVALID_PAYLOAD; + roomId: string; + socketId: string; +}; + +/** GameSessionLifecycleServiceスコープのログ契約 */ +type GameSessionLifecycleServiceLogPayload = { + event: typeof gameDomainLogEvents.SESSION_START; + result: + | typeof logResults.IGNORED_ALREADY_RUNNING + | typeof logResults.STARTED; + roomId: string; +}; + +/** RoomJoinServiceのROOM_CREATEログ契約 */ +type RoomJoinServiceRoomCreateLogPayload = { + event: typeof roomDomainLogEvents.ROOM_CREATE; + result: typeof logResults.CREATED; + roomId: string; + socketId: string; +}; + +/** RoomJoinServiceのPLAYER_JOINログ契約 */ +type RoomJoinServicePlayerJoinLogPayload = { + event: typeof roomDomainLogEvents.PLAYER_JOIN; + result: + | typeof logResults.IGNORED_DUPLICATE + | typeof logResults.IGNORED_ROOM_FULL + | typeof logResults.JOINED; + roomId: string; + socketId: string; +}; + +/** RoomJoinServiceスコープのログ契約ユニオン */ +type RoomJoinServiceLogPayload = + | RoomJoinServiceRoomCreateLogPayload + | RoomJoinServicePlayerJoinLogPayload; + +/** RoomExitServiceのPLAYER_LEAVEログ契約 */ +type RoomExitServicePlayerLeaveLogPayload = { + event: typeof roomDomainLogEvents.PLAYER_LEAVE; + result: typeof logResults.REMOVED; + roomId: string; + socketId: string; +}; + +/** RoomExitServiceのROOM_DELETEログ契約 */ +type RoomExitServiceRoomDeleteLogPayload = { + event: typeof roomDomainLogEvents.ROOM_DELETE; + result: typeof logResults.DELETED; + roomId: string; + socketId: string; +}; + +/** RoomExitServiceのOWNER_TRANSFERログ契約 */ +type RoomExitServiceOwnerTransferLogPayload = { + event: typeof roomDomainLogEvents.OWNER_TRANSFER; + result: typeof logResults.TRANSFERRED; + roomId: string; + socketId: string; +}; + +/** RoomExitServiceスコープのログ契約ユニオン */ +type RoomExitServiceLogPayload = + | RoomExitServicePlayerLeaveLogPayload + | RoomExitServiceRoomDeleteLogPayload + | RoomExitServiceOwnerTransferLogPayload; + +/** スコープごとの event/result と必須項目の型契約 */ +export type LogPayloadByScope = { + [logScopes.NETWORK]: NetworkLogPayload; + [logScopes.GAME_USE_CASE]: GameUseCaseLogPayload; + [logScopes.ROOM_USE_CASE]: RoomUseCaseLogPayload; + [logScopes.GAME_LOOP]: GameLoopLogPayload; + [logScopes.GAME_ROOM_SESSION]: GameRoomSessionLogPayload; + [logScopes.GAME_PLAYER_OPERATION_SERVICE]: GamePlayerOperationServiceLogPayload; + [logScopes.GAME_SESSION_LIFECYCLE_SERVICE]: GameSessionLifecycleServiceLogPayload; + [logScopes.ROOM_JOIN_SERVICE]: RoomJoinServiceLogPayload; + [logScopes.ROOM_EXIT_SERVICE]: RoomExitServiceLogPayload; +}; + +/** スコープごとのログペイロード型を参照するユーティリティ */ +export type LogPayloadOf = LogPayloadByScope[TScope]; diff --git a/apps/server/src/logging/index.ts b/apps/server/src/logging/index.ts new file mode 100644 index 0000000..af0031a --- /dev/null +++ b/apps/server/src/logging/index.ts @@ -0,0 +1,25 @@ +/** + * index + * logging配下の公開APIを集約して再公開する + */ + +/** ログ出力関数を再公開 */ +export { logEvent } from "./logger"; + +/** ログスコープ定数と型を再公開 */ +export { logScopes } from "./constants/scopes"; +export type { LogScope } from "./constants/scopes"; + +/** ログ結果値定数を再公開 */ +export { logResults } from "./constants/results"; + +/** ログイベント名定数群を再公開 */ +export { + gameUseCaseLogEvents, + roomUseCaseLogEvents, + gameDomainLogEvents, + roomDomainLogEvents, +} from "./constants/eventNames"; + +/** ログペイロード契約型を再公開 */ +export type { LogPayloadByScope, LogPayloadOf } from "./contracts/payloadByScope"; diff --git a/apps/server/src/logging/logEvent.ts b/apps/server/src/logging/logEvent.ts deleted file mode 100644 index c521ab8..0000000 --- a/apps/server/src/logging/logEvent.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * logEvent - * 共通ログ出力で利用するイベントログ関数を提供する - */ -type LogEventPayload = { - event: string; - result: string; - socketId?: string; - roomId?: string; - [key: string]: unknown; -}; - -/** スコープ名とイベント情報を標準出力へ記録する */ -export const logEvent = (scope: string, payload: LogEventPayload) => { - console.log(`[${scope}]`, payload); -}; diff --git a/apps/server/src/logging/logger.ts b/apps/server/src/logging/logger.ts new file mode 100644 index 0000000..a4dac46 --- /dev/null +++ b/apps/server/src/logging/logger.ts @@ -0,0 +1,22 @@ +/** + * logger + * 共通ログ出力で利用するイベントログ関数を提供する + */ +import type { LogPayloadByScope } from "./contracts/payloadByScope"; +import type { LogScope } from "./constants/scopes"; + +type LoggerPayload = { + socketId?: string; + roomId?: string; + [key: string]: unknown; +}; + +/** スコープ名とイベント情報を標準出力へ記録する */ +export const logEvent = < + TScope extends LogScope, +>( + scope: TScope, + payload: LoggerPayload & LogPayloadByScope[TScope] +) => { + console.log(`[${scope}]`, payload); +}; diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index f96235d..b7bd305 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -15,10 +15,10 @@ } from "@server/domains/game/application/ports/gameUseCasePorts"; import { movePlayerUseCase } from "@server/domains/game/application/useCases/movePlayerUseCase"; import { pingUseCase } from "@server/domains/game/application/useCases/pingUseCase"; -import { logEvent } from "@server/logging/logEvent"; import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler"; import { isMovePayload, isPingPayload } from "@server/network/validation/socketPayloadValidators"; import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge"; +import { createPayloadGuard } from "@server/network/handlers/payloadGuard"; import { createGameOutputAdapter } from "./createGameOutputAdapter"; /** ゲーム受信イベントごとの入力検証関数を保持するテーブル */ @@ -37,15 +37,19 @@ const common = createCommonHandlerContext(io, socket); const gameOutputAdapter = createGameOutputAdapter(common); const { onEvent } = createServerSocketOnBridge(socket); + const { guardOnEvent } = createPayloadGuard(socket.id); + const guardPingPayload = guardOnEvent( + protocol.SocketEvents.PING, + gamePayloadValidators[protocol.SocketEvents.PING] + ); + const guardMovePayload = guardOnEvent( + protocol.SocketEvents.MOVE, + gamePayloadValidators[protocol.SocketEvents.MOVE] + ); // 遅延計測用のPINGを検証しPONGを返す onEvent(protocol.SocketEvents.PING, (clientTime) => { - if (!gamePayloadValidators[protocol.SocketEvents.PING](clientTime)) { - logEvent("Network", { - event: "PING", - result: "ignored_invalid_payload", - socketId: socket.id, - }); + if (!guardPingPayload(clientTime)) { return; } @@ -77,12 +81,7 @@ // 移動入力を検証しプレイヤー移動ユースケースへ連携する onEvent(protocol.SocketEvents.MOVE, (data) => { - if (!gamePayloadValidators[protocol.SocketEvents.MOVE](data)) { - logEvent("Network", { - event: "MOVE", - result: "ignored_invalid_payload", - socketId: socket.id, - }); + if (!guardMovePayload(data)) { return; } diff --git a/apps/server/src/network/handlers/payloadGuard.ts b/apps/server/src/network/handlers/payloadGuard.ts new file mode 100644 index 0000000..dc308c4 --- /dev/null +++ b/apps/server/src/network/handlers/payloadGuard.ts @@ -0,0 +1,71 @@ +/** + * payloadGuard + * 受信ペイロード検証と不正時ログ記録を共通化するガード生成を担う + */ +import { protocol } from "@repo/shared"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes } from "@server/logging/index"; + +type PayloadValidator = (value: unknown) => value is TPayload; +type PayloadGuardEventName = + | typeof protocol.SocketEvents.JOIN_ROOM + | typeof protocol.SocketEvents.PING + | typeof protocol.SocketEvents.MOVE; +type EventBoundPayloadGuard = (payload: unknown) => payload is TPayload; + +/** + * 受信ペイロードを検証し,不正時は共通ログを記録するガード関数を生成する + */ +export const createPayloadGuard = (socketId: string) => { + const isValidPayload = ( + event: TEvent, + payload: unknown, + validator: PayloadValidator + ): payload is TPayload => { + if (validator(payload)) { + return true; + } + + switch (event) { + case protocol.SocketEvents.JOIN_ROOM: + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.JOIN_ROOM, + result: logResults.IGNORED_INVALID_PAYLOAD, + socketId, + }); + break; + + case protocol.SocketEvents.PING: + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.PING, + result: logResults.IGNORED_INVALID_PAYLOAD, + socketId, + }); + break; + + case protocol.SocketEvents.MOVE: + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.MOVE, + result: logResults.IGNORED_INVALID_PAYLOAD, + socketId, + }); + break; + } + + return false; + }; + + const guardOnEvent = ( + event: TEvent, + validator: PayloadValidator + ): EventBoundPayloadGuard => { + return (payload: unknown): payload is TPayload => { + return isValidPayload(event, payload, validator); + }; + }; + + return { + isValidPayload, + guardOnEvent, + }; +}; diff --git a/apps/server/src/network/handlers/registerConnectionHandlers.ts b/apps/server/src/network/handlers/registerConnectionHandlers.ts index fc7fad4..66850a8 100644 --- a/apps/server/src/network/handlers/registerConnectionHandlers.ts +++ b/apps/server/src/network/handlers/registerConnectionHandlers.ts @@ -5,17 +5,13 @@ import { Server, Socket } from "socket.io"; import { protocol } from "@repo/shared"; import { disconnectCoordinator } from "@server/application/coordinators/disconnectCoordinator"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes } from "@server/logging/index"; import { registerGameHandlers } from "./GameHandler"; import { registerRoomHandlers } from "./RoomHandler"; import { createGameDisconnectOutputAdapter } from "./game/createGameOutputAdapter"; import { createRoomDisconnectOutputAdapter } from "./room/createRoomOutputAdapter"; import type { - ConnectionGamePort, - ConnectionRoomPort, - DisconnectCoordinatorPortBundle, - DisconnectGamePort, - DisconnectRoomHandlerPort, RegisterConnectionHandlersParams, } from "../types/connectionPorts"; @@ -27,39 +23,32 @@ }: RegisterConnectionHandlersParams) => { const gameDisconnectOutputAdapter = createGameDisconnectOutputAdapter(io); const roomDisconnectOutputAdapter = createRoomDisconnectOutputAdapter(io); - const connectionGameManager: ConnectionGamePort = gameManager; - const disconnectGameManager: DisconnectGamePort = gameManager; - const connectionRoomManager: ConnectionRoomPort = roomManager; - const disconnectRoomManager: DisconnectRoomHandlerPort = roomManager; - const disconnectPorts: DisconnectCoordinatorPortBundle = { - gameManager: disconnectGameManager, - roomManager: disconnectRoomManager, - gameOutput: gameDisconnectOutputAdapter, - roomOutput: roomDisconnectOutputAdapter, - }; io.on(protocol.SocketEvents.CONNECT, (socket: Socket) => { // 接続ログを記録してドメイン別ハンドラを登録する - logEvent("Network", { - event: "CONNECT", - result: "connected", + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.CONNECT, + result: logResults.CONNECTED, socketId: socket.id, }); - registerRoomHandlers(io, socket, connectionRoomManager); - registerGameHandlers(io, socket, connectionGameManager, connectionRoomManager); + registerRoomHandlers(io, socket, roomManager); + registerGameHandlers(io, socket, gameManager, roomManager); socket.on(protocol.SocketEvents.DISCONNECT, () => { // 切断ログ記録後にドメイン別の後処理を実行する - logEvent("Network", { - event: "DISCONNECT", - result: "disconnected", + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.DISCONNECT, + result: logResults.DISCONNECTED, socketId: socket.id, }); disconnectCoordinator({ socketId: socket.id, - ...disconnectPorts, + gameManager, + roomManager, + gameOutput: gameDisconnectOutputAdapter, + roomOutput: roomDisconnectOutputAdapter, }); }); }); diff --git a/apps/server/src/network/handlers/room/registerRoomHandlers.ts b/apps/server/src/network/handlers/room/registerRoomHandlers.ts index 5c0145f..3f1e989 100644 --- a/apps/server/src/network/handlers/room/registerRoomHandlers.ts +++ b/apps/server/src/network/handlers/room/registerRoomHandlers.ts @@ -6,8 +6,10 @@ import { protocol } from "@repo/shared"; import type { JoinRoomPort } from "@server/domains/room/application/ports/roomUseCasePorts"; import { joinRoomUseCase } from "@server/domains/room/application/useCases/joinRoomUseCase"; -import { logEvent } from "@server/logging/logEvent"; +import { logEvent } from "@server/logging/logger"; +import { logResults, logScopes } from "@server/logging/index"; import { createCommonHandlerContext } from "@server/network/handlers/CommonHandler"; +import { createPayloadGuard } from "@server/network/handlers/payloadGuard"; import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge"; import { isJoinRoomPayload } from "@server/network/validation/socketPayloadValidators"; import { createRoomOutputAdapter } from "./createRoomOutputAdapter"; @@ -26,15 +28,15 @@ const common = createCommonHandlerContext(io, socket); const roomOutputAdapter = createRoomOutputAdapter(common); const { onEvent } = createServerSocketOnBridge(socket); + const { guardOnEvent } = createPayloadGuard(socket.id); + const guardJoinRoomPayload = guardOnEvent( + protocol.SocketEvents.JOIN_ROOM, + roomPayloadValidators[protocol.SocketEvents.JOIN_ROOM] + ); // 参加要求のペイロード検証と参加処理を実行する onEvent(protocol.SocketEvents.JOIN_ROOM, async (data) => { - if (!roomPayloadValidators[protocol.SocketEvents.JOIN_ROOM](data)) { - logEvent("Network", { - event: "JOIN_ROOM", - result: "ignored_invalid_payload", - socketId: socket.id, - }); + if (!guardJoinRoomPayload(data)) { return; } @@ -50,18 +52,18 @@ // 参加拒否時は理由を通知する switch (joinResult.status) { case "full": - logEvent("Network", { - event: "JOIN_ROOM", - result: "rejected_room_full", + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.JOIN_ROOM, + result: logResults.REJECTED_ROOM_FULL, roomId, socketId: socket.id, }); return; case "duplicate": - logEvent("Network", { - event: "JOIN_ROOM", - result: "rejected_duplicate", + logEvent(logScopes.NETWORK, { + event: protocol.SocketEvents.JOIN_ROOM, + result: logResults.REJECTED_DUPLICATE, roomId, socketId: socket.id, }); @@ -70,9 +72,9 @@ case "joined": await socket.join(roomId); roomOutputAdapter.publishRoomUpdateToRoom(roomId, joinResult.room); - logEvent("RoomUseCase", { - event: "ROOM_UPDATE", - result: "emitted", + logEvent(logScopes.ROOM_USE_CASE, { + event: protocol.SocketEvents.ROOM_UPDATE, + result: logResults.EMITTED, roomId, socketId: socket.id, ownerId: joinResult.room.ownerId, diff --git a/apps/server/src/network/handlers/socketEventBridge.ts b/apps/server/src/network/handlers/socketEventBridge.ts index 2facfe6..977ef98 100644 --- a/apps/server/src/network/handlers/socketEventBridge.ts +++ b/apps/server/src/network/handlers/socketEventBridge.ts @@ -7,15 +7,36 @@ import { createSocketEventBridge, type ClientToServerEventPayloadMap, + type SocketBridgeTarget, type ServerToClientEventPayloadMap, } from "@repo/shared"; /** サーバー向けの型付きソケットイベント bridge を生成する */ export const createServerSocketOnBridge = (socket: Socket) => { + const bridgeTarget: SocketBridgeTarget = { + on: (event, callback) => { + socket.on(event, callback); + }, + once: (event, callback) => { + socket.once(event, callback); + }, + off: (event, callback) => { + socket.off(event, callback); + }, + emit: (event, payload) => { + if (payload === undefined) { + socket.emit(event); + return; + } + + socket.emit(event, payload); + }, + }; + const { onEvent, onceEvent } = createSocketEventBridge< ClientToServerEventPayloadMap, ServerToClientEventPayloadMap - >(socket as any); + >(bridgeTarget); return { onEvent, diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index f7973ac..1b3fd0f 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -41,5 +41,7 @@ } from "./protocol/events"; /** ソケットイベントブリッジ生成関数を再公開 */ export { createSocketEventBridge } from "./protocol/socketEventBridge"; +/** ソケットイベントブリッジ生成に必要な最小インターフェースを再公開 */ +export type { SocketBridgeTarget } from "./protocol/socketEventBridge"; /** 共有設定値を再公開 */ export * as config from "./config"; \ No newline at end of file diff --git a/packages/shared/src/protocol/socketEventBridge.ts b/packages/shared/src/protocol/socketEventBridge.ts index f171a3f..a5f61e2 100644 --- a/packages/shared/src/protocol/socketEventBridge.ts +++ b/packages/shared/src/protocol/socketEventBridge.ts @@ -1,8 +1,13 @@ +/** + * socketEventBridge + * ソケットイベントの受信購読と送信を型安全に橋渡しする共通ブリッジを提供する + */ type EventPayloadMap = Record; type EventNameOf = Extract; -type SocketBridgeTarget = { +/** ソケットブリッジ生成に必要な最小インターフェース */ +export type SocketBridgeTarget = { on: (event: string, callback: (payload: unknown) => void) => void; once: (event: string, callback: (payload: unknown) => void) => void; off: (event: string, callback: (payload: unknown) => void) => void; @@ -10,7 +15,7 @@ }; /** - * 受信・送信イベントの型マップを指定して、Socket.IOブリッジを生成する + * 受信・送信イベントの型マップを指定して,Socket.IOブリッジを生成する */ export const createSocketEventBridge = < TInboundMap extends EventPayloadMap, @@ -20,32 +25,32 @@ event: TEvent, callback: (payload: TInboundMap[TEvent]) => void ) => { - (socket as any).on(event, callback); + socket.on(event, callback as (payload: unknown) => void); }; const onceEvent = >( event: TEvent, callback: (payload: TInboundMap[TEvent]) => void ) => { - (socket as any).once(event, callback); + socket.once(event, callback as (payload: unknown) => void); }; const offEvent = >( event: TEvent, callback: (payload: TInboundMap[TEvent]) => void ) => { - (socket as any).off(event, callback); + socket.off(event, callback as (payload: unknown) => void); }; function emitEvent>(event: TEvent): void; function emitEvent>(event: TEvent, payload: TOutboundMap[TEvent]): void; function emitEvent>(event: TEvent, payload?: TOutboundMap[TEvent]): void { if (payload === undefined) { - (socket as any).emit(event); + socket.emit(event); return; } - (socket as any).emit(event, payload); + socket.emit(event, payload as unknown); } return {