diff --git a/apps/client/src/scenes/game/application/network/handlers/MapSyncHandler.ts b/apps/client/src/scenes/game/application/network/handlers/MapSyncHandler.ts index 2e5f871..3762863 100644 --- a/apps/client/src/scenes/game/application/network/handlers/MapSyncHandler.ts +++ b/apps/client/src/scenes/game/application/network/handlers/MapSyncHandler.ts @@ -1,9 +1,10 @@ /** * MapSyncHandler * マップ同期イベントの受信処理を担当する - * セル更新データをマップへ適用する + * グループ化セル更新データを展開してマップへ適用する */ import type { UpdateMapCellsPayload } from "@repo/shared"; +import { domain } from "@repo/shared"; import { GameMapController } from "@client/scenes/game/entities/map/GameMapController"; /** MapSyncHandler の初期化入力 */ @@ -19,8 +20,9 @@ this.gameMap = gameMap; } - /** マップセル更新を適用する */ - public handleUpdateMapCells = (updates: UpdateMapCellsPayload): void => { + /** グループ化マップセル更新を展開して適用する */ + public handleUpdateMapCells = (grouped: UpdateMapCellsPayload): void => { + const updates = domain.game.gridMap.ungroupCellUpdates(grouped); this.gameMap.updateCells(updates); }; } \ No newline at end of file diff --git a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts index c800c3c..96de2f4 100644 --- a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts @@ -14,6 +14,7 @@ logScopes, } from "@server/logging/index"; import { createBotBombActionHandler } from "../services/bot/index.js"; +import { domain } from "@repo/shared"; const excludeRecipientFromPlayerUpdates = < TPlayerUpdate extends { id: string }, @@ -83,7 +84,10 @@ } if (tickData.cellUpdates.length > 0) { - output.publishMapCellUpdatesToRoom(roomId, tickData.cellUpdates); + output.publishMapCellUpdatesToRoom( + roomId, + domain.game.gridMap.groupCellUpdates(tickData.cellUpdates), + ); } }, onGameEnd: (resultPayload) => { diff --git a/packages/shared/src/domains/game/gridMap/gridMap.type.ts b/packages/shared/src/domains/game/gridMap/gridMap.type.ts index c59a2da..327c6cd 100644 --- a/packages/shared/src/domains/game/gridMap/gridMap.type.ts +++ b/packages/shared/src/domains/game/gridMap/gridMap.type.ts @@ -14,3 +14,10 @@ index: number; teamId: number; } + +/** + * teamId別にグループ化したマップ差分更新 + * キーは teamId(文字列),値は塗り替え対象セルの index 配列 + * 帯域最適化のためキー名の重複を排除した形式で送受信する + */ +export type GroupedCellUpdates = Record; diff --git a/packages/shared/src/domains/game/gridMap/groupedCellUpdates.ts b/packages/shared/src/domains/game/gridMap/groupedCellUpdates.ts new file mode 100644 index 0000000..758cd87 --- /dev/null +++ b/packages/shared/src/domains/game/gridMap/groupedCellUpdates.ts @@ -0,0 +1,43 @@ +/** + * groupedCellUpdates + * CellUpdate配列とGroupedCellUpdatesの相互変換ユーティリティを提供する + * サーバー送信時のグループ化とクライアント受信時の展開を担う + */ +import type { CellUpdate, GroupedCellUpdates } from "./gridMap.type"; + +/** CellUpdate 配列を teamId 別にグループ化する(送信時) */ +export const groupCellUpdates = ( + updates: CellUpdate[], +): GroupedCellUpdates => { + const grouped: GroupedCellUpdates = {}; + + for (const { index, teamId } of updates) { + const key = String(teamId); + const list = grouped[key]; + + if (list) { + list.push(index); + } else { + grouped[key] = [index]; + } + } + + return grouped; +}; + +/** GroupedCellUpdates を CellUpdate 配列へ展開する(受信時) */ +export const ungroupCellUpdates = ( + grouped: GroupedCellUpdates, +): CellUpdate[] => { + const updates: CellUpdate[] = []; + + for (const [teamIdStr, indices] of Object.entries(grouped)) { + const teamId = Number(teamIdStr); + + for (const index of indices) { + updates.push({ index, teamId }); + } + } + + return updates; +}; diff --git a/packages/shared/src/domains/game/gridMap/index.ts b/packages/shared/src/domains/game/gridMap/index.ts index cef8bbc..af9db0a 100644 --- a/packages/shared/src/domains/game/gridMap/index.ts +++ b/packages/shared/src/domains/game/gridMap/index.ts @@ -5,6 +5,8 @@ */ /** グリッドマップ関連の型を再公開する */ -export type { MapState, CellUpdate } from "./gridMap.type"; +export type { MapState, CellUpdate, GroupedCellUpdates } from "./gridMap.type"; /** グリッド座標変換ロジックを再公開する */ export { getGridIndexFromPosition } from "./gridMap.logic"; +/** CellUpdate配列とGroupedCellUpdatesの相互変換を再公開する */ +export { groupCellUpdates, ungroupCellUpdates } from "./groupedCellUpdates"; diff --git a/packages/shared/src/protocol/payloads/gamePayloads.ts b/packages/shared/src/protocol/payloads/gamePayloads.ts index 7090983..80752ad 100644 --- a/packages/shared/src/protocol/payloads/gamePayloads.ts +++ b/packages/shared/src/protocol/payloads/gamePayloads.ts @@ -4,7 +4,7 @@ * プレイヤー差分,マップ差分,開始終了系の契約を集約する */ import type { PlayerPositionUpdate } from "../../domains/game/tick/tick.type"; -import type { CellUpdate } from "../../domains/game/gridMap/gridMap.type"; +import type { GroupedCellUpdates } from "../../domains/game/gridMap/gridMap.type"; import type { MovePayload as PlayerMovePayload, PlayerData, @@ -52,8 +52,8 @@ /** current-players イベントで送受信するプレイヤー一覧 */ export type CurrentPlayersPayload = PlayerSnapshotPayload; -/** update-map-cells イベントで送受信するマップ差分配列 */ -export type UpdateMapCellsPayload = CellUpdate[]; +/** update-map-cells イベントで送受信するグループ化マップ差分 */ +export type UpdateMapCellsPayload = GroupedCellUpdates; /** * new-player イベントで送受信するプレイヤー情報