Repository navigation
Feature/in game websocket - #82
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthrough共有イベント型とワークスペース設定が追加され、バックエンドは WebSocket ゲートウェイとゲームサービスで試合進行を扱うようになった。フロントエンドはソケット駆動の状態管理、入力、結果表示、描画、プレビューに更新された。実行環境と Docker 設定も monorepo 構成へ調整された。 Changesボンバーマン共有契約とゲームフロー移行
Sequence Diagram(s)sequenceDiagram
participant User
participant useInputManager
participant useGameInput
participant Socket
participant GameGateway
participant GameService
participant useGameStore
User->>useInputManager: keydown / keyup / blur
useInputManager->>useGameInput: emitTouchInput(input)
useGameInput->>Socket: emit player:input / bomb:place
Socket->>GameGateway: player:input / bomb:place
GameGateway->>GameService: handlePlayerInput / handleBombPlace
GameService-->>Socket: game:init / game:state / bomb:explode / game:end
Socket-->>useGameStore: state updates
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related issues
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/pages/GameRoomPage.tsx (1)
27-50: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win存在しない部屋を仮ルームで埋めないでください。
この PR ではローカルゲーム実装を外しているので、未知の
roomIdでcurrentRoomを捏造すると不正な URL でもゲーム画面に進めてしまいます。そのままgame:joinも飛ぶため、ここはロビーへ戻すか not-found を表示した方が安全です。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/GameRoomPage.tsx` around lines 27 - 50, The fallback in GameRoomPage for an unknown roomId should not create a synthetic room object anymore, since that lets invalid URLs enter the game flow. Update the logic around the room lookup and setCurrentRoom usage so that when rooms.find(...) returns nothing, the page redirects back to the lobby or renders a not-found state instead of populating a fake local room. Keep the real room path unchanged for valid room matches.
🧹 Nitpick comments (1)
backend/src/game/game.service.spec.ts (1)
15-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftこのテストでは今回の回帰を拾えません。
定義確認だけだと、追加された
game:countdown→game:state→game:endの配線やrankingspayload の形が崩れても通ります。少なくとも room 開始/終了時の emit 順序と payload を固定するテストを 1 本入れておくと、この PR の主経路を守れます。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/src/game/game.service.spec.ts` around lines 15 - 17, The current GameService spec only checks that the service is defined, so it will not catch regressions in the new event flow. Add a focused test in game.service.spec.ts around GameService that verifies the room lifecycle emits game:countdown, then game:state, then game:end in that order, and asserts the rankings payload shape stays consistent. Use the existing GameService setup/methods to drive start/end behavior so the main path is covered even if the wiring changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/src/game/game.gateway.spec.ts`:
- Around line 1-2: The GameGateway test module is missing required DI mocks,
causing compile() to fail because GameGateway depends on GameService and
JwtService. Update the Test.createTestingModule setup in GameGateway spec to add
both dependencies under providers with mock implementations, and keep the
GameGateway setup aligned with its constructor injection so the test can resolve
successfully.
In `@backend/src/game/game.gateway.ts`:
- Around line 79-80: handleJoin/handleLeave では client.data.roomId
の更新だけでなく、既存の参加状態も必ず解除してください。GameGateway の handleJoin と game:leave の処理で、現在の
roomId がある場合は先に client.leave(previousRoomId) を呼んで旧 room を抜け、その後に
client.data.roomId をクリアまたは新しい roomId に更新するようにしてください。これにより GameGateway の room
切り替え時に古い room の参加状態が残らないようにします。
In `@backend/src/game/game.service.ts`:
- Around line 31-34: In GameService’s room join flow, the current unconditional
addPlayerToRoom call is overwriting active match state for both new joins during
an ongoing phase and re-joins with the same playerId. Update the join logic to
validate the room phase before admitting a player, and if the playerId already
exists, restore the existing player state instead of reinitializing
alive/position/stats. Also adjust removePlayerFromRoom so a disconnected player
cannot be implicitly revived by a later join unless the intended reconnection
path explicitly allows it.
- Around line 69-70: The player input handling in game.service.ts allows stale
`player:input` events to overwrite newer state because `seq` is stored without
any ordering check. Update the input update path in the relevant service method
that writes to `room.playerInputs[playerId]` so it compares the incoming `seq`
against the currently stored sequence for that player and ignores any input with
an equal or lower `seq`; only accept and persist newer inputs to keep
server-authoritative movement from rewinding.
- Around line 124-133: In GameService, the countdown cleanup only runs when
result.isEmpty, so a room that drops below the minimum players during countdown
keeps progressing. Update the player-removal flow in the relevant method around
stopGameLoop() and countdownTimerId handling to also detect when the room falls
under the required player count, clear the countdown timer, stop the game loop,
and roll the room phase back from countdown to the appropriate waiting state so
clients can observe the cancellation.
In `@backend/src/game/logic/bomb.logic.ts`:
- Around line 84-99: The bomb explosion hit check in the room.players loop
currently uses only Math.round(p.position.x/z), so it can disagree with the
player collision logic used elsewhere. Update the affectedTiles.some check in
bomb.logic.ts to evaluate overlap using the same player hitbox/collision size as
movement.logic.ts, rather than the player center point, and keep the
alive/kills/survivalTime updates unchanged.
In `@backend/src/game/logic/player.logic.ts`:
- Around line 16-18: The player spawn logic in player.logic.ts is using
Object.keys(room.players).length in the start-position/color selection path, so
a leaving player can cause the next joiner to reuse an occupied slot. Update the
logic around START_POSITIONS and PLAYER_COLORS to choose the first unused slot
based on existing player assignments rather than current player count, and make
sure the same unique slot is reserved until the player is removed.
- Around line 63-67: The waiting-room disconnect path in player.logic.ts removes
the player from room.players but leaves room.stats[playerId] behind, so stale
entries can later be included by evaluateGameEnd(). Update the disconnect
handling in the same block that deletes room.players/playerInputs to also remove
the matching entry from room.stats for the departing player. Keep the fix
localized around the waiting-state cleanup logic so the rankings data stays
consistent before game start.
In `@docker/backend/Dockerfile`:
- Line 16: The backend container startup path no longer runs Prisma migrations,
so a fresh Compose-up can start the API before the schema is applied. Update the
startup flow in the Docker backend entrypoint, around the CMD in the Dockerfile
and any related start script used by the backend workspace, so migrations run
automatically before `npm run start:dev`. Keep the existing manual Makefile
command as a fallback, but restore an automatic migration step for `prisma
migrate dev` or the equivalent startup migration command.
In `@docker/nginx/conf/nginx.conf`:
- Around line 36-43: Add the same long proxy timeout settings to the Socket.IO
proxy block so `/socket.io/` stays connected during idle periods; update the
`location /socket.io/` section in `nginx.conf` to mirror the timeout behavior
already used for `/api/`, alongside the existing WebSocket headers in that
block.
In `@frontend/src/components/game/BombermanScene.tsx`:
- Around line 208-216: The bomb material selection in BombermanScene is still
using the placeholder ownerId check against 'local-player', so self-owned bombs
are classified incorrectly. Update the bomb rendering logic in the map over
gameState.bombs to compare bomb.ownerId against the real myPlayerId saved by
game:init, and read that value from the store or props instead of the temporary
string. Keep the existing localBombMaterial/enemyBombMaterial branch, but base
it on the actual player ID field used throughout the scene.
In `@frontend/src/hooks/useGameInput.ts`:
- Line 1: The useGameInput hook imports MutableRefObject as a value import, but
with verbatimModuleSyntax enabled this must be a type-only import. Update the
React import in useGameInput so MutableRefObject is brought in via import type,
while keeping useCallback, useRef, and useState as normal imports.
In `@frontend/src/hooks/useGamePhaseTransition.ts`:
- Around line 13-27: The phase transition logic in useGamePhaseTransition is
incorrectly using Date.now() against countdown.startsAt to decide when to switch
to playing, which makes start timing depend on the client clock. Update the hook
so gamePhase changes only from a server-authoritative start signal, or derive
the comparison from the existing server-client time offset captured at
connection time instead of local time. Keep the timer/setGamePhase flow in
useGamePhaseTransition, but base its trigger on synchronized server timing so
useGameInput is gated consistently across clients.
In `@frontend/src/hooks/useGameSocket.ts`:
- Around line 128-140: The cleanup in useGameSocket only resets
gameState-related fields, so gamePhase and countdown from the previous room
persist across unmounts. Update the unmount logic in useEffect to also clear the
state used by GameCountdownOverlay and useGamePhaseTransition, keeping the reset
alongside setGameState, setMyPlayerId, setResultStats, and setErrorMessage so a
fresh session starts from a clean phase/countdown state.
- Around line 16-111: In useGameSocket, the connection/join failure path is
never updating the error UI because setErrorMessage is unused; wire socket
connection/auth/join failure handling into this hook so errors from io() or the
game:join flow set a meaningful errorMessage and switch the game phase to
ended/error state. Use the existing useGameSocket effect and its socket listener
setup/cleanup to add the failure handler, and make sure GameResultOverlay can
receive the ERROR state instead of leaving the screen stalled.
In `@frontend/src/hooks/useInputManager.ts`:
- Around line 24-41: The input state in useInputManager is tracking only
Direction values, which causes duplicate bindings like W and ArrowUp to cancel
each other incorrectly. Update the key handling in
useInputManager/getDirectionFromKey so active presses are tracked by the actual
key code or by per-direction press counts, and only send stop when the last key
for that direction is released. Make sure the pressed/released logic in the
keyboard event handlers uses this new tracking instead of a single
Direction-only set.
In `@frontend/src/hooks/useLobbySocket.ts`:
- Line 6: useLobbySocket の BACKEND_URL フォールバックが同一オリジン前提の Socket.IO
ルーティングを壊しているため、VITE_BACKEND_URL 未設定時は localhost:3000
ではなく現在のオリジンを使うように修正してください。useLobbySocket 内の Socket.IO 接続先決定ロジックで、BACKEND_URL
の既定値をブラウザの同一オリジンに合わせるか、相対パスで /socket.io/ を利用する形に変更し、HTTPS 配信時でも mixed content
にならないようにしてください。
In `@frontend/vite.config.ts`:
- Around line 7-16: The dev server config in Vite is overriding server.host by
setting it twice, which prevents binding beyond localhost. Update the
configuration in vite.config.ts so server.host is set only once to the intended
value for container access, and move the WebSocket-related settings (protocol
and clientPort) under server.ws instead of keeping them at the top level. Use
the existing server config block as the place to separate host handling from the
ws settings.
In `@shared/game-events.types.ts`:
- Around line 89-92: The game:end payload in game-events.types.ts currently uses
rankings as Record<string, PlayerStats>, which loses ordering and leaves result
display dependent on insertion/join order. Update the event contract and the
backend/src/game/logic/end.logic.ts flow so game end results are sent as a
server-sorted ordered list or include an explicit rank field in each entry,
using the existing game:end and room.stats handling as the main points to
change.
- Around line 36-42: `PlayerStats` の shape が shared と backend で重複しているので、shared
側の `PlayerStats` を唯一の定義元にし、backend/src/game/game.types.ts
側は同じ型を再利用するように整理してください。`game:end` payload を組み立てる `GameEndedPayload` や関連する
`game.types` の型参照を、backend 独自定義ではなく shared の `PlayerStats`
に向けて統一し、将来の差分が二重管理にならないようにしてください。
---
Outside diff comments:
In `@frontend/src/pages/GameRoomPage.tsx`:
- Around line 27-50: The fallback in GameRoomPage for an unknown roomId should
not create a synthetic room object anymore, since that lets invalid URLs enter
the game flow. Update the logic around the room lookup and setCurrentRoom usage
so that when rooms.find(...) returns nothing, the page redirects back to the
lobby or renders a not-found state instead of populating a fake local room. Keep
the real room path unchanged for valid room matches.
---
Nitpick comments:
In `@backend/src/game/game.service.spec.ts`:
- Around line 15-17: The current GameService spec only checks that the service
is defined, so it will not catch regressions in the new event flow. Add a
focused test in game.service.spec.ts around GameService that verifies the room
lifecycle emits game:countdown, then game:state, then game:end in that order,
and asserts the rankings payload shape stays consistent. Use the existing
GameService setup/methods to drive start/end behavior so the main path is
covered even if the wiring changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 982c7bc9-08a1-4aff-aef3-6915cc0b485a
⛔ Files ignored due to path filters (2)
backend/package-lock.jsonis excluded by!**/package-lock.jsonpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (60)
Makefilebackend/eslint.config.mjsbackend/package.jsonbackend/src/app.module.tsbackend/src/auth/interfaces/jwt-payload.interface.tsbackend/src/auth/jwt.strategy.tsbackend/src/game/constants/game-constants.tsbackend/src/game/game.gateway.spec.tsbackend/src/game/game.gateway.tsbackend/src/game/game.module.tsbackend/src/game/game.service.spec.tsbackend/src/game/game.service.tsbackend/src/game/game.types.tsbackend/src/game/logic/bomb.logic.tsbackend/src/game/logic/end.logic.tsbackend/src/game/logic/map.logic.tsbackend/src/game/logic/movement.logic.tsbackend/src/game/logic/player.logic.tsbackend/tsconfig.jsondocker/backend/Dockerfiledocker/docker-compose.ymldocker/frontend/Dockerfiledocker/nginx/conf/nginx.conffrontend/package.jsonfrontend/src/App.cssfrontend/src/components/game/BombermanCharacter.tsxfrontend/src/components/game/BombermanEffects.tsxfrontend/src/components/game/BombermanScene.tsxfrontend/src/components/game/GameCanvas.tsxfrontend/src/components/game/GameCountdownOverlay.tsxfrontend/src/components/game/GameResultOverlay.tsxfrontend/src/components/game/TouchControls.tsxfrontend/src/components/game/constants/scene-constants.tsfrontend/src/components/game/preview/BombermanRenderer.tsfrontend/src/components/game/preview/GameMapPreview.tsxfrontend/src/components/game/preview/map-mock.tsfrontend/src/components/game/utils/map-colors.tsfrontend/src/constants/game-constants.tsfrontend/src/game/bomb-explosion.tsfrontend/src/game/bomberman/BombermanGame.tsfrontend/src/game/bomberman/MockBombermanApi.tsfrontend/src/game/bomberman/bombermanMap.tsfrontend/src/game/bomberman/bombermanRules.tsfrontend/src/game/bomberman/bombermanTypes.tsfrontend/src/game/engine/GameLoop.tsfrontend/src/game/engine/InputManager.tsfrontend/src/game/ranking-logic.tsfrontend/src/hooks/useGameInput.tsfrontend/src/hooks/useGamePhaseTransition.tsfrontend/src/hooks/useGameSocket.tsfrontend/src/hooks/useInputManager.tsfrontend/src/hooks/useLobbySocket.tsfrontend/src/pages/GameRoomPage.tsxfrontend/src/pages/WaitingRoom.tsxfrontend/src/stores/gameStore.tsfrontend/src/types/game.tsfrontend/vite.config.tspackage.jsonshared/game-events.types.tsshared/package.json
💤 Files with no reviewable changes (7)
- frontend/src/game/engine/GameLoop.ts
- frontend/src/game/bomberman/BombermanGame.ts
- frontend/src/game/bomberman/bombermanTypes.ts
- frontend/src/game/engine/InputManager.ts
- frontend/src/game/bomberman/MockBombermanApi.ts
- frontend/src/game/bomberman/bombermanRules.ts
- frontend/src/game/bomberman/bombermanMap.ts
|
@CodeRabbit ポエムを書いて |
|
Tip For best results, initiate chat on the files or code changes.
PRの海に |
|
@CodeRabbit あなたは何者?AI? |
|
Tip For best results, initiate chat on the files or code changes.
雑談も少しできます。必要なら、このPRの変更内容も一緒に見ます。 (ᐢᵕᐢ)՞ |
|
@CodeRabbit あなたの使用制限はあるの? |
|
Tip For best results, initiate chat on the files or code changes.
主な制限はこんな感じです。
要するに、「レビュー・調査・整理・提案」は得意で、 必要ならこのPRで「何ができて、何ができないか」をこの変更内容に即して具体例で答えます。 |
|
@CodeRabbit このPRをマージしてOKかな?起票しておくべき課題はある? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Files =="
fd -p 'game.gateway.ts|game.service.ts|useGameSocket.ts|useGamePhaseTransition.ts|game-events.types.ts|app.module.ts|game.module.ts' .
echo
echo "== Outline: backend/src/game/game.gateway.ts =="
ast-grep outline backend/src/game/game.gateway.ts --view expanded || true
echo
echo "== Outline: backend/src/game/game.service.ts =="
ast-grep outline backend/src/game/game.service.ts --view expanded || true
echo
echo "== Outline: frontend/src/hooks/useGameSocket.ts =="
ast-grep outline frontend/src/hooks/useGameSocket.ts --view expanded || true
echo
echo "== Outline: frontend/src/hooks/useGamePhaseTransition.ts =="
ast-grep outline frontend/src/hooks/useGamePhaseTransition.ts --view expanded || true
echo
echo "== backend/src/game/game.gateway.ts =="
sed -n '1,240p' backend/src/game/game.gateway.ts
echo
echo "== backend/src/game/game.service.ts (1/2) =="
sed -n '1,260p' backend/src/game/game.service.ts
echo
echo "== backend/src/game/game.service.ts (2/2) =="
sed -n '261,520p' backend/src/game/game.service.ts
echo
echo "== frontend/src/hooks/useGameSocket.ts =="
sed -n '1,260p' frontend/src/hooks/useGameSocket.ts
echo
echo "== frontend/src/hooks/useGamePhaseTransition.ts =="
sed -n '1,220p' frontend/src/hooks/useGamePhaseTransition.ts
echo
echo "== shared/game-events.types.ts =="
sed -n '1,260p' shared/game-events.types.ts
echo
echo "== backend/src/app.module.ts =="
sed -n '1,220p' backend/src/app.module.ts
echo
echo "== backend/src/game/game.module.ts =="
sed -n '1,220p' backend/src/game/game.module.tsLength of output: 23010 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Files =="
fd -p 'player.logic.ts|end.logic.ts|GameCountdownOverlay.tsx|GameCanvas.tsx|GameRoomPage.tsx|gameStore.ts|bomb-explosion.ts' .
echo
echo "== Outline: backend/src/game/logic/player.logic.ts =="
ast-grep outline backend/src/game/logic/player.logic.ts --view expanded || true
echo
echo "== Outline: backend/src/game/logic/end.logic.ts =="
ast-grep outline backend/src/game/logic/end.logic.ts --view expanded || true
echo
echo "== backend/src/game/logic/player.logic.ts =="
sed -n '1,240p' backend/src/game/logic/player.logic.ts
echo
echo "== backend/src/game/logic/end.logic.ts =="
sed -n '1,220p' backend/src/game/logic/end.logic.ts
echo
echo "== frontend/src/components/game/GameCountdownOverlay.tsx =="
sed -n '1,220p' frontend/src/components/game/GameCountdownOverlay.tsx
echo
echo "== frontend/src/components/game/GameCanvas.tsx =="
sed -n '1,220p' frontend/src/components/game/GameCanvas.tsx
echo
echo "== frontend/src/pages/GameRoomPage.tsx =="
sed -n '1,240p' frontend/src/pages/GameRoomPage.tsx
echo
echo "== frontend/src/stores/gameStore.ts =="
sed -n '1,220p' frontend/src/stores/gameStore.ts
echo
echo "== frontend/src/game/bomb-explosion.ts =="
sed -n '1,220p' frontend/src/game/bomb-explosion.tsLength of output: 14591
先に結論
優先度高: 起票というより blocker 寄り
マージ後に起票してよい follow-up
私ならこう判断します
起票しておくべき課題一覧
必要なら、この5件をそのまま GitHub Issue の文面(ja-JP) にして、起票までやります。 |
|
@CodeRabbit とりあえずマージしちゃうからその5件を一旦起票して |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
repo="cimura/ft_transcendence"
pr_number="82"
pr_url="https://github.com/cimura/ft_transcendence/pull/82"
requester="cimura"
# Find the latest PR conversation comment from the requester that asks to open the five issues.
comment_url=$(
gh api "repos/$repo/issues/$pr_number/comments" --paginate \
--jq '[ .[] | select(.user.login == "'"$requester"'") | select(.body | contains("その5件")) ] | last | .html_url'
)
if [ -z "${comment_url:-}" ] || [ "$comment_url" = "null" ]; then
echo "Failed to find the requester comment URL on PR #$pr_number" >&2
exit 1
fi
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
cat > "$tmpdir/issue1.md" <<EOF
## 概要
対戦ルームが \`countdown\` フェーズの間にプレイヤーが離脱した場合、開始条件を満たさなくなってもカウントダウンが継続し、1人だけでゲームが開始されうる問題を解消する。
## 背景
現在の実装では、2人以上参加した時点でサーバー側がカウントダウンを開始するが、\`countdown\` 中の離脱時にカウントダウン停止・フェーズ巻き戻し・状態再通知が十分に行われていない。
これにより、実際の参加人数とクライアント表示が乖離したり、開始条件を満たさないまま試合が始まる可能性がある。
## 対応方針
- \`countdown\` 中に参加人数が開始条件を下回ったら、カウントダウンを中止する
- 必要に応じてフェーズを \`waiting\` に戻す
- 残っているクライアントへ最新状態を再通知する
- 再度開始条件を満たしたときだけ、新しいカウントダウンを開始する
## 影響箇所
- \`backend/src/game/game.service.ts\`
- \`backend/src/game/logic/player.logic.ts\`
- 必要なら \`shared/game-events.types.ts\` / フロント側のフェーズ表示処理
## 受け入れ条件
- \`countdown\` 中に1人抜けて参加人数が不足した場合、試合は開始しない
- 残存クライアントの表示が waiting 相当に更新される
- 参加人数が再び条件を満たした場合のみ、新しいカウントダウンが始まる
- 同様のケースを再発防止するテストが追加される
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue2.md" <<EOF
## 概要
ゲームのフェーズ遷移(waiting / countdown / playing / ended)を、フロントのローカルタイマーではなくサーバー主導で一貫して管理する。
## 背景
このPRは server-authoritative なゲーム進行への移行が目的だが、現状はフロントエンド側にもフェーズ遷移ロジックが残っている。
そのため、カウントダウン中断・延長・再接続・通信遅延などのケースで、サーバー状態とクライアント状態がズレる余地がある。
## 対応方針
- フェーズの真実のソースをサーバーに統一する
- \`game:init\` / \`game:state\` / 専用イベント等で、クライアントが必要なフェーズ情報を常に受け取れるようにする
- フロントの \`setTimeout\` / \`requestAnimationFrame\` ベースの遷移確定ロジックを最小化、または廃止する
- 中断・再開・再接続時にも整合が取れるイベント設計にする
## 影響箇所
- \`backend/src/game/game.service.ts\`
- \`shared/game-events.types.ts\`
- \`frontend/src/hooks/useGameSocket.ts\`
- \`frontend/src/hooks/useGamePhaseTransition.ts\`
- \`frontend/src/stores/gameStore.ts\`
## 受け入れ条件
- playing への遷移はサーバー通知に基づいてのみ確定する
- countdown の中断・延長が起きてもクライアント表示がサーバーと一致する
- 再接続時に現在フェーズを正しく復元できる
- フロント単独のローカルタイマーでは試合開始を確定しない
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue3.md" <<EOF
## 概要
WebSocket 切断後の reconnect / 再参加フローを実装し、一時的な通信断やリロードで即敗北・即離脱扱いにならないようにする。
## 背景
現状は切断がそのまま離脱として扱われる前提で、再接続猶予やセッション復帰の実装が未完了である。
実運用では一時的なネットワーク断、ブラウザ更新、モバイル回線の瞬断などが起こりうるため、対戦体験への影響が大きい。
## 対応方針
- 切断時に即時退室ではなく、一定の猶予時間を設ける
- 同一ユーザーが復帰した場合に、元のルーム・プレイヤー状態へ再アタッチできるようにする
- 猶予超過時のみ正式離脱として扱う
- クライアント側も reconnect 時の復帰シナリオを考慮する
## 影響箇所
- \`backend/src/game/game.gateway.ts\`
- \`backend/src/game/game.service.ts\`
- \`backend/src/game/constants/game-constants.ts\`
- \`frontend/src/hooks/useGameSocket.ts\`
- 必要なら認証・ルーム参加周辺
## 受け入れ条件
- 一時切断後、猶予時間内の再接続で試合に復帰できる
- 猶予時間を超えた場合のみ離脱・敗北処理が確定する
- 復帰後の game state / phase / player 情報が整合している
- 通信断・再接続ケースのテストまたは検証手順が整備される
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue4.md" <<EOF
## 概要
ゲーム参加時のプレイヤー名を仮値ではなく、実ユーザー情報から解決して結果画面やゲーム内表示に反映する。
## 背景
現在のゲーム参加処理では username が固定の仮値で扱われており、ランキング表示や対戦中の識別に不正確な情報が出る。
プレイヤー識別は UX 上もデバッグ上も重要なため、認証済みユーザー情報とゲームセッションを正しく結び付ける必要がある。
## 対応方針
- JWT またはユーザー取得処理から実 username を解決する
- ゲーム参加時に \`playerId\` と対応する \`username\` を \`GameSession\` に保存する
- 結果画面やランキング表示でその情報を利用する
- 未取得時のフォールバック表示も整理する
## 影響箇所
- \`backend/src/game/game.service.ts\`
- \`backend/src/game/logic/player.logic.ts\`
- 認証/ユーザー取得周辺
- \`frontend/src/components/game/GameResultOverlay.tsx\`
## 受け入れ条件
- 参加プレイヤーが実際の username でゲーム状態に反映される
- 結果画面・ランキングで仮値ではなく実ユーザー名が表示される
- username を取得できない場合のフォールバックが定義される
- 識別子(playerId)と表示名(username)の責務が整理される
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue5.md" <<EOF
## 概要
追加された Game Gateway / Game Service / ゲームロジックの自動テストを拡充し、サーバー主導ゲーム進行の回帰を防ぐ。
## 背景
現状の spec は \`should be defined\` 中心で、重要なシナリオの振る舞いを十分に保証できていない。
今回のPRでは countdown、参加/離脱、爆弾、終了判定、ランキングなどの重要ロジックが追加されているため、最低限のユースケースを自動テスト化したい。
## 対応方針
- Gateway の接続・参加・離脱イベントのテストを追加する
- Service の countdown 開始/中止、playing 中離脱、終了判定を検証する
- 爆弾設置制限、爆発処理、ランキング集計などのロジック単位テストを追加する
- 主要な境界ケース(1人開始防止、同時離脱、復帰前提の準備)も洗い出す
## 影響箇所
- \`backend/src/game/game.gateway.spec.ts\`
- \`backend/src/game/game.service.spec.ts\`
- \`backend/src/game/logic/*.ts\`
- 必要ならテスト用のモック/ヘルパー
## 受け入れ条件
- 少なくとも以下のケースが自動テストで担保される
- 2人参加で countdown 開始
- countdown 中離脱で countdown 中止
- playing 中離脱で surrender 扱い
- 爆弾設置数制限
- 終了判定と rankings の整合
- テストが将来のリファクタで回帰を検知できる粒度になっている
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
url1=$(gh issue create --repo "$repo" --title "countdown中の離脱でゲーム開始条件が崩れる問題を修正する" --body-file "$tmpdir/issue1.md" --assignee "$requester")
url2=$(gh issue create --repo "$repo" --title "ゲームのphase遷移を完全にserver-authoritativeにする" --body-file "$tmpdir/issue2.md" --assignee "$requester")
url3=$(gh issue create --repo "$repo" --title "WebSocketのreconnect / 再参加フローを実装する" --body-file "$tmpdir/issue3.md" --assignee "$requester")
url4=$(gh issue create --repo "$repo" --title "Game join時に実ユーザーのusernameを解決して表示に反映する" --body-file "$tmpdir/issue4.md" --assignee "$requester")
url5=$(gh issue create --repo "$repo" --title "Game Gateway / Service / logic の自動テストを拡充する" --body-file "$tmpdir/issue5.md" --assignee "$requester")
printf "COMMENT_URL=%s\n" "$comment_url"
printf "ISSUE_1=%s\n" "$url1"
printf "ISSUE_2=%s\n" "$url2"
printf "ISSUE_3=%s\n" "$url3"
printf "ISSUE_4=%s\n" "$url4"
printf "ISSUE_5=%s\n" "$url5"Length of output: 6122 🏁 Script executed: #!/bin/bash
set -euo pipefail
repo="cimura/ft_transcendence"
pr_number="82"
pr_url="https://github.com/cimura/ft_transcendence/pull/82"
requester="cimura"
comment_url=$(
gh api "repos/$repo/issues/$pr_number/comments" --paginate \
--jq '[ .[] | select(.user.login == "'"$requester"'") | select(.body | contains("その5件")) ] | last | .html_url'
)
if [ -z "${comment_url:-}" ] || [ "$comment_url" = "null" ]; then
echo "Failed to find the requester comment URL on PR #$pr_number" >&2
exit 1
fi
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
cat > "$tmpdir/issue1.md" <<EOF
## 概要
対戦ルームが \`countdown\` フェーズの間にプレイヤーが離脱した場合、開始条件を満たさなくなってもカウントダウンが継続し、1人だけでゲームが開始されうる問題を解消する。
## 背景
現在の実装では、2人以上参加した時点でサーバー側がカウントダウンを開始するが、\`countdown\` 中の離脱時にカウントダウン停止・フェーズ巻き戻し・状態再通知が十分に行われていない。
これにより、実際の参加人数とクライアント表示が乖離したり、開始条件を満たさないまま試合が始まる可能性がある。
## 対応方針
- \`countdown\` 中に参加人数が開始条件を下回ったら、カウントダウンを中止する
- 必要に応じてフェーズを \`waiting\` に戻す
- 残っているクライアントへ最新状態を再通知する
- 再度開始条件を満たしたときだけ、新しいカウントダウンを開始する
## 影響箇所
- \`backend/src/game/game.service.ts\`
- \`backend/src/game/logic/player.logic.ts\`
- 必要なら \`shared/game-events.types.ts\` / フロント側のフェーズ表示処理
## 受け入れ条件
- \`countdown\` 中に1人抜けて参加人数が不足した場合、試合は開始しない
- 残存クライアントの表示が waiting 相当に更新される
- 参加人数が再び条件を満たした場合のみ、新しいカウントダウンが始まる
- 同様のケースを再発防止するテストが追加される
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue2.md" <<EOF
## 概要
ゲームのフェーズ遷移(waiting / countdown / playing / ended)を、フロントのローカルタイマーではなくサーバー主導で一貫して管理する。
## 背景
このPRは server-authoritative なゲーム進行への移行が目的だが、現状はフロントエンド側にもフェーズ遷移ロジックが残っている。
そのため、カウントダウン中断・延長・再接続・通信遅延などのケースで、サーバー状態とクライアント状態がズレる余地がある。
## 対応方針
- フェーズの真実のソースをサーバーに統一する
- \`game:init\` / \`game:state\` / 専用イベント等で、クライアントが必要なフェーズ情報を常に受け取れるようにする
- フロントの \`setTimeout\` / \`requestAnimationFrame\` ベースの遷移確定ロジックを最小化、または廃止する
- 中断・再開・再接続時にも整合が取れるイベント設計にする
## 影響箇所
- \`backend/src/game/game.service.ts\`
- \`shared/game-events.types.ts\`
- \`frontend/src/hooks/useGameSocket.ts\`
- \`frontend/src/hooks/useGamePhaseTransition.ts\`
- \`frontend/src/stores/gameStore.ts\`
## 受け入れ条件
- playing への遷移はサーバー通知に基づいてのみ確定する
- countdown の中断・延長が起きてもクライアント表示がサーバーと一致する
- 再接続時に現在フェーズを正しく復元できる
- フロント単独のローカルタイマーでは試合開始を確定しない
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue3.md" <<EOF
## 概要
WebSocket 切断後の reconnect / 再参加フローを実装し、一時的な通信断やリロードで即敗北・即離脱扱いにならないようにする。
## 背景
現状は切断がそのまま離脱として扱われる前提で、再接続猶予やセッション復帰の実装が未完了である。
実運用では一時的なネットワーク断、ブラウザ更新、モバイル回線の瞬断などが起こりうるため、対戦体験への影響が大きい。
## 対応方針
- 切断時に即時退室ではなく、一定の猶予時間を設ける
- 同一ユーザーが復帰した場合に、元のルーム・プレイヤー状態へ再アタッチできるようにする
- 猶予超過時のみ正式離脱として扱う
- クライアント側も reconnect 時の復帰シナリオを考慮する
## 影響箇所
- \`backend/src/game/game.gateway.ts\`
- \`backend/src/game/game.service.ts\`
- \`backend/src/game/constants/game-constants.ts\`
- \`frontend/src/hooks/useGameSocket.ts\`
- 必要なら認証・ルーム参加周辺
## 受け入れ条件
- 一時切断後、猶予時間内の再接続で試合に復帰できる
- 猶予時間を超えた場合のみ離脱・敗北処理が確定する
- 復帰後の game state / phase / player 情報が整合している
- 通信断・再接続ケースのテストまたは検証手順が整備される
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue4.md" <<EOF
## 概要
ゲーム参加時のプレイヤー名を仮値ではなく、実ユーザー情報から解決して結果画面やゲーム内表示に反映する。
## 背景
現在のゲーム参加処理では username が固定の仮値で扱われており、ランキング表示や対戦中の識別に不正確な情報が出る。
プレイヤー識別は UX 上もデバッグ上も重要なため、認証済みユーザー情報とゲームセッションを正しく結び付ける必要がある。
## 対応方針
- JWT またはユーザー取得処理から実 username を解決する
- ゲーム参加時に \`playerId\` と対応する \`username\` を \`GameSession\` に保存する
- 結果画面やランキング表示でその情報を利用する
- 未取得時のフォールバック表示も整理する
## 影響箇所
- \`backend/src/game/game.service.ts\`
- \`backend/src/game/logic/player.logic.ts\`
- 認証/ユーザー取得周辺
- \`frontend/src/components/game/GameResultOverlay.tsx\`
## 受け入れ条件
- 参加プレイヤーが実際の username でゲーム状態に反映される
- 結果画面・ランキングで仮値ではなく実ユーザー名が表示される
- username を取得できない場合のフォールバックが定義される
- 識別子(playerId)と表示名(username)の責務が整理される
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
cat > "$tmpdir/issue5.md" <<EOF
## 概要
追加された Game Gateway / Game Service / ゲームロジックの自動テストを拡充し、サーバー主導ゲーム進行の回帰を防ぐ。
## 背景
現状の spec は \`should be defined\` 中心で、重要なシナリオの振る舞いを十分に保証できていない。
今回のPRでは countdown、参加/離脱、爆弾、終了判定、ランキングなどの重要ロジックが追加されているため、最低限のユースケースを自動テスト化したい。
## 対応方針
- Gateway の接続・参加・離脱イベントのテストを追加する
- Service の countdown 開始/中止、playing 中離脱、終了判定を検証する
- 爆弾設置制限、爆発処理、ランキング集計などのロジック単位テストを追加する
- 主要な境界ケース(1人開始防止、同時離脱、復帰前提の準備)も洗い出す
## 影響箇所
- \`backend/src/game/game.gateway.spec.ts\`
- \`backend/src/game/game.service.spec.ts\`
- \`backend/src/game/logic/*.ts\`
- 必要ならテスト用のモック/ヘルパー
## 受け入れ条件
- 少なくとも以下のケースが自動テストで担保される
- 2人参加で countdown 開始
- countdown 中離脱で countdown 中止
- playing 中離脱で surrender 扱い
- 爆弾設置数制限
- 終了判定と rankings の整合
- テストが将来のリファクタで回帰を検知できる粒度になっている
## Backlinks
- PR: $pr_url
- Request comment: $comment_url
- Requested by: @$requester
EOF
url1=$(gh issue create --repo "$repo" --title "countdown中の離脱でゲーム開始条件が崩れる問題を修正する" --body-file "$tmpdir/issue1.md")
url2=$(gh issue create --repo "$repo" --title "ゲームのphase遷移を完全にserver-authoritativeにする" --body-file "$tmpdir/issue2.md")
url3=$(gh issue create --repo "$repo" --title "WebSocketのreconnect / 再参加フローを実装する" --body-file "$tmpdir/issue3.md")
url4=$(gh issue create --repo "$repo" --title "Game join時に実ユーザーのusernameを解決して表示に反映する" --body-file "$tmpdir/issue4.md")
url5=$(gh issue create --repo "$repo" --title "Game Gateway / Service / logic の自動テストを拡充する" --body-file "$tmpdir/issue5.md")
printf "COMMENT_URL=%s\n" "$comment_url"
printf "ISSUE_1=%s\n" "$url1"
printf "ISSUE_2=%s\n" "$url2"
printf "ISSUE_3=%s\n" "$url3"
printf "ISSUE_4=%s\n" "$url4"
printf "ISSUE_5=%s\n" "$url5"Length of output: 6057
補足:
必要なら次に、5件のうち どれをこのPR前に直すべきか も優先順で整理します。 |
概要
フロントエンド単体で動いていたゲームロジックをバックエンド主体で動かし、Server Authoritative な動作を目指しました。
変更内容
新機能
旧仕様の変更
その他
shared/にpackage.jsonを配置、frontend/backendコンテナでshared/の内容を含めてビルドする 1動作確認方法
ブラウザでウィンドウを2つ用意し確認してください。(シークレットモードなどを使用)
やっていないこと
shared/へまとめる(ビルド処理が複雑になるため)※今後別Issueで対応します
その他
シンプルな実装にするために省いた機能がいくつかあります。今後デザインを確立していくときに必要であれば追加していく予定です。
変更差分が大きくなってしまいました🙇
Summary by CodeRabbit
Summary by CodeRabbit
Footnotes
dockerでfrontend/package.jsonを元に
npm installしたいときは以下のように-wオプションを付けてワーキングディレクトリを指定してください。backendも同様です。docker exec -w /app/frontend frontend npm install↩今後ソースを参照するときのために
git tagを追加しました。tag:
archive-local-game-logic↩