diff --git a/apps/client/src/app.tsx b/apps/client/src/app.tsx index 37a01a3..8b37e31 100644 --- a/apps/client/src/app.tsx +++ b/apps/client/src/app.tsx @@ -8,7 +8,7 @@ import { ResultScene } from "./scenes/result/ResultScene"; import { LandscapeOnlyGate } from "./components/LandscapeOnlyGate"; -import { appConsts } from "@repo/shared"; +import { domain } from "@repo/shared"; export default function App() { const { @@ -25,7 +25,7 @@ let scene = ; // タイトル画面分岐 - if (scenePhase === appConsts.ScenePhase.TITLE) { + if (scenePhase === domain.app.ScenePhase.TITLE) { scene = ( void; + requestJoin: (payload: domain.room.JoinRoomPayload) => void; returnToTitle: (options?: { leaveRoom?: boolean }) => void; }; @@ -28,7 +28,7 @@ }; type JoinFailureReason = - | roomTypes.JoinRoomRejectedPayload["reason"] + | domain.room.JoinRoomRejectedPayload["reason"] | "timeout"; type JoinFailure = { @@ -65,16 +65,16 @@ /** アプリ全体のシーン状態と参加要求フローを管理するフック */ export const useAppFlow = (): AppFlowState => { - const [scenePhase, setScenePhase] = useState( - appConsts.ScenePhase.TITLE, + const [scenePhase, setScenePhase] = useState( + domain.app.ScenePhase.TITLE, ); - const [room, setRoom] = useState(null); + const [room, setRoom] = useState(null); const [myId, setMyId] = useState(null); const [gameResult, setGameResult] = useState(null); const [joinState, dispatchJoin] = useReducer(joinReducer, initialJoinState); const joinTimeoutRef = useRef | null>(null); const joinRejectedHandlerRef = useRef< - ((payload: roomTypes.JoinRoomRejectedPayload) => void) | null + ((payload: domain.room.JoinRoomRejectedPayload) => void) | null >(null); const clearJoinRejectedHandler = useCallback(() => { @@ -128,7 +128,7 @@ ); const requestJoin = useCallback( - (payload: roomTypes.JoinRoomPayload) => { + (payload: domain.room.JoinRoomPayload) => { if (joinState.isJoining) { return; } @@ -137,7 +137,7 @@ dispatchJoin({ type: "start" }); const handleJoinRejected = ( - payload: roomTypes.JoinRoomRejectedPayload, + payload: domain.room.JoinRoomRejectedPayload, ) => { completeJoinRequest({ reason: payload.reason, @@ -162,7 +162,7 @@ completeJoinRequest(); setRoom(null); setGameResult(null); - setScenePhase(appConsts.ScenePhase.TITLE); + setScenePhase(domain.app.ScenePhase.TITLE); if (!options?.leaveRoom) { return; diff --git a/apps/client/src/hooks/useSocketSubscriptions.ts b/apps/client/src/hooks/useSocketSubscriptions.ts index 33f2df7..19a2ef8 100644 --- a/apps/client/src/hooks/useSocketSubscriptions.ts +++ b/apps/client/src/hooks/useSocketSubscriptions.ts @@ -5,20 +5,20 @@ */ import { useEffect } from "react"; import { socketManager } from "@client/network/SocketManager"; -import { appConsts } from "@repo/shared"; -import type { appTypes, roomTypes, GameResultPayload } from "@repo/shared"; +import { domain } from "@repo/shared"; +import type { GameResultPayload } from "@repo/shared"; type UseSocketSubscriptionsParams = { completeJoinRequest: () => void; setGameResult: (payload: GameResultPayload | null) => void; setMyId: (id: string | null) => void; - setRoom: (room: roomTypes.Room | null) => void; - setScenePhase: (phase: appTypes.ScenePhase) => void; + setRoom: (room: domain.room.Room | null) => void; + setScenePhase: (phase: domain.app.ScenePhaseType) => void; }; type AppSocketHandlers = { handleConnect: (id: string) => void; - handleRoomUpdate: (updatedRoom: roomTypes.Room) => void; + handleRoomUpdate: (updatedRoom: domain.room.Room) => void; handleGameStart: () => void; handleGameResult: (payload: GameResultPayload) => void; }; @@ -77,20 +77,20 @@ setMyId(id); }, - handleRoomUpdate: (updatedRoom: roomTypes.Room) => { + handleRoomUpdate: (updatedRoom: domain.room.Room) => { completeJoinRequest(); setRoom(updatedRoom); - setScenePhase(appConsts.ScenePhase.LOBBY); + setScenePhase(domain.app.ScenePhase.LOBBY); }, handleGameStart: () => { setGameResult(null); - setScenePhase(appConsts.ScenePhase.PLAYING); + setScenePhase(domain.app.ScenePhase.PLAYING); }, handleGameResult: (payload: GameResultPayload) => { setGameResult(payload); - setScenePhase(appConsts.ScenePhase.RESULT); + setScenePhase(domain.app.ScenePhase.RESULT); }, }; diff --git a/apps/client/src/network/handlers/CommonHandler.ts b/apps/client/src/network/handlers/CommonHandler.ts index 9f08170..840f99c 100644 --- a/apps/client/src/network/handlers/CommonHandler.ts +++ b/apps/client/src/network/handlers/CommonHandler.ts @@ -4,7 +4,7 @@ * connect イベントをアプリ用の id 通知に変換する */ import type { Socket } from "socket.io-client"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { ConnectionLifecyclePayloadOf } from "@repo/shared"; import { createClientSocketEventBridge } from "./socketEventBridge"; diff --git a/apps/client/src/network/handlers/GameHandler.ts b/apps/client/src/network/handlers/GameHandler.ts index c5f42c6..51bc03a 100644 --- a/apps/client/src/network/handlers/GameHandler.ts +++ b/apps/client/src/network/handlers/GameHandler.ts @@ -4,7 +4,7 @@ * シーン層が利用する通信操作を集約する */ import type { Socket } from "socket.io-client"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { BombHitReportPayload, BombPlacedAckPayload, diff --git a/apps/client/src/network/handlers/LobbyHandler.ts b/apps/client/src/network/handlers/LobbyHandler.ts index 6e9c994..1b1d70e 100644 --- a/apps/client/src/network/handlers/LobbyHandler.ts +++ b/apps/client/src/network/handlers/LobbyHandler.ts @@ -4,7 +4,7 @@ * ルーム更新購読とゲーム開始要求送信を提供する */ import type { Socket } from "socket.io-client"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { ServerToClientPayloadOf } from "@repo/shared"; import { createClientSocketEventBridge } from "./socketEventBridge"; diff --git a/apps/client/src/network/handlers/TitleHandler.ts b/apps/client/src/network/handlers/TitleHandler.ts index 06b18d8..456cdc0 100644 --- a/apps/client/src/network/handlers/TitleHandler.ts +++ b/apps/client/src/network/handlers/TitleHandler.ts @@ -4,7 +4,7 @@ * ルーム参加フローの送受信イベントを集約する */ import type { Socket } from "socket.io-client"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { ClientToServerPayloadOf, ServerToClientPayloadOf } from "@repo/shared"; import { createClientSocketEventBridge } from "./socketEventBridge"; diff --git a/apps/client/src/network/handlers/socketEventBridge.ts b/apps/client/src/network/handlers/socketEventBridge.ts index 143fe00..7c7f602 100644 --- a/apps/client/src/network/handlers/socketEventBridge.ts +++ b/apps/client/src/network/handlers/socketEventBridge.ts @@ -8,6 +8,7 @@ createSocketEventBridge, type ClientToServerEventPayloadMap, type ConnectionLifecycleEventPayloadMap, + type SocketBridgeTarget, type ServerToClientEventPayloadMap, } from "@repo/shared"; @@ -17,10 +18,30 @@ /** クライアント向けの型付きソケットイベント bridge を生成する */ export const createClientSocketEventBridge = (socket: Socket) => { + const bridgeTarget: SocketBridgeTarget = { + on: (event: string, callback: (payload: TPayload) => void) => { + socket.on(event, callback as (payload: unknown) => void); + }, + once: (event: string, callback: (payload: TPayload) => void) => { + socket.once(event, callback as (payload: unknown) => void); + }, + off: (event: string, callback: (payload: TPayload) => void) => { + socket.off(event, callback as (payload: unknown) => void); + }, + emit: (event: string, payload?: unknown) => { + if (payload === undefined) { + socket.emit(event); + return; + } + + socket.emit(event, payload); + }, + }; + const { onEvent, onceEvent, offEvent, emitEvent } = createSocketEventBridge< ClientInboundEventPayloadMap, ClientToServerEventPayloadMap - >(socket as any); + >(bridgeTarget); return { onEvent, diff --git a/apps/client/src/scenes/game/entities/map/GameMapController.ts b/apps/client/src/scenes/game/entities/map/GameMapController.ts index 6213174..da2d52c 100644 --- a/apps/client/src/scenes/game/entities/map/GameMapController.ts +++ b/apps/client/src/scenes/game/entities/map/GameMapController.ts @@ -3,7 +3,7 @@ * 外部からのマップ更新入力をModelとViewへ仲介するコントローラー * 全体更新と差分更新を統一的に扱い,描画同期を提供する */ -import type { gridMapTypes } from '@repo/shared'; +import type { domain } from '@repo/shared'; import type { Container } from 'pixi.js'; import { AppearanceResolver } from '@client/scenes/game/application/AppearanceResolver'; import { GameMapModel } from './GameMapModel'; @@ -29,13 +29,13 @@ } /** 全体マップ状態を反映する */ - public updateMapState(state: gridMapTypes.MapState): void { + public updateMapState(state: domain.gridMap.MapState): void { this.model.applyMapState(state); this.view.renderAll(this.resolveAllCellColors(this.model.getAllTeamIds())); } /** 差分セル更新を反映する */ - public updateCells(updates: gridMapTypes.CellUpdate[]): void { + public updateCells(updates: domain.gridMap.CellUpdate[]): void { this.model.applyUpdates(updates); updates.forEach(({ index }) => { diff --git a/apps/client/src/scenes/game/entities/map/GameMapModel.ts b/apps/client/src/scenes/game/entities/map/GameMapModel.ts index 75b490e..886bdbb 100644 --- a/apps/client/src/scenes/game/entities/map/GameMapModel.ts +++ b/apps/client/src/scenes/game/entities/map/GameMapModel.ts @@ -4,7 +4,7 @@ * 全体更新と差分更新を適用して描画入力用の状態を保持する */ import { config } from '@client/config'; -import type { gridMapTypes } from '@repo/shared'; +import type { domain } from '@repo/shared'; /** マップセル状態の計算責務を担うモデル */ export class GameMapModel { @@ -17,7 +17,7 @@ } /** 全体マップ状態を適用する */ - public applyMapState(state: gridMapTypes.MapState): void { + public applyMapState(state: domain.gridMap.MapState): void { const maxLength = Math.min(this.cellTeamIds.length, state.gridColors.length); for (let index = 0; index < maxLength; index++) { this.cellTeamIds[index] = state.gridColors[index]; @@ -25,7 +25,7 @@ } /** 差分セル更新を適用する */ - public applyUpdates(updates: gridMapTypes.CellUpdate[]): void { + public applyUpdates(updates: domain.gridMap.CellUpdate[]): void { updates.forEach(({ index, teamId }) => { if (!this.isValidIndex(index)) return; this.cellTeamIds[index] = teamId; diff --git a/apps/client/src/scenes/game/entities/player/PlayerController.ts b/apps/client/src/scenes/game/entities/player/PlayerController.ts index 3543d25..fa4467c 100644 --- a/apps/client/src/scenes/game/entities/player/PlayerController.ts +++ b/apps/client/src/scenes/game/entities/player/PlayerController.ts @@ -3,7 +3,7 @@ * 外部入出力とModel/Viewの橋渡しを担うコントローラー群 * ローカル入力適用,リモート更新適用,描画同期を分離して扱う */ -import type { playerTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import { config } from "@client/config"; import { AppearanceResolver } from "@client/scenes/game/application/AppearanceResolver"; import { BombHitBlinkRenderer } from "@client/scenes/game/entities/bomb/BombHitBlinkRenderer"; @@ -18,7 +18,7 @@ }; /** リモート移動更新を表す型 */ -export type RemoteUpdate = Partial; +export type RemoteUpdate = Partial; /** * ローカル用コントローラーとリモート用コントローラーの共通基底 @@ -30,7 +30,7 @@ /** 共通初期化としてModelとViewを生成する */ protected constructor( - data: playerTypes.PlayerData, + data: domain.player.PlayerData, isLocal: boolean, appearanceResolver: AppearanceResolver, ) { @@ -57,12 +57,12 @@ } /** 現在座標を取得する */ - public getPosition(): playerTypes.MovePayload { + public getPosition(): domain.player.MovePayload { return this.model.getPosition(); } /** 外部送信用スナップショットを取得する */ - public getSnapshot(): playerTypes.PlayerData { + public getSnapshot(): domain.player.PlayerData { return this.model.getSnapshot(); } @@ -82,7 +82,7 @@ export class LocalPlayerController extends BasePlayerController { /** ローカルプレイヤー用コントローラーを初期化する */ constructor( - data: playerTypes.PlayerData, + data: domain.player.PlayerData, appearanceResolver: AppearanceResolver, ) { super(data, true, appearanceResolver); @@ -104,7 +104,7 @@ export class RemotePlayerController extends BasePlayerController { /** リモートプレイヤー用コントローラーを初期化する */ constructor( - data: playerTypes.PlayerData, + data: domain.player.PlayerData, appearanceResolver: AppearanceResolver, ) { super(data, false, appearanceResolver); diff --git a/apps/client/src/scenes/game/entities/player/PlayerModel.ts b/apps/client/src/scenes/game/entities/player/PlayerModel.ts index 1e2882f..630f248 100644 --- a/apps/client/src/scenes/game/entities/player/PlayerModel.ts +++ b/apps/client/src/scenes/game/entities/player/PlayerModel.ts @@ -4,7 +4,7 @@ * ローカル移動,リモート目標座標,送信スナップショットを管理する */ import { config } from "@client/config"; -import type { playerTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; /** プレイヤーの座標計算と補間計算を管理するモデル */ export class PlayerModel { @@ -18,7 +18,7 @@ private targetGridY: number; /** 共有プレイヤー情報から初期状態を構築する */ - constructor(data: playerTypes.PlayerData) { + constructor(data: domain.player.PlayerData) { this.id = data.id; this.name = data.name; this.teamId = data.teamId; @@ -29,12 +29,12 @@ } /** 現在座標を取得する */ - public getPosition(): playerTypes.MovePayload { + public getPosition(): domain.player.MovePayload { return { x: this.gridX, y: this.gridY }; } /** 送信用スナップショットを取得する */ - public getSnapshot(): playerTypes.PlayerData { + public getSnapshot(): domain.player.PlayerData { return { id: this.id, name: this.name, @@ -64,7 +64,7 @@ } /** リモート更新の目標座標を設定する */ - public setRemoteTarget(update: Partial): void { + public setRemoteTarget(update: Partial): void { if (update.x !== undefined && this.isFiniteNumber(update.x)) this.targetGridX = update.x; if (update.y !== undefined && this.isFiniteNumber(update.y)) diff --git a/apps/client/src/scenes/lobby/LobbyScene.tsx b/apps/client/src/scenes/lobby/LobbyScene.tsx index 12ae64a..483505d 100644 --- a/apps/client/src/scenes/lobby/LobbyScene.tsx +++ b/apps/client/src/scenes/lobby/LobbyScene.tsx @@ -1,8 +1,8 @@ import { useEffect, useMemo, useState } from "react"; -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; type Props = { - room: roomTypes.Room | null; + room: domain.room.Room | null; myId: string | null; onStart: (targetPlayerCount: number) => void; onBackToTitle: () => void; @@ -278,7 +278,7 @@ paddingRight: "10px", }} > - {room.players.map((p: roomTypes.RoomMember) => ( + {room.players.map((p: domain.room.RoomMember) => (
  • void; + onJoin: (payload: domain.room.JoinRoomPayload) => void; // 入室失敗時の表示メッセージ joinErrorMessage: string | null; // 入室リクエスト送信中フラグ diff --git a/apps/server/src/domains/game/GameManager.ts b/apps/server/src/domains/game/GameManager.ts index fa7b906..ce01adc 100644 --- a/apps/server/src/domains/game/GameManager.ts +++ b/apps/server/src/domains/game/GameManager.ts @@ -3,7 +3,7 @@ * ゲームセッション集合の生成,更新,参照管理を統括する */ import type { - gameTypes, + domain, GameResultPayload, PlaceBombPayload, } from "@repo/shared"; @@ -61,7 +61,7 @@ startRoomSession( playerIds: string[], playerNamesById: Record, - onTick: (data: gameTypes.TickData) => void, + onTick: (data: domain.game.TickData) => void, onGameEnd: (payload: GameResultPayload) => void, onBotPlaceBomb?: (ownerId: string, payload: PlaceBombPayload) => void, ) { diff --git a/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts b/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts index a6a84fa..73819f0 100644 --- a/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts +++ b/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts @@ -7,10 +7,8 @@ BombPlacedAckPayload, BombPlacedPayload, PlayerDeadPayload, - gameTypes, - playerTypes, + domain, PlaceBombPayload, - roomTypes, CurrentPlayersPayload, GameResultPayload, GameStartPayload, @@ -25,7 +23,7 @@ startRoomSession( playerIds: string[], playerNamesById: Record, - onTick: (data: gameTypes.TickData) => void, + onTick: (data: domain.game.TickData) => void, onGameEnd: (payload: GameResultPayload) => void, onBotPlaceBomb?: (ownerId: string, payload: PlaceBombPayload) => void, ): void; @@ -34,7 +32,7 @@ /** 準備完了ユースケースが利用するゲーム状態参照入力ポート */ export interface ReadyForGamePort { - getRoomPlayers(): playerTypes.PlayerData[]; + getRoomPlayers(): domain.player.PlayerData[]; getRoomStartTime(): number | undefined; } @@ -56,22 +54,22 @@ players: UpdatePlayersPayload, ): void; publishMapCellUpdatesToRoom( - roomId: roomTypes.Room["roomId"], + roomId: domain.room.Room["roomId"], cellUpdates: UpdateMapCellsPayload, ): void; - publishGameEndToRoom(roomId: roomTypes.Room["roomId"]): void; + publishGameEndToRoom(roomId: domain.room.Room["roomId"]): void; publishGameResultToRoom( - roomId: roomTypes.Room["roomId"], + roomId: domain.room.Room["roomId"], payload: GameResultPayload, ): void; publishGameStartToRoom( - roomId: roomTypes.Room["roomId"], + roomId: domain.room.Room["roomId"], payload: GameStartPayload, ): void; publishCurrentPlayersToSocket(players: CurrentPlayersPayload): void; publishGameStartToSocket(payload: GameStartPayload): void; publishPlayerRemovedToRoom( - roomId: roomTypes.Room["roomId"], + roomId: domain.room.Room["roomId"], removedPlayerId: RemovePlayerPayload, ): void; } @@ -79,7 +77,7 @@ /** 爆弾ユースケースが利用する送信出力ポート */ export interface BombOutputPort { publishBombPlacedToOthersInRoom( - roomId: roomTypes.Room["roomId"], + roomId: domain.room.Room["roomId"], ownerSocketId: string, payload: BombPlacedPayload, ): void; @@ -88,7 +86,7 @@ payload: BombPlacedAckPayload, ): void; publishPlayerDeadToOthersInRoom( - roomId: roomTypes.Room["roomId"], + roomId: domain.room.Room["roomId"], deadPlayerId: string, payload: PlayerDeadPayload, ): void; diff --git a/apps/server/src/domains/game/application/services/GameRoomSession.ts b/apps/server/src/domains/game/application/services/GameRoomSession.ts index c932481..abbd935 100644 --- a/apps/server/src/domains/game/application/services/GameRoomSession.ts +++ b/apps/server/src/domains/game/application/services/GameRoomSession.ts @@ -8,7 +8,7 @@ logResults, logScopes, } from "@server/logging/index"; -import type { gameTypes, GameResultPayload } from "@repo/shared"; +import type { domain, GameResultPayload } from "@repo/shared"; import { config } from "@server/config"; import { GameLoop } from "../../loop/GameLoop"; import { Player } from "../../entities/player/Player.js"; @@ -57,7 +57,7 @@ public start( tickRate: number, - onTick: (data: gameTypes.TickData) => void, + onTick: (data: domain.game.TickData) => void, onGameEnd: (payload: GameResultPayload) => void, onBotPlaceBomb?: (ownerId: string, payload: PlaceBombPayload) => void, ): void { diff --git a/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts b/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts index a8e0da7..e5d05ec 100644 --- a/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts +++ b/apps/server/src/domains/game/application/services/GameSessionLifecycleService.ts @@ -4,7 +4,7 @@ */ import { config } from "@server/config"; import type { - gameTypes, + domain, GameResultPayload, PlaceBombPayload, } from "@repo/shared"; @@ -64,7 +64,7 @@ public startRoomSession( playerIds: string[], playerNamesById: Record, - onTick: (data: gameTypes.TickData) => void, + onTick: (data: domain.game.TickData) => void, onGameEnd: (payload: GameResultPayload) => void, onBotPlaceBomb?: (ownerId: string, payload: PlaceBombPayload) => void, ) { diff --git a/apps/server/src/domains/game/application/useCases/movePlayerUseCase.ts b/apps/server/src/domains/game/application/useCases/movePlayerUseCase.ts index 400b1ae..054fbfe 100644 --- a/apps/server/src/domains/game/application/useCases/movePlayerUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/movePlayerUseCase.ts @@ -2,13 +2,13 @@ * movePlayerUseCase * プレイヤー移動入力を受け取り,ゲーム管理へ反映する */ -import type { playerTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import type { MovePlayerPort } from "../ports/gameUseCasePorts"; type MovePlayerUseCaseParams = { gameManager: MovePlayerPort; playerId: string; - move: playerTypes.MovePayload; + move: domain.player.MovePayload; }; /** プレイヤー移動入力をゲーム管理へ委譲する */ diff --git a/apps/server/src/domains/game/entities/map/MapStore.ts b/apps/server/src/domains/game/entities/map/MapStore.ts index f116f44..3c9e35b 100644 --- a/apps/server/src/domains/game/entities/map/MapStore.ts +++ b/apps/server/src/domains/game/entities/map/MapStore.ts @@ -2,7 +2,7 @@ * MapStore * 塗り状態グリッドと差分更新キューを保持して提供する */ -import type { gridMapTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import { createInitialGridColors } from "./mapGrid.js"; import { paintCellIfChanged } from "./mapPainting.js"; import { drainPendingUpdates } from "./mapUpdates.js"; @@ -12,7 +12,7 @@ // 全マスの現在の色(teamId)を保持 private gridColors: number[]; // 次回の送信ループで送る差分リスト - private pendingUpdates: gridMapTypes.CellUpdate[]; + private pendingUpdates: domain.gridMap.CellUpdate[]; constructor() { // 初期状態は -1 (無色) などで初期化 @@ -35,7 +35,7 @@ /** * 溜まっている差分を取得し,キューをクリアする(ループ送信時に使用) */ - public getAndClearUpdates(): gridMapTypes.CellUpdate[] { + public getAndClearUpdates(): domain.gridMap.CellUpdate[] { return drainPendingUpdates(this.pendingUpdates); } diff --git a/apps/server/src/domains/game/entities/map/mapPainting.ts b/apps/server/src/domains/game/entities/map/mapPainting.ts index 8ffe9fb..efb054c 100644 --- a/apps/server/src/domains/game/entities/map/mapPainting.ts +++ b/apps/server/src/domains/game/entities/map/mapPainting.ts @@ -2,11 +2,11 @@ * mapPainting * マップセルの塗り更新と差分追加処理を提供する */ -import type { gridMapTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; type PaintCellParams = { gridColors: number[]; - pendingUpdates: gridMapTypes.CellUpdate[]; + pendingUpdates: domain.gridMap.CellUpdate[]; index: number; teamId: number; }; diff --git a/apps/server/src/domains/game/entities/map/mapUpdates.ts b/apps/server/src/domains/game/entities/map/mapUpdates.ts index 9c51618..2d56960 100644 --- a/apps/server/src/domains/game/entities/map/mapUpdates.ts +++ b/apps/server/src/domains/game/entities/map/mapUpdates.ts @@ -2,12 +2,12 @@ * mapUpdates * マップ差分キューの取り出しとクリア処理を提供する */ -import type { gridMapTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; /** 差分キューを配列として返却し,キューを空にする */ export const drainPendingUpdates = ( - pendingUpdates: gridMapTypes.CellUpdate[] -): gridMapTypes.CellUpdate[] => { + pendingUpdates: domain.gridMap.CellUpdate[] +): domain.gridMap.CellUpdate[] => { const updates = [...pendingUpdates]; pendingUpdates.length = 0; return updates; diff --git a/apps/server/src/domains/game/entities/player/Player.ts b/apps/server/src/domains/game/entities/player/Player.ts index fc4daa5..48943b1 100644 --- a/apps/server/src/domains/game/entities/player/Player.ts +++ b/apps/server/src/domains/game/entities/player/Player.ts @@ -2,10 +2,9 @@ * Player * サーバー側で保持するプレイヤー状態モデルを定義する */ -import type { playerTypes } from "@repo/shared"; -// configのimportは不要になります +import { domain } from "@repo/shared"; -export class Player implements playerTypes.PlayerData { +export class Player implements domain.player.PlayerData { public id: string; public name: string; public x: number = 0; diff --git a/apps/server/src/domains/game/entities/player/playerPosition.ts b/apps/server/src/domains/game/entities/player/playerPosition.ts index 0a2112c..700b0da 100644 --- a/apps/server/src/domains/game/entities/player/playerPosition.ts +++ b/apps/server/src/domains/game/entities/player/playerPosition.ts @@ -2,10 +2,10 @@ * playerPosition * プレイヤー座標からマップ上のセル位置を解決する */ -import { gridMapLogic } from "@repo/shared"; +import { domain } from "@repo/shared"; import { Player } from "./Player.js"; /** プレイヤー座標に対応するグリッドインデックスを返す */ export const getPlayerGridIndex = (player: Player): number | null => { - return gridMapLogic.getGridIndexFromPosition(player.x, player.y); + return domain.gridMap.getGridIndexFromPosition(player.x, player.y); }; diff --git a/apps/server/src/domains/game/loop/GameLoop.ts b/apps/server/src/domains/game/loop/GameLoop.ts index 339c05d..7497a75 100644 --- a/apps/server/src/domains/game/loop/GameLoop.ts +++ b/apps/server/src/domains/game/loop/GameLoop.ts @@ -6,7 +6,7 @@ import { MapStore } from "../entities/map/MapStore"; import { getPlayerGridIndex } from "../entities/player/playerPosition.js"; import { config } from "@server/config"; -import type { gameTypes, PlaceBombPayload } from "@repo/shared"; +import type { domain, PlaceBombPayload } from "@repo/shared"; import { logEvent } from "@server/logging/logger"; import { gameDomainLogEvents, @@ -25,7 +25,7 @@ private endMonotonicTimeMs: number = 0; private nextTickAtMs: number = 0; private readonly maxCatchUpTicks: number = 3; - private lastSentPlayers: Map = + private lastSentPlayers: Map = new Map(); private botAiService: BotAiService = new BotAiService(); @@ -34,7 +34,7 @@ private tickRate: number, private players: Map, private mapStore: MapStore, - private onTick: (data: gameTypes.TickData) => void, + private onTick: (data: domain.game.TickData) => void, private onGameEnd: () => void, private onBotPlaceBomb?: ( ownerId: string, @@ -143,7 +143,7 @@ }); } - private buildTickData(): gameTypes.TickData { + private buildTickData(): domain.game.TickData { const activePlayerIds = new Set(); const playerUpdates = this.collectChangedPlayerUpdates(activePlayerIds); this.cleanupInactivePlayerSnapshots(activePlayerIds); @@ -156,8 +156,8 @@ private collectChangedPlayerUpdates( activePlayerIds: Set, - ): gameTypes.TickData["playerUpdates"] { - const changedPlayers: gameTypes.TickData["playerUpdates"] = []; + ): domain.game.TickData["playerUpdates"] { + const changedPlayers: domain.game.TickData["playerUpdates"] = []; this.players.forEach((player) => { activePlayerIds.add(player.id); @@ -167,7 +167,7 @@ } // 送信用のプレイヤーデータを構築 - const playerData: gameTypes.PlayerPositionUpdate = { + const playerData: domain.game.PlayerPositionUpdate = { id: player.id, x: player.x, y: player.y, diff --git a/apps/server/src/domains/room/RoomManager.ts b/apps/server/src/domains/room/RoomManager.ts index fc446c6..6a7be2e 100644 --- a/apps/server/src/domains/room/RoomManager.ts +++ b/apps/server/src/domains/room/RoomManager.ts @@ -2,7 +2,7 @@ * RoomManager * ルーム状態の保持とルーム操作サービスへの委譲を担うマネージャ */ -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import { RoomJoinService } from "./application/services/RoomJoinService"; import { RoomExitService } from "./application/services/RoomExitService"; import { RoomPhaseService } from "./application/services/RoomPhaseService"; @@ -15,7 +15,7 @@ /** ルーム操作の公開インターフェースを提供するマネージャ */ export class RoomManager { - private rooms: Map = new Map(); + private rooms: Map = new Map(); private roomJoinService: RoomJoinService; private roomExitService: RoomExitService; private roomPhaseService: RoomPhaseService; @@ -39,17 +39,17 @@ } // オーナーIDからルームを取得する - public getRoomByOwnerId(ownerId: string): roomTypes.Room | undefined { + public getRoomByOwnerId(ownerId: string): domain.room.Room | undefined { return this.roomQueryService.getRoomByOwnerId(ownerId); } // ルームIDからルームを取得する - public getRoomById(roomId: string): roomTypes.Room | undefined { + public getRoomById(roomId: string): domain.room.Room | undefined { return this.roomQueryService.getRoomById(roomId); } // プレイヤーIDから所属ルームを取得する - public getRoomByPlayerId(playerId: string): roomTypes.Room | undefined { + public getRoomByPlayerId(playerId: string): domain.room.Room | undefined { return this.roomQueryService.getRoomByPlayerId(playerId); } diff --git a/apps/server/src/domains/room/application/ports/roomUseCasePorts.ts b/apps/server/src/domains/room/application/ports/roomUseCasePorts.ts index d72a13c..3cbf6d3 100644 --- a/apps/server/src/domains/room/application/ports/roomUseCasePorts.ts +++ b/apps/server/src/domains/room/application/ports/roomUseCasePorts.ts @@ -2,7 +2,7 @@ * roomUseCasePorts * ルームユースケースが依存する操作ポートを定義する */ -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import type { BombHitReportValidationPort, BombPlacementPort, @@ -23,14 +23,14 @@ /** ルーム参加処理の実行結果 */ export type JoinRoomResult = { - room: roomTypes.Room; + room: domain.room.Room; status: "joined" | "duplicate" | "full"; }; /** ルームユースケースが利用する出力ポート */ export interface RoomOutputPort { - publishRoomUpdateToRoom(roomId: roomTypes.Room["roomId"], room: roomTypes.Room): void; - publishJoinRejectedToSocket(payload: roomTypes.JoinRoomRejectedPayload): void; + publishRoomUpdateToRoom(roomId: domain.room.Room["roomId"], room: domain.room.Room): void; + publishJoinRejectedToSocket(payload: domain.room.JoinRoomRejectedPayload): void; } /** ルーム参加ユースケースが利用する参加操作ポート */ @@ -45,18 +45,18 @@ /** 退出処理で更新対象となったルーム情報 */ export type RoomDisconnectResult = { - updatedRooms: roomTypes.Room[]; + updatedRooms: domain.room.Room[]; deletedRoomIds: string[]; }; /** 切断調停で利用するプレイヤー所属ルーム参照ポート */ export interface FindRoomByPlayerPort { - getRoomByPlayerId(playerId: string): roomTypes.Room | undefined; + getRoomByPlayerId(playerId: string): domain.room.Room | undefined; } /** ゲーム開始調停で利用するオーナー所属ルーム参照ポート */ export interface FindRoomByOwnerPort { - getRoomByOwnerId(ownerId: string): roomTypes.Room | undefined; + getRoomByOwnerId(ownerId: string): domain.room.Room | undefined; } /** ゲーム開始調停で利用するルーム状態遷移ポート */ @@ -68,12 +68,12 @@ /** ルーム状態遷移の実行結果 */ export type RoomPhaseTransitionResult = { status: "updated" | "not_found" | "invalid_transition"; - room?: roomTypes.Room; + room?: domain.room.Room; }; /** ルームIDでの存在確認に利用する参照ポート */ export interface FindRoomByIdPort { - getRoomById(roomId: string): roomTypes.Room | undefined; + getRoomById(roomId: string): domain.room.Room | undefined; } /** ルーム参加後にゲームランタイムを確保する操作ポート */ diff --git a/apps/server/src/domains/room/application/services/RoomExitService.ts b/apps/server/src/domains/room/application/services/RoomExitService.ts index 1c54f38..90afa17 100644 --- a/apps/server/src/domains/room/application/services/RoomExitService.ts +++ b/apps/server/src/domains/room/application/services/RoomExitService.ts @@ -2,17 +2,17 @@ * RoomExitService * ルーム退出処理とオーナー移譲処理を担うサービス */ -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import type { RoomDisconnectResult } from "../ports/roomUseCasePorts"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes, roomDomainLogEvents } from "@server/logging/index"; /** 退出要求に応じてプレイヤー削除とルーム整理を行うサービス */ export class RoomExitService { - constructor(private rooms: Map) {} + constructor(private rooms: Map) {} public removePlayer(socketId: string): RoomDisconnectResult { - const updatedRooms: roomTypes.Room[] = []; + const updatedRooms: domain.room.Room[] = []; const deletedRoomIds: string[] = []; for (const [roomId, room] of this.rooms.entries()) { diff --git a/apps/server/src/domains/room/application/services/RoomJoinService.ts b/apps/server/src/domains/room/application/services/RoomJoinService.ts index dfaf7b8..8b72619 100644 --- a/apps/server/src/domains/room/application/services/RoomJoinService.ts +++ b/apps/server/src/domains/room/application/services/RoomJoinService.ts @@ -2,16 +2,15 @@ * RoomJoinService * ルーム作成とプレイヤー参加処理を担うサービス */ -import { roomConsts } from "@repo/shared"; +import { domain } from "@repo/shared"; import { config } from "@server/config"; -import type { roomTypes } from "@repo/shared"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes, roomDomainLogEvents } from "@server/logging/index"; import type { JoinRoomResult } from "../ports/roomUseCasePorts"; /** 参加要求に応じてルーム作成と参加者追加を行うサービス */ export class RoomJoinService { - constructor(private rooms: Map) {} + constructor(private rooms: Map) {} public addPlayerToRoom(roomId: string, socketId: string, playerName: string): JoinRoomResult { let room = this.rooms.get(roomId); @@ -20,7 +19,7 @@ roomId, ownerId: socketId, players: [], - status: roomConsts.RoomPhase.WAITING, + status: domain.room.RoomPhase.WAITING, maxPlayers: config.GAME_CONFIG.MAX_PLAYERS_PER_ROOM, }; this.rooms.set(roomId, room); @@ -59,7 +58,7 @@ return { room, status: "full" }; } - const newPlayer: roomTypes.RoomMember = { + const newPlayer: domain.room.RoomMember = { id: socketId, name: playerName, isOwner: room.ownerId === socketId, diff --git a/apps/server/src/domains/room/application/services/RoomPhaseService.ts b/apps/server/src/domains/room/application/services/RoomPhaseService.ts index b79a237..3ee0466 100644 --- a/apps/server/src/domains/room/application/services/RoomPhaseService.ts +++ b/apps/server/src/domains/room/application/services/RoomPhaseService.ts @@ -2,13 +2,12 @@ * RoomPhaseService * ルーム状態のフェーズ更新処理を提供する */ -import { roomConsts } from "@repo/shared"; -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import type { RoomPhaseTransitionResult } from "../ports/roomUseCasePorts"; /** ルーム状態のフェーズ遷移を管理するサービス */ export class RoomPhaseService { - constructor(private rooms: Map) {} + constructor(private rooms: Map) {} public markRoomPlaying(roomId: string): RoomPhaseTransitionResult { const room = this.rooms.get(roomId); @@ -16,14 +15,14 @@ return { status: "not_found" }; } - if (room.status === roomConsts.RoomPhase.PLAYING) { + if (room.status === domain.room.RoomPhase.PLAYING) { return { status: "invalid_transition", room, }; } - room.status = roomConsts.RoomPhase.PLAYING; + room.status = domain.room.RoomPhase.PLAYING; return { status: "updated", room, @@ -36,14 +35,14 @@ return { status: "not_found" }; } - if (room.status === roomConsts.RoomPhase.WAITING) { + if (room.status === domain.room.RoomPhase.WAITING) { return { status: "invalid_transition", room, }; } - room.status = roomConsts.RoomPhase.WAITING; + room.status = domain.room.RoomPhase.WAITING; return { status: "updated", room, diff --git a/apps/server/src/domains/room/application/services/RoomQueryService.ts b/apps/server/src/domains/room/application/services/RoomQueryService.ts index 6881df8..e9b02da 100644 --- a/apps/server/src/domains/room/application/services/RoomQueryService.ts +++ b/apps/server/src/domains/room/application/services/RoomQueryService.ts @@ -2,17 +2,17 @@ * RoomQueryService * ルーム状態の参照系クエリを提供するサービス */ -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; /** ルームの参照クエリを提供するサービス */ export class RoomQueryService { - constructor(private rooms: Map) {} + constructor(private rooms: Map) {} - public getRoomById(roomId: string): roomTypes.Room | undefined { + public getRoomById(roomId: string): domain.room.Room | undefined { return this.rooms.get(roomId); } - public getRoomByPlayerId(playerId: string): roomTypes.Room | undefined { + public getRoomByPlayerId(playerId: string): domain.room.Room | undefined { for (const room of this.rooms.values()) { if (room.players.some((player) => player.id === playerId)) { return room; @@ -22,7 +22,7 @@ return undefined; } - public getRoomByOwnerId(ownerId: string): roomTypes.Room | undefined { + public getRoomByOwnerId(ownerId: string): domain.room.Room | undefined { for (const room of this.rooms.values()) { if (room.ownerId === ownerId) { return room; diff --git a/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts b/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts index f83aa9b..2b8b02f 100644 --- a/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts +++ b/apps/server/src/domains/room/application/useCases/joinRoomUseCase.ts @@ -2,7 +2,7 @@ * joinRoomUseCase * ルーム参加要求を処理し,状態更新を配信するユースケース */ -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import type { EnsureGameRuntimePort, JoinRoomPort, @@ -16,7 +16,7 @@ roomManager: JoinRoomPort; runtimeRegistry: EnsureGameRuntimePort; socketId: string; - data: roomTypes.JoinRoomPayload; + data: domain.room.JoinRoomPayload; output: Pick; }; diff --git a/apps/server/src/logging/constants/eventNames.ts b/apps/server/src/logging/constants/eventNames.ts index fdeeb92..0aed1a4 100644 --- a/apps/server/src/logging/constants/eventNames.ts +++ b/apps/server/src/logging/constants/eventNames.ts @@ -2,7 +2,7 @@ * eventNames * ログ出力で利用するイベント名定数群を提供する */ -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; /** GameUseCaseログで利用するイベント名定数 */ export const gameUseCaseLogEvents = { diff --git a/apps/server/src/logging/contracts/payloadByScope.ts b/apps/server/src/logging/contracts/payloadByScope.ts index 0480671..8620418 100644 --- a/apps/server/src/logging/contracts/payloadByScope.ts +++ b/apps/server/src/logging/contracts/payloadByScope.ts @@ -2,7 +2,7 @@ * payloadByScope * スコープごとのログペイロード型契約を提供する */ -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import { gameDomainLogEvents, gameUseCaseLogEvents, roomDomainLogEvents, roomUseCaseLogEvents } from "../constants/eventNames"; import { logResults } from "../constants/results"; import { logScopes } from "../constants/scopes"; diff --git a/apps/server/src/network/handlers/connectionEventLogger.ts b/apps/server/src/network/handlers/connectionEventLogger.ts index f5ccc84..0fec54f 100644 --- a/apps/server/src/network/handlers/connectionEventLogger.ts +++ b/apps/server/src/network/handlers/connectionEventLogger.ts @@ -2,7 +2,7 @@ * connectionEventLogger * 接続ハンドラで利用するログ記録処理を共通化する */ -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes } from "@server/logging/index"; diff --git a/apps/server/src/network/handlers/game/createGameOutputAdapter.ts b/apps/server/src/network/handlers/game/createGameOutputAdapter.ts index bad3082..6e3945c 100644 --- a/apps/server/src/network/handlers/game/createGameOutputAdapter.ts +++ b/apps/server/src/network/handlers/game/createGameOutputAdapter.ts @@ -3,15 +3,15 @@ * ゲーム系ユースケースから利用する送信関数群を生成する */ import { Server } from "socket.io"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { BombPlacedAckPayload, BombPlacedPayload, + domain, GameStartPayload, GameResultPayload, PlayerDeadPayload, PongPayload, - roomTypes, CurrentPlayersPayload, RemovePlayerPayload, UpdateMapCellsPayload, @@ -25,7 +25,7 @@ import { createEmitToRoom } from "@server/network/adapters/socketEmitters"; import type { CommonHandlerContext } from "../CommonHandler"; -type RoomId = roomTypes.Room["roomId"]; +type RoomId = domain.room.Room["roomId"]; /** ゲーム出力アダプターのインターフェース */ export type GameOutputAdapter = Omit & BombOutputPort; diff --git a/apps/server/src/network/handlers/game/gameEventOrchestrators.ts b/apps/server/src/network/handlers/game/gameEventOrchestrators.ts index a522685..874cdf2 100644 --- a/apps/server/src/network/handlers/game/gameEventOrchestrators.ts +++ b/apps/server/src/network/handlers/game/gameEventOrchestrators.ts @@ -4,7 +4,7 @@ * 受信ハンドラからユースケース実行責務を分離する * ランタイム未解決時はNetworkスコープでignored_missing_roomを記録する */ -import { protocol, type BombHitReportPayload, type PingPayload, type PlaceBombPayload, type playerTypes } from "@repo/shared"; +import { contracts as protocol, domain, type BombHitReportPayload, type PingPayload, type PlaceBombPayload } from "@repo/shared"; import { readyForGameCoordinator } from "@server/application/coordinators/readyForGameCoordinator"; import { startGameCoordinator } from "@server/application/coordinators/startGameCoordinator"; import { movePlayerUseCase } from "@server/domains/game/application/useCases/movePlayerUseCase"; @@ -84,7 +84,7 @@ /** MOVEイベントを調停して移動ユースケースを実行する */ export const handleMoveEvent = ( deps: GameEventOrchestratorDeps, - move: playerTypes.MovePayload, + move: domain.player.MovePayload, ): void => { const resolved = runWithRuntimeByPlayerId( deps.roomManager, diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index 22b807c..46a7dee 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -3,7 +3,7 @@ * ゲーム関連イベントの受信ハンドラを登録する */ import { Socket } from "socket.io"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { GameEventRoomUseCasePort, GameEventRuntimeUseCasePort, diff --git a/apps/server/src/network/handlers/orchestratorEventLogger.ts b/apps/server/src/network/handlers/orchestratorEventLogger.ts index 7cfb0a5..ca7487b 100644 --- a/apps/server/src/network/handlers/orchestratorEventLogger.ts +++ b/apps/server/src/network/handlers/orchestratorEventLogger.ts @@ -2,7 +2,7 @@ * orchestratorEventLogger * オーケストレータ層で利用するイベントログ記録を共通化する */ -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes } from "@server/logging/index"; diff --git a/apps/server/src/network/handlers/payloadGuard.ts b/apps/server/src/network/handlers/payloadGuard.ts index dd1ab88..3bd6936 100644 --- a/apps/server/src/network/handlers/payloadGuard.ts +++ b/apps/server/src/network/handlers/payloadGuard.ts @@ -2,7 +2,7 @@ * payloadGuard * 受信ペイロード検証と不正時ログ記録を共通化するガード生成を担う */ -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes } from "@server/logging/index"; diff --git a/apps/server/src/network/handlers/registerConnectionHandlers.ts b/apps/server/src/network/handlers/registerConnectionHandlers.ts index 0703ca3..fc5d64b 100644 --- a/apps/server/src/network/handlers/registerConnectionHandlers.ts +++ b/apps/server/src/network/handlers/registerConnectionHandlers.ts @@ -3,7 +3,7 @@ * 接続時にルームとゲームの各ハンドラを登録する */ import { Socket } from "socket.io"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import { disconnectCoordinator } from "@server/application/coordinators/disconnectCoordinator"; import { registerGameHandlers } from "./game/registerGameHandlers"; import { registerRoomHandlers } from "./room/registerRoomHandlers"; diff --git a/apps/server/src/network/handlers/room/createRoomOutputAdapter.ts b/apps/server/src/network/handlers/room/createRoomOutputAdapter.ts index 90aacab..090a0b9 100644 --- a/apps/server/src/network/handlers/room/createRoomOutputAdapter.ts +++ b/apps/server/src/network/handlers/room/createRoomOutputAdapter.ts @@ -3,14 +3,14 @@ * ルーム系ユースケースから利用する送信関数を生成する */ import { Server } from "socket.io"; -import { protocol } from "@repo/shared"; -import type { roomTypes } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; +import { domain } from "@repo/shared"; import type { RoomOutputPort } from "@server/domains/room/application/ports/roomUseCasePorts"; import { createEmitToRoom } from "@server/network/adapters/socketEmitters"; import type { CommonHandlerContext } from "../CommonHandler"; -type RoomId = roomTypes.Room["roomId"]; -type RoomUpdatePayload = roomTypes.Room; +type RoomId = domain.room.Room["roomId"]; +type RoomUpdatePayload = domain.room.Room; /** ルーム出力アダプターのインターフェース */ export type RoomOutputAdapter = RoomOutputPort; @@ -23,7 +23,7 @@ publishRoomUpdateToRoom: (roomId: RoomId, room: RoomUpdatePayload) => { common.emitToRoom(roomId, protocol.SocketEvents.ROOM_UPDATE, room); }, - publishJoinRejectedToSocket: (payload: roomTypes.JoinRoomRejectedPayload) => { + publishJoinRejectedToSocket: (payload: domain.room.JoinRoomRejectedPayload) => { common.emitToSocket(protocol.SocketEvents.ROOM_JOIN_REJECTED, payload); }, }; diff --git a/apps/server/src/network/handlers/room/registerRoomHandlers.ts b/apps/server/src/network/handlers/room/registerRoomHandlers.ts index 7fdd565..bb3f083 100644 --- a/apps/server/src/network/handlers/room/registerRoomHandlers.ts +++ b/apps/server/src/network/handlers/room/registerRoomHandlers.ts @@ -3,7 +3,7 @@ * ルーム参加イベントの受信ハンドラを登録する */ import { Socket } from "socket.io"; -import { protocol } from "@repo/shared"; +import { contracts as protocol } from "@repo/shared"; import type { JoinRoomEventRoomUseCasePort, JoinRoomEventRuntimeUseCasePort, diff --git a/apps/server/src/network/handlers/room/roomEventOrchestrators.ts b/apps/server/src/network/handlers/room/roomEventOrchestrators.ts index 3de3c7a..b3b4996 100644 --- a/apps/server/src/network/handlers/room/roomEventOrchestrators.ts +++ b/apps/server/src/network/handlers/room/roomEventOrchestrators.ts @@ -4,7 +4,7 @@ * 受信ハンドラからユースケース実行責務を分離する * 本ファイルではランタイム未解決ログ対象イベントを扱わない */ -import type { roomTypes } from "@repo/shared"; +import { domain } from "@repo/shared"; import { joinRoomUseCase } from "@server/domains/room/application/useCases/joinRoomUseCase"; import { logEvent } from "@server/logging/logger"; import { logResults, logScopes, roomUseCaseLogEvents } from "@server/logging/index"; @@ -29,7 +29,7 @@ /** JOIN_ROOMイベントを調停して参加ユースケースを実行する */ export const handleJoinRoomEvent = async ( deps: JoinRoomOrchestratorDeps, - payload: roomTypes.JoinRoomPayload, + payload: domain.room.JoinRoomPayload, ): Promise => { const joinResult = joinRoomUseCase({ roomManager: deps.roomManager, diff --git a/apps/server/src/network/handlers/socketEventBridge.ts b/apps/server/src/network/handlers/socketEventBridge.ts index 977ef98..3ead355 100644 --- a/apps/server/src/network/handlers/socketEventBridge.ts +++ b/apps/server/src/network/handlers/socketEventBridge.ts @@ -14,16 +14,16 @@ /** サーバー向けの型付きソケットイベント bridge を生成する */ export const createServerSocketOnBridge = (socket: Socket) => { const bridgeTarget: SocketBridgeTarget = { - on: (event, callback) => { - socket.on(event, callback); + on: (event: string, callback: (payload: TPayload) => void) => { + socket.on(event, callback as (payload: unknown) => void); }, - once: (event, callback) => { - socket.once(event, callback); + once: (event: string, callback: (payload: TPayload) => void) => { + socket.once(event, callback as (payload: unknown) => void); }, - off: (event, callback) => { - socket.off(event, callback); + off: (event: string, callback: (payload: TPayload) => void) => { + socket.off(event, callback as (payload: unknown) => void); }, - emit: (event, payload) => { + emit: (event: string, payload?: unknown) => { if (payload === undefined) { socket.emit(event); return; diff --git a/apps/server/src/network/validation/socketPayloadValidators.ts b/apps/server/src/network/validation/socketPayloadValidators.ts index 7f05d71..39941ea 100644 --- a/apps/server/src/network/validation/socketPayloadValidators.ts +++ b/apps/server/src/network/validation/socketPayloadValidators.ts @@ -3,8 +3,7 @@ * ソケット受信ペイロードの型ガードを提供する */ import type { - playerTypes, - roomTypes, + domain, PlaceBombPayload, BombHitReportPayload, } from "@repo/shared"; @@ -27,7 +26,7 @@ /** MOVEイベントのペイロードが移動座標であるか判定する */ export const isMovePayload = ( value: unknown, -): value is playerTypes.MovePayload => { +): value is domain.player.MovePayload => { if (typeof value !== "object" || value === null) { return false; } @@ -79,7 +78,7 @@ /** JOIN_ROOMイベントのペイロードが参加情報であるか判定する */ export const isJoinRoomPayload = ( value: unknown, -): value is roomTypes.JoinRoomPayload => { +): value is domain.room.JoinRoomPayload => { if (typeof value !== "object" || value === null) { return false; } diff --git "a/docs/02_Guide/GUIDE_05_\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253\350\277\275\345\212\240\346\211\213\351\240\206.txt" "b/docs/02_Guide/GUIDE_05_\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253\350\277\275\345\212\240\346\211\213\351\240\206.txt" index 56c5839..3104ac6 100644 --- "a/docs/02_Guide/GUIDE_05_\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253\350\277\275\345\212\240\346\211\213\351\240\206.txt" +++ "b/docs/02_Guide/GUIDE_05_\343\203\227\343\203\255\343\203\210\343\202\263\343\203\253\350\277\275\345\212\240\346\211\213\351\240\206.txt" @@ -73,8 +73,8 @@ ・events.ts 再公開が過不足ないか ・client/server で型エラーがないか ・既存イベントの型互換を壊していないか -・player 座標差分イベント(UPDATE_PLAYERS)に teamId を含めていないか -・初期同期イベント(CURRENT_PLAYERS / NEW_PLAYER)に teamId を含めているか +・player 座標差分イベント(SocketEvents.UPDATE_PLAYERS / update-players)に teamId を含めていないか +・初期同期イベント(SocketEvents.CURRENT_PLAYERS / current-players,SocketEvents.NEW_PLAYER / new-player)に teamId を含めているか 6. 典型ミスと対策 (Common Pitfalls) diff --git a/packages/shared/eslint.config.mjs b/packages/shared/eslint.config.mjs new file mode 100644 index 0000000..e314f77 --- /dev/null +++ b/packages/shared/eslint.config.mjs @@ -0,0 +1,16 @@ +import tsParser from '@typescript-eslint/parser'; + +export default [ + { + ignores: ['dist/**', 'node_modules/**'], + }, + { + files: ['src/**/*.ts'], + languageOptions: { + parser: tsParser, + ecmaVersion: 'latest', + sourceType: 'module', + }, + rules: {}, + }, +]; diff --git a/packages/shared/package.json b/packages/shared/package.json index 02afce3..55e5450 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -18,6 +18,8 @@ }, "scripts": { "build": "tsup src/index.ts src/config/gameConfig.ts --format cjs,esm --dts", + "lint": "eslint src --ext .ts", + "lint:fix": "eslint src --ext .ts --fix", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -25,6 +27,8 @@ "license": "ISC", "packageManager": "pnpm@10.28.2", "devDependencies": { + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^10.0.2", "tsup": "^8.5.1", "typescript": "^5.9.3" } diff --git a/packages/shared/src/config/gameConfig.ts b/packages/shared/src/config/gameConfig.ts index c3dcdbd..7c11d92 100644 --- a/packages/shared/src/config/gameConfig.ts +++ b/packages/shared/src/config/gameConfig.ts @@ -43,36 +43,3 @@ /** プレイヤー情報から teamId を解決できない場合に利用する既定値 */ export const UNKNOWN_TEAM_ID = -1; - -/** teamId が unknown を表す値か判定する */ -export const isUnknownTeamId = (teamId: number): boolean => { - return teamId === UNKNOWN_TEAM_ID; -}; - -/** teamId が有効範囲内かを真偽値で判定する */ -export const isKnownTeamId = (teamId: number): boolean => { - return ( - Number.isInteger(teamId) && teamId >= 0 && teamId < GAME_CONFIG.TEAM_COUNT - ); -}; - -/** TEAM_COUNT と TEAM_NAMES の整合性を検証する */ -export const validateTeamConfig = (): void => { - const { TEAM_COUNT } = GAME_CONFIG; - - if (TEAM_NAMES.length !== TEAM_COUNT) { - throw new Error( - `GAME_CONFIG mismatch: TEAM_NAMES length (${TEAM_NAMES.length}) must equal TEAM_COUNT (${TEAM_COUNT})`, - ); - } -}; - -/** teamId が有効範囲内かを検証する */ -export const assertValidTeamId = (teamId: number): void => { - validateTeamConfig(); - - const { TEAM_COUNT } = GAME_CONFIG; - if (!Number.isInteger(teamId) || teamId < 0 || teamId >= TEAM_COUNT) { - throw new Error(`Invalid teamId: ${teamId}`); - } -}; diff --git a/packages/shared/src/config/index.ts b/packages/shared/src/config/index.ts index 27ba7e8..3d3230d 100644 --- a/packages/shared/src/config/index.ts +++ b/packages/shared/src/config/index.ts @@ -1,5 +1,21 @@ +/** + * index + * 共有設定値を集約して再公開するエントリ + * ゲーム設定とネットワーク設定の参照口を一本化する + */ + +/** ゲーム全体の共有設定値を再公開する */ export { GAME_CONFIG } from "./gameConfig"; +/** チーム名配列を再公開する */ export { TEAM_NAMES } from "./gameConfig"; +/** 未確定 teamId の既定値を再公開する */ export { UNKNOWN_TEAM_ID } from "./gameConfig"; -export { validateTeamConfig, assertValidTeamId, isUnknownTeamId, isKnownTeamId } from "./gameConfig"; +/** チーム設定関連の検証関数を再公開する */ +export { + validateTeamConfig, + assertValidTeamId, + isUnknownTeamId, + isKnownTeamId, +} from "./teamValidators"; +/** ネットワーク共有設定値を再公開する */ export { NETWORK_CONFIG } from "./networkConfig"; diff --git a/packages/shared/src/config/networkConfig.ts b/packages/shared/src/config/networkConfig.ts index eff99b6..f5ad576 100644 --- a/packages/shared/src/config/networkConfig.ts +++ b/packages/shared/src/config/networkConfig.ts @@ -1,3 +1,10 @@ +/** + * networkConfig + * ネットワーク通信で利用する共有設定値を定義する + * クライアントとサーバーの接続契約を集約する + */ + +/** ソケット通信で利用する共有設定値 */ export const NETWORK_CONFIG = { SOCKET_IO_PATH: "/socket.io", } as const; diff --git a/packages/shared/src/config/teamValidators.ts b/packages/shared/src/config/teamValidators.ts new file mode 100644 index 0000000..badcc25 --- /dev/null +++ b/packages/shared/src/config/teamValidators.ts @@ -0,0 +1,39 @@ +/** + * teamValidators + * チーム設定に対する検証関数を定義する + * 設定値と参照IDの整合性検査を集約する + */ +import { GAME_CONFIG, TEAM_NAMES, UNKNOWN_TEAM_ID } from "./gameConfig"; + +/** teamId が unknown を表す値か判定する */ +export const isUnknownTeamId = (teamId: number): boolean => { + return teamId === UNKNOWN_TEAM_ID; +}; + +/** teamId が有効範囲内かを真偽値で判定する */ +export const isKnownTeamId = (teamId: number): boolean => { + return ( + Number.isInteger(teamId) && teamId >= 0 && teamId < GAME_CONFIG.TEAM_COUNT + ); +}; + +/** TEAM_COUNT と TEAM_NAMES の整合性を検証する */ +export const validateTeamConfig = (): void => { + const { TEAM_COUNT } = GAME_CONFIG; + + if (TEAM_NAMES.length !== TEAM_COUNT) { + throw new Error( + `GAME_CONFIG mismatch: TEAM_NAMES length (${TEAM_NAMES.length}) must equal TEAM_COUNT (${TEAM_COUNT})`, + ); + } +}; + +/** teamId が有効範囲内かを検証する */ +export const assertValidTeamId = (teamId: number): void => { + validateTeamConfig(); + + const { TEAM_COUNT } = GAME_CONFIG; + if (!Number.isInteger(teamId) || teamId < 0 || teamId >= TEAM_COUNT) { + throw new Error(`Invalid teamId: ${teamId}`); + } +}; diff --git a/packages/shared/src/domains/app/app.const.ts b/packages/shared/src/domains/app/app.const.ts index f8beb8d..f72fd2a 100644 --- a/packages/shared/src/domains/app/app.const.ts +++ b/packages/shared/src/domains/app/app.const.ts @@ -1,6 +1,11 @@ +/** + * app.const + * アプリ状態領域で利用する定数を定義する + * 画面遷移フェーズ値を外部利用向けに提供する + */ import type { ScenePhase as ScenePhaseType } from "./app.type"; -// クライアント画面遷移利用フェーズの値 +/** クライアント画面遷移で利用するフェーズ定数 */ export const ScenePhase = { TITLE: "title", LOBBY: "lobby", diff --git a/packages/shared/src/domains/app/app.type.ts b/packages/shared/src/domains/app/app.type.ts index 83777d0..b334be3 100644 --- a/packages/shared/src/domains/app/app.type.ts +++ b/packages/shared/src/domains/app/app.type.ts @@ -1,2 +1,8 @@ -// クライアント画面遷移利用フェーズ型 +/** + * app.type + * アプリ状態領域で利用する共有型を定義する + * 画面遷移フェーズ契約を外部参照向けに提供する + */ + +/** クライアント画面遷移で利用するフェーズ型 */ export type ScenePhase = "title" | "lobby" | "playing" | "result"; diff --git a/packages/shared/src/domains/app/index.ts b/packages/shared/src/domains/app/index.ts new file mode 100644 index 0000000..026f0bb --- /dev/null +++ b/packages/shared/src/domains/app/index.ts @@ -0,0 +1,10 @@ +/** + * index + * app ドメインの公開要素を集約して再公開する + * 画面遷移フェーズの型と定数を外部利用向けに束ねる + */ + +/** 画面遷移フェーズ定数を再公開する */ +export { ScenePhase } from "./app.const"; +/** 画面遷移フェーズ型を再公開する */ +export type { ScenePhase as ScenePhaseType } from "./app.type"; diff --git a/packages/shared/src/domains/game/index.ts b/packages/shared/src/domains/game/index.ts new file mode 100644 index 0000000..3b3cd6b --- /dev/null +++ b/packages/shared/src/domains/game/index.ts @@ -0,0 +1,8 @@ +/** + * index + * game ドメインの公開要素を集約して再公開する + * ゲーム進行で利用する型を外部利用向けに束ねる + */ + +/** ゲーム進行関連の型を再公開する */ +export type { PlayerPositionUpdate, TickData } from "./game.type"; diff --git a/packages/shared/src/domains/gridMap/gridMap.type.ts b/packages/shared/src/domains/gridMap/gridMap.type.ts index d85090e..3e7811a 100644 --- a/packages/shared/src/domains/gridMap/gridMap.type.ts +++ b/packages/shared/src/domains/gridMap/gridMap.type.ts @@ -1,7 +1,15 @@ +/** + * gridMap.type + * グリッドマップ領域で利用する共有型を定義する + * マップ状態と差分更新の契約を集約する + */ + +/** マップ全体の色状態を保持する構造 */ export interface MapState { gridColors: number[]; } +/** マップ1セル分の差分更新情報 */ export interface CellUpdate { index: number; teamId: number; diff --git a/packages/shared/src/domains/gridMap/index.ts b/packages/shared/src/domains/gridMap/index.ts new file mode 100644 index 0000000..5daa48f --- /dev/null +++ b/packages/shared/src/domains/gridMap/index.ts @@ -0,0 +1,10 @@ +/** + * index + * gridMap ドメインの公開要素を集約して再公開する + * 型定義と座標変換ロジックを外部利用向けに束ねる + */ + +/** グリッドマップ関連の型を再公開する */ +export type { MapState, CellUpdate } from "./gridMap.type"; +/** グリッド座標変換ロジックを再公開する */ +export { getGridIndexFromPosition } from "./gridMap.logic"; diff --git a/packages/shared/src/domains/index.ts b/packages/shared/src/domains/index.ts new file mode 100644 index 0000000..15dcb80 --- /dev/null +++ b/packages/shared/src/domains/index.ts @@ -0,0 +1,16 @@ +/** + * index + * domains 配下の公開要素を集約して再公開する + * ドメイン単位で参照できる安定公開面を提供する + */ + +/** app ドメインを再公開する */ +export * as app from "./app"; +/** game ドメインを再公開する */ +export * as game from "./game"; +/** gridMap ドメインを再公開する */ +export * as gridMap from "./gridMap"; +/** player ドメインを再公開する */ +export * as player from "./player"; +/** room ドメインを再公開する */ +export * as room from "./room"; diff --git a/packages/shared/src/domains/player/index.ts b/packages/shared/src/domains/player/index.ts new file mode 100644 index 0000000..9ce56af --- /dev/null +++ b/packages/shared/src/domains/player/index.ts @@ -0,0 +1,8 @@ +/** + * index + * player ドメインの公開要素を集約して再公開する + * プレイヤー契約で利用する型を外部利用向けに束ねる + */ + +/** プレイヤー契約関連の型を再公開する */ +export type { PlayerData, MovePayload } from "./player.type"; diff --git a/packages/shared/src/domains/player/player.type.ts b/packages/shared/src/domains/player/player.type.ts index c478463..108052d 100644 --- a/packages/shared/src/domains/player/player.type.ts +++ b/packages/shared/src/domains/player/player.type.ts @@ -1,4 +1,10 @@ -// クライアント・サーバー間共有プレイヤー基本情報型 +/** + * player.type + * プレイヤー領域で利用する共有型を定義する + * クライアントとサーバーで参照する契約を集約する + */ + +/** クライアントとサーバー間で共有するプレイヤー基本情報 */ export interface PlayerData { id: string; name: string; @@ -8,7 +14,7 @@ teamId: number; // 0〜3 のチームID } -// 移動イベント送信ペイロード型 +/** MOVE イベントで利用する移動入力ペイロード */ export interface MovePayload { // グリッド単位の座標 x: number; diff --git a/packages/shared/src/domains/room/index.ts b/packages/shared/src/domains/room/index.ts new file mode 100644 index 0000000..cd19cd4 --- /dev/null +++ b/packages/shared/src/domains/room/index.ts @@ -0,0 +1,18 @@ +/** + * index + * room ドメインの公開要素を集約して再公開する + * ルーム状態契約の型と定数を外部利用向けに束ねる + */ + +/** ルーム進行フェーズ定数を再公開する */ +export { RoomPhase } from "./room.const"; +/** ルーム進行フェーズ型を再公開する */ +export type { RoomPhase as RoomPhaseType } from "./room.const"; +/** ルーム契約関連の型を再公開する */ +export type { + RoomMember, + Room, + JoinRoomPayload, + JoinRoomRejectedReason, + JoinRoomRejectedPayload, +} from "./room.type"; diff --git a/packages/shared/src/domains/room/room.const.ts b/packages/shared/src/domains/room/room.const.ts index bf0f189..ee3ece3 100644 --- a/packages/shared/src/domains/room/room.const.ts +++ b/packages/shared/src/domains/room/room.const.ts @@ -1,6 +1,11 @@ +/** + * room.const + * ルーム領域で利用する定数を定義する + * 進行フェーズ値を外部利用向けに提供する + */ import type { RoomPhase as RoomPhaseType } from "./room.type"; -// ルーム進行フェーズ状態の値 +/** ルーム進行フェーズで利用する定数 */ export const RoomPhase = { WAITING: "waiting", PLAYING: "playing", diff --git a/packages/shared/src/domains/room/room.type.ts b/packages/shared/src/domains/room/room.type.ts index f69dcbd..ce8c529 100644 --- a/packages/shared/src/domains/room/room.type.ts +++ b/packages/shared/src/domains/room/room.type.ts @@ -1,7 +1,13 @@ -// ルーム進行フェーズ状態型 +/** + * room.type + * ルーム領域で利用する共有型を定義する + * 参加状態とイベントペイロード契約を集約する + */ + +/** ルーム進行フェーズ状態型 */ export type RoomPhase = "waiting" | "playing" | "result"; -// ルーム所属プレイヤー情報型 +/** ルーム所属プレイヤー情報 */ export interface RoomMember { id: string; name: string; @@ -9,7 +15,7 @@ isReady: boolean; } -// ルーム全体状態データ構造型 +/** ルーム全体状態データ */ export interface Room { roomId: string; ownerId: string; @@ -18,16 +24,16 @@ maxPlayers: number; } -// ルーム参加時送信ペイロード型 +/** ルーム参加時に送信するペイロード */ export interface JoinRoomPayload { roomId: string; playerName: string; } -// ルーム参加拒否理由型 +/** ルーム参加拒否理由 */ export type JoinRoomRejectedReason = "full" | "duplicate"; -// ルーム参加拒否通知ペイロード型 +/** ルーム参加拒否通知ペイロード */ export interface JoinRoomRejectedPayload { roomId: string; reason: JoinRoomRejectedReason; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 8eb0070..4f69fcd 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,8 +1,14 @@ /** * index * shared パッケージの公開 API を集約して再公開するエントリ - * ドメイン型,プロトコル型,設定値を外部利用向けに束ねる + * 安定公開面と既存互換公開面を併存して外部利用向けに束ねる */ +/** 安定公開面として domains の集約を再公開 */ +export * as domain from "./domains"; +/** 安定公開面として protocol 契約の集約を再公開 */ +export * as contracts from "./protocol"; + +/** 既存互換のため,以下は従来どおり再公開する */ /** グリッドマップ関連の型定義を再公開 */ export * as gridMapTypes from "./domains/gridMap/gridMap.type"; /** グリッドマップ関連のロジックを再公開 */ diff --git a/packages/shared/src/protocol/eventPayloadMaps.ts b/packages/shared/src/protocol/eventPayloadMaps.ts index eabb623..5f371c3 100644 --- a/packages/shared/src/protocol/eventPayloadMaps.ts +++ b/packages/shared/src/protocol/eventPayloadMaps.ts @@ -4,7 +4,7 @@ * 機能別に分割した対応表を合成して公開契約を維持する */ import type { - ConnectionLifecycleEventPayloadMap as CommonConnectionLifecycleEventPayloadMap, + ConnectionLifecycleEventPayloadMap, } from "./maps/commonEventPayloadMap"; import type { LobbyClientToServerEventPayloadMap, @@ -18,9 +18,6 @@ /** 接続ライフサイクルイベントのペイロード対応表を再公開する */ export type { ConnectionLifecycleEventPayloadMap } from "./maps/commonEventPayloadMap"; -/** 接続ライフサイクルイベントのペイロード対応表 */ -type ConnectionLifecycleEventPayloadMap = CommonConnectionLifecycleEventPayloadMap; - /** クライアントからサーバーへ送信するイベントごとのペイロード対応表 */ export type ClientToServerEventPayloadMap = & LobbyClientToServerEventPayloadMap diff --git a/packages/shared/src/protocol/eventPayloads.ts b/packages/shared/src/protocol/eventPayloads.ts index e444c18..3276144 100644 --- a/packages/shared/src/protocol/eventPayloads.ts +++ b/packages/shared/src/protocol/eventPayloads.ts @@ -16,6 +16,8 @@ /** ゲームイベントのペイロード型を再公開する */ export type { + PlayerSnapshotPayload, + PlayerDeltaPayload, InitialPlayerSyncPayload, DeltaPlayerSyncPayload, UpdatePlayersPayload, diff --git a/packages/shared/src/protocol/index.ts b/packages/shared/src/protocol/index.ts new file mode 100644 index 0000000..f6e00f1 --- /dev/null +++ b/packages/shared/src/protocol/index.ts @@ -0,0 +1,12 @@ +/** + * index + * protocol 配下の公開要素を集約して再公開する + * イベント契約と bridge をまとめた安定公開面を提供する + */ + +/** ソケットイベント契約を再公開する */ +export * from "./events"; +/** ソケットイベント bridge を再公開する */ +export { createSocketEventBridge } from "./socketEventBridge"; +/** ソケットイベント bridge の対象インターフェースを再公開する */ +export type { SocketBridgeTarget } from "./socketEventBridge"; diff --git a/packages/shared/src/protocol/maps/gameEventPayloadMap.ts b/packages/shared/src/protocol/maps/gameEventPayloadMap.ts index 7cc6fc9..2772b08 100644 --- a/packages/shared/src/protocol/maps/gameEventPayloadMap.ts +++ b/packages/shared/src/protocol/maps/gameEventPayloadMap.ts @@ -5,6 +5,10 @@ */ import { SocketEvents } from "../socketEvents"; import type { + PingPayload, + PongPayload, +} from "../payloads/commonPayloads"; +import type { BombHitReportPayload, BombPlacedAckPayload, BombPlacedPayload, @@ -16,12 +20,10 @@ NewPlayerPayload, PlaceBombPayload, PlayerDeadPayload, - PingPayload, - PongPayload, RemovePlayerPayload, UpdateMapCellsPayload, UpdatePlayersPayload, -} from "../eventPayloads"; +} from "../payloads/gamePayloads"; /** ゲーム関連のクライアント送信イベントペイロード対応表 */ export type GameClientToServerEventPayloadMap = { @@ -36,11 +38,11 @@ /** ゲーム関連のサーバー送信イベントペイロード対応表 */ export type GameServerToClientEventPayloadMap = { [SocketEvents.GAME_START]: GameStartPayload; - [SocketEvents.CURRENT_PLAYERS]: CurrentPlayersPayload; - [SocketEvents.NEW_PLAYER]: NewPlayerPayload; - [SocketEvents.UPDATE_PLAYERS]: UpdatePlayersPayload; - [SocketEvents.REMOVE_PLAYER]: RemovePlayerPayload; - [SocketEvents.UPDATE_MAP_CELLS]: UpdateMapCellsPayload; + [SocketEvents.CURRENT_PLAYERS_SYNC]: CurrentPlayersPayload; + [SocketEvents.NEW_PLAYER_SYNC]: NewPlayerPayload; + [SocketEvents.UPDATE_PLAYERS_SYNC]: UpdatePlayersPayload; + [SocketEvents.REMOVE_PLAYER_SYNC]: RemovePlayerPayload; + [SocketEvents.UPDATE_MAP_CELLS_SYNC]: UpdateMapCellsPayload; [SocketEvents.BOMB_PLACED]: BombPlacedPayload; [SocketEvents.BOMB_PLACED_ACK]: BombPlacedAckPayload; [SocketEvents.PLAYER_DEAD]: PlayerDeadPayload; diff --git a/packages/shared/src/protocol/maps/lobbyEventPayloadMap.ts b/packages/shared/src/protocol/maps/lobbyEventPayloadMap.ts index 796ff47..9625774 100644 --- a/packages/shared/src/protocol/maps/lobbyEventPayloadMap.ts +++ b/packages/shared/src/protocol/maps/lobbyEventPayloadMap.ts @@ -8,7 +8,7 @@ JoinRoomPayload, RoomJoinRejectedPayload, RoomUpdatePayload, -} from "../eventPayloads"; +} from "../payloads/lobbyPayloads"; /** ロビー関連のクライアント送信イベントペイロード対応表 */ export type LobbyClientToServerEventPayloadMap = { diff --git a/packages/shared/src/protocol/payloads/gamePayloads.ts b/packages/shared/src/protocol/payloads/gamePayloads.ts index a8d5999..6e1d6b2 100644 --- a/packages/shared/src/protocol/payloads/gamePayloads.ts +++ b/packages/shared/src/protocol/payloads/gamePayloads.ts @@ -10,7 +10,7 @@ PlayerData, } from "../../domains/player/player.type"; -/** GAME_RESULT イベントで送受信するランキング1行 */ +/** game-result イベントで送受信するランキング1行 */ export type GameResultRanking = { rank: number; teamId: number; @@ -18,53 +18,62 @@ paintRate: number; }; -/** GAME_RESULT イベントで送受信する最終結果 */ +/** game-result イベントで送受信する最終結果 */ export type GameResultPayload = { rankings: GameResultRanking[]; }; -/** - * 初期同期(CURRENT_PLAYERS)で利用するプレイヤー一覧 - * 初期同期用のため teamId を含む完全な PlayerData を配信する - */ -export type InitialPlayerSyncPayload = PlayerData[]; +/** current-players で配信するプレイヤー全体スナップショット */ +export type PlayerSnapshotPayload = PlayerData[]; /** - * 差分同期(UPDATE_PLAYERS)で利用するプレイヤー差分配列 + * update-players で配信するプレイヤー差分配列 * 帯域最適化のため teamId は含めず,id/x/y のみを配信する */ -export type DeltaPlayerSyncPayload = PlayerPositionUpdate[]; +export type PlayerDeltaPayload = PlayerPositionUpdate[]; -/** UPDATE_PLAYERS イベントで送受信するプレイヤー差分配列 */ -export type UpdatePlayersPayload = DeltaPlayerSyncPayload; +/** + * 初期同期(current-players)で利用するプレイヤー一覧 + * 初期同期用のため teamId を含む完全な PlayerData を配信する + */ +export type InitialPlayerSyncPayload = PlayerSnapshotPayload; -/** CURRENT_PLAYERS イベントで送受信するプレイヤー一覧 */ -export type CurrentPlayersPayload = InitialPlayerSyncPayload; +/** + * 差分同期(update-players)で利用するプレイヤー差分配列 + * 帯域最適化のため teamId は含めず,id/x/y のみを配信する + */ +export type DeltaPlayerSyncPayload = PlayerDeltaPayload; -/** UPDATE_MAP_CELLS イベントで送受信するマップ差分配列 */ +/** update-players イベントで送受信するプレイヤー差分配列 */ +export type UpdatePlayersPayload = PlayerDeltaPayload; + +/** current-players イベントで送受信するプレイヤー一覧 */ +export type CurrentPlayersPayload = PlayerSnapshotPayload; + +/** update-map-cells イベントで送受信するマップ差分配列 */ export type UpdateMapCellsPayload = CellUpdate[]; /** - * NEW_PLAYER イベントで送受信するプレイヤー情報 + * new-player イベントで送受信するプレイヤー情報 * 初回参加通知のため teamId を含む完全な PlayerData を配信する */ export type NewPlayerPayload = PlayerData; -/** REMOVE_PLAYER イベントで送受信するプレイヤーID */ +/** remove-player イベントで送受信するプレイヤーID */ export type RemovePlayerPayload = PlayerData["id"]; -/** GAME_START イベントで送受信するゲーム開始情報 */ +/** game-start イベントで送受信するゲーム開始情報 */ export type GameStartPayload = { startTime: number }; -/** START_GAME イベントで受信するゲーム開始要求 */ +/** start-game イベントで受信するゲーム開始要求 */ export type StartGameRequestPayload = { targetPlayerCount?: number; }; -/** MOVE イベントで送受信する移動入力情報 */ +/** move イベントで送受信する移動入力情報 */ export type MovePayload = PlayerMovePayload; -/** PLACE_BOMB イベントで送受信する爆弾設置要求 */ +/** place-bomb イベントで送受信する爆弾設置要求 */ export type PlaceBombPayload = { requestId: string; x: number; @@ -72,7 +81,7 @@ explodeAtElapsedMs: number; }; -/** BOMB_PLACED イベントで送受信する他プレイヤー向け爆弾確定情報,設置者識別は ownerSocketId で扱う */ +/** bomb-placed イベントで送受信する他プレイヤー向け爆弾確定情報,設置者識別は ownerSocketId で扱う */ export type BombPlacedPayload = { bombId: string; ownerSocketId: string; @@ -81,18 +90,18 @@ explodeAtElapsedMs: number; }; -/** BOMB_PLACED_ACK イベントで送受信する設置者向け確定情報 */ +/** bomb-placed-ack イベントで送受信する設置者向け確定情報 */ export type BombPlacedAckPayload = { bombId: string; requestId: string; }; -/** BOMB_HIT_REPORT イベントで送受信する被弾報告 */ +/** bomb-hit-report イベントで送受信する被弾報告 */ export type BombHitReportPayload = { bombId: string; }; -/** PLAYER_DEAD イベントで送受信する死亡プレイヤー情報 */ +/** player-dead イベントで送受信する死亡プレイヤー情報 */ export type PlayerDeadPayload = { playerId: string; }; diff --git a/packages/shared/src/protocol/socketEventBridge.ts b/packages/shared/src/protocol/socketEventBridge.ts index a5f61e2..8055445 100644 --- a/packages/shared/src/protocol/socketEventBridge.ts +++ b/packages/shared/src/protocol/socketEventBridge.ts @@ -8,10 +8,13 @@ /** ソケットブリッジ生成に必要な最小インターフェース */ 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; - emit: (event: string, payload?: unknown) => void; + on: (event: string, callback: (payload: TPayload) => void) => void; + once: (event: string, callback: (payload: TPayload) => void) => void; + off: (event: string, callback: (payload: TPayload) => void) => void; + emit: { + (event: string): void; + (event: string, payload: TPayload): void; + }; }; /** @@ -25,21 +28,21 @@ event: TEvent, callback: (payload: TInboundMap[TEvent]) => void ) => { - socket.on(event, callback as (payload: unknown) => void); + socket.on(event, callback); }; const onceEvent = >( event: TEvent, callback: (payload: TInboundMap[TEvent]) => void ) => { - socket.once(event, callback as (payload: unknown) => void); + socket.once(event, callback); }; const offEvent = >( event: TEvent, callback: (payload: TInboundMap[TEvent]) => void ) => { - socket.off(event, callback as (payload: unknown) => void); + socket.off(event, callback); }; function emitEvent>(event: TEvent): void; @@ -50,7 +53,7 @@ return; } - socket.emit(event, payload as unknown); + socket.emit(event, payload); } return { diff --git a/packages/shared/src/protocol/socketEvents.ts b/packages/shared/src/protocol/socketEvents.ts index f2c9a50..f896567 100644 --- a/packages/shared/src/protocol/socketEvents.ts +++ b/packages/shared/src/protocol/socketEvents.ts @@ -19,14 +19,21 @@ READY_FOR_GAME: "ready-for-game", // ゲームプレイ関連イベント名 - CURRENT_PLAYERS: "current_players", - NEW_PLAYER: "new_player", - UPDATE_PLAYERS: "update_players", - REMOVE_PLAYER: "remove_player", + CURRENT_PLAYERS_SYNC: "current-players", + NEW_PLAYER_SYNC: "new-player", + UPDATE_PLAYERS_SYNC: "update-players", + REMOVE_PLAYER_SYNC: "remove-player", + UPDATE_MAP_CELLS_SYNC: "update-map-cells", + + // 互換維持のため残す旧キー名 + CURRENT_PLAYERS: "current-players", + NEW_PLAYER: "new-player", + UPDATE_PLAYERS: "update-players", + REMOVE_PLAYER: "remove-player", MOVE: "move", PLACE_BOMB: "place-bomb", BOMB_HIT_REPORT: "bomb-hit-report", - UPDATE_MAP_CELLS: "update_map_cells", + UPDATE_MAP_CELLS: "update-map-cells", BOMB_PLACED: "bomb-placed", BOMB_PLACED_ACK: "bomb-placed-ack", PLAYER_DEAD: "player-dead", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37621b8..aecee8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,6 +90,12 @@ packages/shared: devDependencies: + '@typescript-eslint/parser': + specifier: ^8.56.1 + version: 8.56.1(eslint@10.0.2)(typescript@5.9.3) + eslint: + specifier: ^10.0.2 + version: 10.0.2 tsup: specifier: ^8.5.1 version: 8.5.1(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) diff --git a/test/load-bot.ts b/test/load-bot.ts index df8930e..c66661f 100644 --- a/test/load-bot.ts +++ b/test/load-bot.ts @@ -233,7 +233,7 @@ } }); - socket.on("current_players", (players: CurrentPlayer[]) => { + socket.on("current-players", (players: CurrentPlayer[]) => { const self = players.find((player) => player.id === socket.id); if (self) { posX = self.x;