diff --git a/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts b/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts index 0ff7535..a6beb7e 100644 --- a/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts +++ b/apps/server/src/domains/game/application/ports/gameUseCasePorts.ts @@ -73,6 +73,10 @@ roomId: domain.room.Room["roomId"], hurricanes: UpdateHurricanesPayload, ): void; + publishInitialHurricanesToRoom( + roomId: domain.room.Room["roomId"], + hurricanes: UpdateHurricanesPayload, + ): void; publishGameEndToRoom(roomId: domain.room.Room["roomId"]): void; publishGameResultToRoom( roomId: domain.room.Room["roomId"], @@ -125,6 +129,7 @@ GameOutputPort, | "publishUpdatePlayersToRoom" | "publishMapCellUpdatesToRoom" + | "publishInitialHurricanesToRoom" | "publishUpdateHurricanesToRoom" | "publishGameEndToRoom" | "publishGameResultToRoom" diff --git a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts index 5301633..0ca5c95 100644 --- a/apps/server/src/domains/game/application/useCases/startGameUseCase.ts +++ b/apps/server/src/domains/game/application/useCases/startGameUseCase.ts @@ -50,6 +50,13 @@ output.publishMapCellUpdatesToRoom(roomId, tickData.cellUpdates); } + if (tickData.hurricaneInitialSnapshot.length > 0) { + output.publishInitialHurricanesToRoom( + roomId, + tickData.hurricaneInitialSnapshot, + ); + } + if (tickData.hurricaneUpdates.length > 0) { output.publishUpdateHurricanesToRoom(roomId, tickData.hurricaneUpdates); } diff --git a/apps/server/src/domains/game/loop/GameLoop.ts b/apps/server/src/domains/game/loop/GameLoop.ts index 3a8ae13..fb1755a 100644 --- a/apps/server/src/domains/game/loop/GameLoop.ts +++ b/apps/server/src/domains/game/loop/GameLoop.ts @@ -263,10 +263,13 @@ const activePlayerIds = new Set(); const playerUpdates = this.collectChangedPlayerUpdates(activePlayerIds); this.cleanupInactivePlayerSnapshots(activePlayerIds); + const hurricaneInitialSnapshot = + this.hurricaneSystem.consumeInitialSyncPayload(); return { playerUpdates, cellUpdates: this.mapStore.getAndClearUpdates(), + hurricaneInitialSnapshot, hurricaneUpdates: this.hurricaneSystem.getUpdatePayload(), }; } diff --git a/apps/server/src/domains/game/loop/HurricaneSystem.ts b/apps/server/src/domains/game/loop/HurricaneSystem.ts index 7c49b1a..9208e1d 100644 --- a/apps/server/src/domains/game/loop/HurricaneSystem.ts +++ b/apps/server/src/domains/game/loop/HurricaneSystem.ts @@ -75,6 +75,7 @@ export class HurricaneSystem { private readonly mapSize: MapGridSize; private hasSpawned = false; + private hasInitialSyncPending = false; private hurricanes: HurricaneState[] = []; private readonly lastHitAtMsByPlayerId = new Map(); private readonly lastSentSnapshotByHurricaneId = new Map< @@ -99,12 +100,35 @@ } this.hasSpawned = true; + this.hasInitialSyncPending = true; this.hurricanes = Array.from( { length: config.GAME_CONFIG.HURRICANE_COUNT }, (_, index) => this.createHurricane(index), ); } + /** 出現直後に1回だけ配信用の初期同期スナップショットを返す */ + public consumeInitialSyncPayload(): HurricaneStatePayload[] { + if (!this.hasInitialSyncPending) { + return []; + } + + this.hasInitialSyncPending = false; + + return this.hurricanes.map((hurricane) => { + const snapshot = toHurricaneSyncSnapshot(hurricane); + this.lastSentSnapshotByHurricaneId.set(hurricane.id, snapshot); + + return { + id: hurricane.id, + x: snapshot.x, + y: snapshot.y, + radius: snapshot.radius, + rotationRad: snapshot.rotationRad, + }; + }); + } + /** ハリケーンを直線移動させ,境界で反射させる */ public update(deltaSec: number): void { if (this.hurricanes.length === 0) { @@ -212,6 +236,7 @@ /** 状態を初期化する */ public clear(): void { this.hasSpawned = false; + this.hasInitialSyncPending = false; this.hurricanes = []; this.lastHitAtMsByPlayerId.clear(); this.lastSentSnapshotByHurricaneId.clear(); diff --git a/apps/server/src/network/handlers/game/createGameOutputAdapter.ts b/apps/server/src/network/handlers/game/createGameOutputAdapter.ts index c1a3d03..18d0370 100644 --- a/apps/server/src/network/handlers/game/createGameOutputAdapter.ts +++ b/apps/server/src/network/handlers/game/createGameOutputAdapter.ts @@ -94,7 +94,17 @@ roomId: RoomId, hurricanes: UpdateHurricanesPayload, ) => { - realtime.emitToRoom( + reliable.emitToRoom( + roomId, + protocol.SocketEvents.UPDATE_HURRICANES, + hurricanes, + ); + }, + publishInitialHurricanesToRoom: ( + roomId: RoomId, + hurricanes: UpdateHurricanesPayload, + ) => { + reliable.emitToRoom( roomId, protocol.SocketEvents.UPDATE_HURRICANES, hurricanes, diff --git a/packages/shared/src/domains/game/tick/tick.type.ts b/packages/shared/src/domains/game/tick/tick.type.ts index c1ab933..d19a27c 100644 --- a/packages/shared/src/domains/game/tick/tick.type.ts +++ b/packages/shared/src/domains/game/tick/tick.type.ts @@ -13,5 +13,7 @@ export interface TickData { playerUpdates: PlayerPositionUpdate[]; cellUpdates: CellUpdate[]; + /** 出現直後に1回だけ配信するハリケーン初期同期スナップショット */ + hurricaneInitialSnapshot: HurricaneStatePayload[]; hurricaneUpdates: HurricaneStatePayload[]; }