diff --git a/apps/server/src/domains/game/entities/bom/BombRoomStateStore.ts b/apps/server/src/domains/game/entities/bom/BombRoomStateStore.ts new file mode 100644 index 0000000..7bae822 --- /dev/null +++ b/apps/server/src/domains/game/entities/bom/BombRoomStateStore.ts @@ -0,0 +1,13 @@ +/** + * BombRoomStateStore + * bomb命名互換のためにbomb実装を再公開する + */ + +/** 爆弾設置イベント配信の重複排除判定関数を再公開する */ +export { shouldBroadcastBombPlaced } from "../bomb/BombRoomStateStore"; + +/** サーバー採番の爆弾ID生成関数を再公開する */ +export { issueServerBombId } from "../bomb/BombRoomStateStore"; + +/** ルーム単位の爆弾状態破棄関数を再公開する */ +export { clearBombRoomState } from "../bomb/BombRoomStateStore"; diff --git a/apps/server/src/domains/game/entities/bomb/BombRoomStateStore.ts b/apps/server/src/domains/game/entities/bomb/BombRoomStateStore.ts new file mode 100644 index 0000000..d305563 --- /dev/null +++ b/apps/server/src/domains/game/entities/bomb/BombRoomStateStore.ts @@ -0,0 +1,62 @@ +/** + * BombRoomStateStore + * ルーム単位の爆弾重複排除状態と採番状態を管理する + */ +import { config } from "@repo/shared"; + +type BombRoomStateClearReason = "game-ended" | "room-deleted"; + +const roomBombDedupTable = new Map>(); +const roomBombSerialTable = new Map(); +const isBombRoomStateDebugEnabled = process.env.NODE_ENV !== "production"; + +const cleanupExpiredBombDedup = (roomId: string, nowMs: number) => { + const roomTable = roomBombDedupTable.get(roomId); + if (!roomTable) return; + + roomTable.forEach((expiresAtMs, dedupeKey) => { + if (expiresAtMs <= nowMs) { + roomTable.delete(dedupeKey); + } + }); + + if (roomTable.size === 0) { + roomBombDedupTable.delete(roomId); + } +}; + +/** 爆弾設置イベントを配信すべきか判定し,配信時は重複排除状態を更新する */ +export const shouldBroadcastBombPlaced = (roomId: string, dedupeKey: string, nowMs: number): boolean => { + cleanupExpiredBombDedup(roomId, nowMs); + + const roomTable = roomBombDedupTable.get(roomId) ?? new Map(); + if (roomTable.has(dedupeKey)) { + return false; + } + + const ttlMs = config.GAME_CONFIG.BOMB_FUSE_MS + config.GAME_CONFIG.BOMB_DEDUP_EXTRA_TTL_MS; + roomTable.set(dedupeKey, nowMs + ttlMs); + roomBombDedupTable.set(roomId, roomTable); + return true; +}; + +/** ルーム単位の連番からサーバー採番の爆弾IDを生成する */ +export const issueServerBombId = (roomId: string): string => { + const serial = (roomBombSerialTable.get(roomId) ?? 0) + 1; + roomBombSerialTable.set(roomId, serial); + return `${roomId}:${serial}`; +}; + +/** 指定ルームの爆弾採番状態と重複排除状態を破棄する */ +export const clearBombRoomState = (roomId: string, reason: BombRoomStateClearReason): void => { + const hadDedupState = roomBombDedupTable.delete(roomId); + const hadSerialState = roomBombSerialTable.delete(roomId); + + if (!isBombRoomStateDebugEnabled) { + return; + } + + console.debug( + `[BombState] cleared room=${roomId} reason=${reason} dedup=${hadDedupState} serial=${hadSerialState}` + ); +}; diff --git a/apps/server/src/network/handlers/GameHandler.ts b/apps/server/src/network/handlers/GameHandler.ts index 891f520..ac3b899 100644 --- a/apps/server/src/network/handlers/GameHandler.ts +++ b/apps/server/src/network/handlers/GameHandler.ts @@ -7,4 +7,4 @@ export { registerGameHandlers } from "./game/registerGameHandlers"; /** ルーム終了時の爆弾状態掃除関数を外部参照向けに再公開 */ -export { clearBombRoomState } from "./game/bombRoomStateStore"; +export { clearBombRoomState } from "@server/domains/game/entities/bomb/BombRoomStateStore"; diff --git a/apps/server/src/network/handlers/game/bombRoomStateStore.ts b/apps/server/src/network/handlers/game/bombRoomStateStore.ts index 53a96e1..0c34d10 100644 --- a/apps/server/src/network/handlers/game/bombRoomStateStore.ts +++ b/apps/server/src/network/handlers/game/bombRoomStateStore.ts @@ -1,62 +1,13 @@ /** * bombRoomStateStore - * ルーム単位の爆弾重複排除状態と採番状態を管理する + * 爆弾状態管理の互換公開口としてdomains実装を再公開する */ -import { config } from "@repo/shared"; -type BombRoomStateClearReason = "game-ended" | "room-deleted"; +/** 爆弾設置イベント配信の重複排除判定関数を再公開する */ +export { shouldBroadcastBombPlaced } from "@server/domains/game/entities/bomb/BombRoomStateStore"; -const roomBombDedupTable = new Map>(); -const roomBombSerialTable = new Map(); -const isBombRoomStateDebugEnabled = process.env.NODE_ENV !== "production"; +/** サーバー採番の爆弾ID生成関数を再公開する */ +export { issueServerBombId } from "@server/domains/game/entities/bomb/BombRoomStateStore"; -const cleanupExpiredBombDedup = (roomId: string, nowMs: number) => { - const roomTable = roomBombDedupTable.get(roomId); - if (!roomTable) return; - - roomTable.forEach((expiresAtMs, dedupeKey) => { - if (expiresAtMs <= nowMs) { - roomTable.delete(dedupeKey); - } - }); - - if (roomTable.size === 0) { - roomBombDedupTable.delete(roomId); - } -}; - -/** 爆弾設置イベントを配信すべきか判定し,配信時は重複排除状態を更新する */ -export const shouldBroadcastBombPlaced = (roomId: string, dedupeKey: string, nowMs: number): boolean => { - cleanupExpiredBombDedup(roomId, nowMs); - - const roomTable = roomBombDedupTable.get(roomId) ?? new Map(); - if (roomTable.has(dedupeKey)) { - return false; - } - - const ttlMs = config.GAME_CONFIG.BOMB_FUSE_MS + config.GAME_CONFIG.BOMB_DEDUP_EXTRA_TTL_MS; - roomTable.set(dedupeKey, nowMs + ttlMs); - roomBombDedupTable.set(roomId, roomTable); - return true; -}; - -/** ルーム単位の連番からサーバー採番の爆弾IDを生成する */ -export const issueServerBombId = (roomId: string): string => { - const serial = (roomBombSerialTable.get(roomId) ?? 0) + 1; - roomBombSerialTable.set(roomId, serial); - return `${roomId}:${serial}`; -}; - -/** 指定ルームの爆弾採番状態と重複排除状態を破棄する */ -export const clearBombRoomState = (roomId: string, reason: BombRoomStateClearReason): void => { - const hadDedupState = roomBombDedupTable.delete(roomId); - const hadSerialState = roomBombSerialTable.delete(roomId); - - if (!isBombRoomStateDebugEnabled) { - return; - } - - console.debug( - `[BombState] cleared room=${roomId} reason=${reason} dedup=${hadDedupState} serial=${hadSerialState}` - ); -}; +/** ルーム単位の爆弾状態破棄関数を再公開する */ +export { clearBombRoomState } from "@server/domains/game/entities/bomb/BombRoomStateStore"; diff --git a/apps/server/src/network/handlers/game/registerGameHandlers.ts b/apps/server/src/network/handlers/game/registerGameHandlers.ts index 89f3d33..75ea885 100644 --- a/apps/server/src/network/handlers/game/registerGameHandlers.ts +++ b/apps/server/src/network/handlers/game/registerGameHandlers.ts @@ -19,8 +19,8 @@ import { isMovePayload, isPingPayload, isPlaceBombPayload } from "@server/network/validation/socketPayloadValidators"; import { createServerSocketOnBridge } from "@server/network/handlers/socketEventBridge"; import { createPayloadGuard } from "@server/network/handlers/payloadGuard"; +import { clearBombRoomState, issueServerBombId, shouldBroadcastBombPlaced } from "@server/domains/game/entities/bomb/BombRoomStateStore"; import { createGameOutputAdapter } from "./createGameOutputAdapter"; -import { clearBombRoomState, issueServerBombId, shouldBroadcastBombPlaced } from "./bombRoomStateStore"; /** ゲーム受信イベントごとの入力検証関数を保持するテーブル */ const gamePayloadValidators = {