Skip to content

Conversation

@Restart2008
Copy link
Contributor

@Restart2008 Restart2008 commented Dec 24, 2025

If this PR fixes an issue, link it below. If not, delete these two lines.
Resolves #2686 Allow Lobby Creator to kick players in game

Description:

Describe the PR.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

image

DISCORD_USERNAME
Restart

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 24, 2025

Walkthrough

Server now includes lobbyCreatorID in game-start payload. Core layers propagate and store it (Schemas, GameRunner, GameView). PlayerInteraction gains optional canKick. Client shows a "Kick Player" action when allowed and emits a kick intent. i18n keys for kick added.

Changes

Cohort / File(s) Summary
Schema & Types
src/core/Schemas.ts, src/core/game/Game.ts
Add optional lobbyCreatorID to GameStartInfoSchema; add optional canKick?: boolean to PlayerInteraction.
Server start payload
src/server/GameServer.ts
Include lobbyCreatorID in the GameStartInfo sent on start and validated by schema.
GameRunner & creation
src/core/GameRunner.ts, src/client/ClientGameRunner.ts
Pass, store, and forward lobbyCreatorID in GameRunner constructor; client creation supplies lobbyCreatorID.
Game view
src/core/game/GameView.ts
Store _lobbyCreatorID, accept it in constructor, and add isLobbyCreator(player: PlayerView): boolean.
Client UI & i18n
src/client/graphics/layers/PlayerPanel.ts, resources/lang/en.json
PlayerPanel reads canKick and renders a Kick action that emits a kick intent; added "kick" and "kick_confirm" translation keys.

Sequence Diagram(s)

sequenceDiagram
  rect rgb(245,250,255)
    participant ClientUI as Client UI
    participant ClientRunner as ClientGameRunner
    participant GameRunner as Server/GameRunner
    participant GameView as GameView
    participant Server as GameServer
  end

  Note over Server,GameRunner: Game start with lobbyCreatorID
  Server->>GameRunner: start(gameStart{..., lobbyCreatorID})
  GameRunner->>GameView: new(game, players, lobbyCreatorID)
  GameView->>GameView: mark lobbyCreator (compare IDs)
  GameRunner->>ClientRunner: push GameUpdate (includes per-player canKick)
  ClientRunner->>ClientUI: forward update
  ClientUI->>ClientUI: render PlayerPanel (show Kick if canKick)
  ClientUI->>Server: SendKickPlayerIntent(targetPlayerID)
  Server->>GameRunner: validate kick -> apply -> push updated GameUpdate
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

Feature - Frontend, Backend, UI/UX

Poem

👑 The lobby maker holds the key,
A tiny button sets players free,
From start to view the flag is found,
A click, an intent—state turns round. ✨

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Lobby Creators can kick players in game' accurately and concisely describes the main feature implementation in the changeset.
Description check ✅ Passed The description clearly relates to the changeset, referencing issue #2686, explaining the feature implementation, UI additions, and confirming standard development practices.
Linked Issues check ✅ Passed The changes fully satisfy issue #2686 requirements by implementing the kick capability when isLobbyCreator() is true and displaying a kick button in the player panel.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the lobby creator kick feature, with no extraneous modifications beyond the linked issue requirements.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83eb1e0 and 36d0684.

📒 Files selected for processing (7)
  • resources/lang/en.json
  • src/client/ClientGameRunner.ts
  • src/core/GameRunner.ts
  • src/core/Schemas.ts
  • src/core/game/Game.ts
  • src/core/game/GameView.ts
  • src/server/GameServer.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/core/Schemas.ts
  • src/core/game/Game.ts
  • src/client/ClientGameRunner.ts
  • src/server/GameServer.ts
  • src/core/game/GameView.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-02T14:27:37.609Z
Learnt from: andrewNiziolek
Repo: openfrontio/OpenFrontIO PR: 1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.

Applied to files:

  • src/core/GameRunner.ts
🧬 Code graph analysis (1)
src/core/GameRunner.ts (1)
src/core/Schemas.ts (1)
  • ClientID (26-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (3)
resources/lang/en.json (1)

697-699: LGTM! Clean translation additions.

The new kick-related translation keys are well-structured and follow existing patterns. The placeholder syntax for the confirmation message is consistent with other entries in the codebase.

src/core/GameRunner.ts (2)

81-81: Clean threading of lobby creator information.

The lobbyCreatorID is properly passed from GameStartInfo to the GameRunner constructor and stored as a private field. The optional type handling (ClientID | undefined) correctly accounts for games without a lobby creator.

Also applies to: 98-98


213-215: Solid kick permission logic with self-kick prevention.

The implementation correctly restricts kicking to the lobby creator while preventing them from kicking themselves. When lobbyCreatorID is undefined, the first condition evaluates to false, which properly disables kicking for games without a lobby creator.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Duwibi
Duwibi previously approved these changes Dec 24, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/GameServer.ts (1)

318-348: Critical: Validate that requester is the claimed lobby creator.

The creatorClientID from the API request is only validated for format using ID.safeParse() at Worker.ts:116, but there is no authorization check that the client making the request is actually that creator. Any client can send creatorClientID=<victim_id> and successfully create a game with the victim as creator.

The endpoint needs to verify the requester's identity matches creatorClientID before accepting it. Use the verifyClientToken function (already imported) or similar JWT validation to ensure the request's authenticated user equals the claimed creator.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28e22c9 and 17231ff.

📒 Files selected for processing (8)
  • resources/lang/en.json
  • src/client/ClientGameRunner.ts
  • src/client/graphics/layers/PlayerPanel.ts
  • src/core/GameRunner.ts
  • src/core/Schemas.ts
  • src/core/game/Game.ts
  • src/core/game/GameView.ts
  • src/server/GameServer.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-06-02T14:27:37.609Z
Learnt from: andrewNiziolek
Repo: openfrontio/OpenFrontIO PR: 1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.

Applied to files:

  • src/core/GameRunner.ts
  • src/client/ClientGameRunner.ts
  • src/core/game/GameView.ts
📚 Learning: 2025-08-12T00:31:50.144Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 1752
File: src/core/game/Game.ts:750-752
Timestamp: 2025-08-12T00:31:50.144Z
Learning: In the OpenFrontIO codebase, changes to the PlayerInteraction interface (like adding canDonateGold and canDonateTroops flags) do not require corresponding updates to src/core/Schemas.ts or server serialization code.

Applied to files:

  • src/server/GameServer.ts
  • src/core/Schemas.ts
  • src/core/game/Game.ts
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/ClientGameRunner.ts
  • src/core/game/GameView.ts
  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-21T20:06:04.823Z
Learnt from: Saphereye
Repo: openfrontio/OpenFrontIO PR: 2233
File: src/client/HostLobbyModal.ts:891-891
Timestamp: 2025-10-21T20:06:04.823Z
Learning: For the HumansVsNations game mode in `src/client/HostLobbyModal.ts` and related files, the implementation strategy is to generate all nations and adjust their strength for balancing, rather than limiting lobby size based on the number of available nations on the map.

Applied to files:

  • src/client/ClientGameRunner.ts
  • src/core/game/GameView.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/core/game/GameView.ts
🧬 Code graph analysis (3)
src/core/GameRunner.ts (1)
src/core/Schemas.ts (1)
  • ClientID (26-26)
src/core/game/GameView.ts (1)
src/core/Schemas.ts (1)
  • ClientID (26-26)
src/client/graphics/layers/PlayerPanel.ts (4)
src/core/game/GameView.ts (1)
  • PlayerView (185-578)
src/client/Transport.ts (1)
  • SendKickPlayerIntentEvent (173-175)
src/client/components/ui/ActionButton.ts (1)
  • actionButton (43-69)
src/client/Utils.ts (1)
  • translateText (92-147)
🔇 Additional comments (6)
src/core/game/Game.ts (1)

799-799: LGTM - Clean addition following established patterns.

The optional canKick field follows the same pattern as existing flags like canDonateGold and canDonateTroops. Based on learnings, PlayerInteraction changes don't require schema updates.

src/client/ClientGameRunner.ts (1)

194-194: LGTM - Safe parameter forwarding.

The lobbyCreatorID is safely accessed after the gameStartInfo undefined check at line 163. The parameter is correctly passed to the updated GameView constructor.

resources/lang/en.json (1)

697-697: LGTM - Translation key follows conventions.

The "kick" translation key is clearly named and properly placed within the player_panel section. The value "Kick Player" is concise and actionable.

src/core/game/GameView.ts (1)

620-622: LGTM - Simple identity check.

The isLobbyCreator method performs a straightforward equality check. Client-side validation is intentionally minimal here, which is appropriate since the server is responsible for validating kick permissions (as seen in GameServer.ts lines 320-328).

src/core/GameRunner.ts (1)

212-214: Logic is sound but depends on server validation.

The canKick logic correctly:

  • Checks if the querying player is the lobby creator
  • Prevents self-kick with player.clientID() !== other.clientID()
  • Handles null clientID safely (bots/nations can't kick and can't be kicked)

However, this depends on lobbyCreatorID being securely validated server-side. See my comment on GameServer.ts regarding verification of lobby creator authentication.

src/server/GameServer.ts (1)

465-465: Schema validation correctly handles undefined lobbyCreatorID.

The GameStartInfoSchema defines lobbyCreatorID: ID.optional() and the code properly passes the optional field using safeParse() with error handling. Validation will succeed when lobbyCreatorID is undefined (public games), and the TypeScript type system correctly infers it as an optional property.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/client/graphics/layers/PlayerPanel.ts (1)

5-5: Consider a more intuitive icon for the kick action.

The disabledIcon name doesn't semantically match the kick action. Users might expect a boot, exit, or ban icon instead. While functional, a more descriptive icon would improve clarity.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17231ff and 5c0f5ab.

📒 Files selected for processing (1)
  • src/client/graphics/layers/PlayerPanel.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: For the window close confirmation feature in `ClientGameRunner.ts`, the troop count requirement (>10,000 troops) from issue #2137 was intentionally removed because it was arbitrary and troop count can be reported as low despite having significant land. The confirmation now triggers for any alive player regardless of troop count.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-07-30T19:46:23.797Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 1436
File: src/server/Worker.ts:93-93
Timestamp: 2025-07-30T19:46:23.797Z
Learning: Input validation at API boundaries is critical for security. All client-provided data, especially parameters used for authorization decisions like creatorClientID in the kick player functionality, must be validated for type, format, length, and business logic constraints before processing.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-27T09:47:26.395Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:770-795
Timestamp: 2025-10-27T09:47:26.395Z
Learning: In src/core/execution/FakeHumanExecution.ts, the selectSteamrollStopTarget() method intentionally allows MIRV targeting when secondHighest city count is 0 (e.g., nuclear endgame scenarios where structures are destroyed). This is valid game design—"if you can afford it, you're good to go"—and should not be flagged as requiring a guard condition.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
🧬 Code graph analysis (1)
src/client/graphics/layers/PlayerPanel.ts (3)
src/core/game/GameView.ts (1)
  • PlayerView (185-578)
src/client/Transport.ts (1)
  • SendKickPlayerIntentEvent (173-175)
src/client/components/ui/ActionButton.ts (1)
  • actionButton (43-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (3)
src/client/graphics/layers/PlayerPanel.ts (3)

279-288: LGTM! Defensive null check properly added.

The method now includes a defensive guard against null clientID, addressing the previous concern about potential runtime errors with bot/nation players. The implementation follows the established pattern of other action handlers.


634-634: LGTM! Consistent permission check pattern.

The canKick flag extraction follows the same pattern as other permission flags in this method, using optional chaining for safe access.


735-744: The translation key player_panel.kick already exists in en.json (line 697) with the value "Kick Player". The code correctly references this existing translation.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 24, 2025
@Restart2008 Restart2008 marked this pull request as ready for review December 24, 2025 22:26
@Restart2008 Restart2008 requested a review from a team as a code owner December 24, 2025 22:26
@iiamlewis
Copy link
Contributor

@Restart2008 how does this affect the allow all trade button if you cancel all trade? Are you able to resume trading if you cancel it with everyone?

@Restart2008
Copy link
Contributor Author

@Restart2008 how does this affect the allow all trade button if you cancel all trade? Are you able to resume trading if you cancel it with everyone?

Screenshot 2025-12-24 at 4 51 19 PM Screenshot 2025-12-24 at 4 52 55 PM

From what I saw during testing, the kick button shouldn't be affecting the allow all trade button at all. I think that was what you were asking? I added some updated screenshots.

@abdallahbahrawi1
Copy link
Contributor

What if the lobby creator accidentally clicks that button?

@abdallahbahrawi1
Copy link
Contributor

@Restart2008 how does this affect the allow all trade button if you cancel all trade? Are you able to resume trading if you cancel it with everyone?

Screenshot 2025-12-24 at 4 51 19 PM Screenshot 2025-12-24 at 4 52 55 PM

From what I saw during testing, the kick button shouldn't be affecting the allow all trade button at all. I think that was what you were asking? I added some updated screenshots.

If you open your player panel, you should see two buttons: “Stop trading with all” and “Start trading with all.” Your screenshot shows something different.

That said, can the lobby creator kick themselves?

@YoussfeCantCode
Copy link
Contributor

FYI, I have another PR #2657 where we also need to check if the player is the lobby creator during the game

@Restart2008
Copy link
Contributor Author

What if the lobby creator accidentally clicks that button?

Thats a good point, maybe a confirm kick popup-button appears to make sure that the lobby creator doesn't accidentally click that button?

That said, can the lobby creator kick themselves?

I actually didn't test that yesterday so I have no clue, I'll try testing it today. If I do see that lobby creator's can kick themselves then I'll add a fix for that.

@Restart2008
Copy link
Contributor Author

If you open your player panel, you should see two buttons: “Stop trading with all” and “Start trading with all.” Your screenshot shows something different.

In the screenshot it shows the player panel for the other player (Anon 712) while I'm Anon 339. I remember from yesterday that the "stop trade with all" and "start trade with all" buttons for my own player panel are still present alongside the kick player button. I'll recheck this again today when I test it with another player.

@Restart2008
Copy link
Contributor Author

Restart2008 commented Dec 25, 2025

FYI, I have another PR #2657 where we also need to check if the player is the lobby creator during the game

Okie dokie. Evan has told me to wait till PR #2657 is merged in case there's merge conflicts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/client/graphics/layers/PlayerPanel.ts (1)

735-744: Request confirmation: Is the accidental-kick prevention popup planned?

The kick button renders correctly and the logic properly prevents kicking the lobby creator (including self-kick). However, in the PR discussion you mentioned adding a confirmation popup to prevent accidental clicks. This safety measure isn't present in the current code—the kick executes immediately when clicked.

Is the confirmation dialog:

  • Planned for a follow-up commit/PR?
  • Deferred for later?
  • No longer needed?

For destructive actions like kicking, a confirmation step is a recommended UX pattern.


Optional: Icon naming clarity

Minor point: disabledIcon as the kick button icon might confuse readers. Consider renaming to something like kickIcon or removePlayerIcon for clearer intent, or document why "disabled" represents kicking.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c0f5ab and cf0e53c.

📒 Files selected for processing (1)
  • src/client/graphics/layers/PlayerPanel.ts
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: For the window close confirmation feature in `ClientGameRunner.ts`, the troop count requirement (>10,000 troops) from issue #2137 was intentionally removed because it was arbitrary and troop count can be reported as low despite having significant land. The confirmation now triggers for any alive player regardless of troop count.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-07-30T19:46:23.797Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 1436
File: src/server/Worker.ts:93-93
Timestamp: 2025-07-30T19:46:23.797Z
Learning: Input validation at API boundaries is critical for security. All client-provided data, especially parameters used for authorization decisions like creatorClientID in the kick player functionality, must be validated for type, format, length, and business logic constraints before processing.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-27T09:47:26.395Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:770-795
Timestamp: 2025-10-27T09:47:26.395Z
Learning: In src/core/execution/FakeHumanExecution.ts, the selectSteamrollStopTarget() method intentionally allows MIRV targeting when secondHighest city count is 0 (e.g., nuclear endgame scenarios where structures are destroyed). This is valid game design—"if you can afford it, you're good to go"—and should not be flagged as requiring a guard condition.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
🧬 Code graph analysis (1)
src/client/graphics/layers/PlayerPanel.ts (2)
src/client/Transport.ts (1)
  • SendKickPlayerIntentEvent (173-175)
src/client/components/ui/ActionButton.ts (1)
  • actionButton (43-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (1)
src/client/graphics/layers/PlayerPanel.ts (1)

279-288: Defensive guard implemented correctly.

The null check on targetClientID properly prevents crashes when the target player lacks a clientID (bots and nations). Clean fix that addresses the previous review concern.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 25, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/client/graphics/layers/PlayerPanel.ts (1)

742-751: Kick button logic looks correct.

The condition canKick && !this.g.isLobbyCreator(other) correctly prevents:

  1. Non-lobby-creators from seeing the kick button (since canKick would be false)
  2. The lobby creator from kicking themselves (since isLobbyCreator(other) returns true when viewing their own panel)

This addresses the self-kick concern raised in PR comments.

One minor note: using disabledIcon for a "Kick" action is a bit semantically odd. If a more fitting icon becomes available (like a boot or door icon), consider swapping it later.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf0e53c and 83eb1e0.

📒 Files selected for processing (2)
  • resources/lang/en.json
  • src/client/graphics/layers/PlayerPanel.ts
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-06-02T14:27:37.609Z
Learnt from: andrewNiziolek
Repo: openfrontio/OpenFrontIO PR: 1007
File: resources/lang/de.json:115-115
Timestamp: 2025-06-02T14:27:37.609Z
Learning: For OpenFrontIO project: When localization keys are renamed in language JSON files, the maintainers separate technical changes from translation content updates. They wait for community translators to update the actual translation values rather than attempting to translate in the same PR. This allows technical changes to proceed while ensuring accurate translations from native speakers.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-06-09T02:20:43.637Z
Learnt from: VariableVince
Repo: openfrontio/OpenFrontIO PR: 1110
File: src/client/Main.ts:293-295
Timestamp: 2025-06-09T02:20:43.637Z
Learning: In src/client/Main.ts, during game start in the handleJoinLobby callback, UI elements are hidden using direct DOM manipulation with classList.add("hidden") for consistency. This includes modals, buttons, and error divs. The codebase follows this pattern rather than using component APIs for hiding elements during game transitions.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: In `ClientGameRunner.ts`, the `myPlayer` field is always set when `shouldPreventWindowClose()` is called, so the null check in that method is sufficient without needing to fetch it again from `gameView.playerByClientID()`.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-12-13T14:58:29.645Z
Learnt from: scamiv
Repo: openfrontio/OpenFrontIO PR: 2607
File: src/core/execution/PlayerExecution.ts:271-295
Timestamp: 2025-12-13T14:58:29.645Z
Learning: In src/core/execution/PlayerExecution.ts surroundedBySamePlayer(), the `as Player` cast on `mg.playerBySmallID(scan.enemyId)` is intentional. Since scan.enemyId comes from ownerID() on an owned tile and playerBySmallID() only returns Player or undefined, the cast expresses a known invariant. The maintainers prefer loud failures (runtime errors) over silent masking (early returns with guards) for corrupted game state scenarios at trusted call sites.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-08T17:14:49.369Z
Learnt from: Foorack
Repo: openfrontio/OpenFrontIO PR: 2141
File: src/client/ClientGameRunner.ts:228-234
Timestamp: 2025-10-08T17:14:49.369Z
Learning: For the window close confirmation feature in `ClientGameRunner.ts`, the troop count requirement (>10,000 troops) from issue #2137 was intentionally removed because it was arbitrary and troop count can be reported as low despite having significant land. The confirmation now triggers for any alive player regardless of troop count.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-07-30T19:46:23.797Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 1436
File: src/server/Worker.ts:93-93
Timestamp: 2025-07-30T19:46:23.797Z
Learning: Input validation at API boundaries is critical for security. All client-provided data, especially parameters used for authorization decisions like creatorClientID in the kick player functionality, must be validated for type, format, length, and business logic constraints before processing.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
📚 Learning: 2025-10-27T09:47:26.395Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:770-795
Timestamp: 2025-10-27T09:47:26.395Z
Learning: In src/core/execution/FakeHumanExecution.ts, the selectSteamrollStopTarget() method intentionally allows MIRV targeting when secondHighest city count is 0 (e.g., nuclear endgame scenarios where structures are destroyed). This is valid game design—"if you can afford it, you're good to go"—and should not be flagged as requiring a guard condition.

Applied to files:

  • src/client/graphics/layers/PlayerPanel.ts
🧬 Code graph analysis (1)
src/client/graphics/layers/PlayerPanel.ts (3)
src/client/Utils.ts (1)
  • translateText (92-147)
src/client/Transport.ts (1)
  • SendKickPlayerIntentEvent (173-175)
src/client/components/ui/ActionButton.ts (1)
  • actionButton (43-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (3)
src/client/graphics/layers/PlayerPanel.ts (2)

279-295: Well done on the defensive null check.

The past review concern about other.clientID()! causing runtime errors for bots/nations is now properly addressed. The confirmation dialog with translated text and the early return for null clientID make this robust.


5-5: Supporting imports and variable extraction look good.

The new imports for disabledIcon and SendKickPlayerIntentEvent, along with the canKick extraction from this.actions?.interaction, cleanly integrate with the existing code structure.

Also applies to: 35-35, 641-641

resources/lang/en.json (1)

696-698: Translation keys properly added.

The kick and kick_confirm keys are correctly placed under player_panel namespace and use proper ICU placeholder syntax ({name}) that matches the translateText call in PlayerPanel.ts. Clear and simple wording.

@Restart2008
Copy link
Contributor Author

@abdallahbahrawi1 I got some new updated screenshots. 1st screenshot shows the kick confirmation. 2nd screenshot shows the hosts player info panel. In that you see no kick player button to prevent lobby hosts from kicking themselves out of the game lol but the trading buttons are present.
Screenshot 2025-12-25 at 10 00 53 AM
Screenshot 2025-12-25 at 10 01 33 AM

canEmbargo: !player.hasEmbargoAgainst(other),
canKick:
this.lobbyCreatorID === player.clientID() &&
player.clientID() !== other.clientID(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shhould also add a chec kthat other.playerType() == PlayerType.Human

lobbyCreatedAt: z.number(),
config: GameConfigSchema,
players: PlayerSchema.array(),
lobbyCreatorID: ID.optional(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be removed, PlayerScheam already has isLobbyCreator field

cosmetics: c.cosmetics,
isLobbyCreator: this.lobbyCreatorID === c.clientID,
})),
lobbyCreatorID: this.lobbyCreatorID,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

canDonateTroops: player.canDonateTroops(other),
canEmbargo: !player.hasEmbargoAgainst(other),
canKick:
this.lobbyCreatorID === player.clientID() &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do player.isLobbyCreator() instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

Allow Lobby Creator to kick players in game

7 participants