Skip to content

Conversation

@abdallahbahrawi1
Copy link
Contributor

@abdallahbahrawi1 abdallahbahrawi1 commented Dec 21, 2025

Description:

Added a "Player limit" setting for private lobbies that allows the host to set a maximum number of players (2-1000) who can join.

Features:

  • New "Player limit" checkbox + number input in host lobby settings
  • Displays player count as "X/Y Players" when a limit is set
  • Server rejects new joins when lobby is full with a clear error message
  • Warning shown to host if limit is set below current player count (existing players are not kicked)

Screenshots

limit 1 limit 2 limit 3

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:

abodcraft1

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2025

Walkthrough

Adds host-configurable per-lobby player limits (2–1000) persisted to game config, server enforcement with a localized "lobby full" error (including args), client UI updates to show limits and warnings, a reusable CheckboxWithInput component, and schema updates for maxPlayers and server error payloads.

Changes

Cohort / File(s) Summary
Localization
resources/lang/en.json, resources/lang/debug.json
Added keys: private_lobby.lobby_full, host_modal.player_limit, host_modal.player_limit_warning.
Host lobby UI
src/client/HostLobbyModal.ts
Added playerLimit/playerLimitWarning, MIN/MAX (2–1000), replaced legacy timer input with CheckboxWithInput, added Player Limit checkbox+numeric input, persisted maxPlayers to game config, updated header to display current/limit, and added updatePlayerLimitWarning().
Join lobby UI
src/client/JoinPrivateLobbyModal.ts
Added maxPlayers state; poll updates from gameConfig?.maxPlayers; UI shows players as X/Y when set; resets on close/leave.
Client error handling
src/client/ClientGameRunner.ts
Unified error rendering to prefer translationKey+args with fallback to raw message; full-lobby error shows localized modal then dispatches leave-lobby.
Shared component
src/client/components/CheckboxWithInput.ts
New @customelement("checkbox-with-input") component (checkbox + numeric input), input sanitization, clamping, and events checkbox-change / value-change.
Schema & types
src/core/Schemas.ts
GameConfigSchema.maxPlayers tightened to int().min(2).max(1000).optional(); ServerErrorSchema adds optional translationKey and args.
Server logic
src/server/GameServer.ts
updateGameConfig applies maxPlayers; joinClient returns full-lobby error with translationKey: "private_lobby.lobby_full" and args: { limit: maxPlayers }.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Host
    participant HostUI as HostLobbyModal (Client)
    participant Server as GameServer
    participant Joiner as ClientGameRunner
    participant JoinUI as JoinPrivateLobbyModal (Client)

    Host->>HostUI: enable player limit + set value (e.g. 6)
    HostUI->>Server: updateGameConfig { maxPlayers: 6 }
    Server-->>HostUI: ack

    Joiner->>Server: joinLobby request
    alt slots available (current < max)
        Server->>Joiner: join success
        JoinUI->>JoinUI: show players count (X/6)
    else full (current >= max)
        Server-->>Joiner: error { translationKey: "private_lobby.lobby_full", args: { limit: 6 }, message }
        Note right of Joiner #: `#f3f7ff`: Client prefers translationKey/args\nfalls back to raw message
        Joiner->>Joiner: show localized "lobby full (limit: 6)" modal
        Joiner->>Server: dispatch leave-lobby
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Feature - Frontend, Backend, UI/UX, Translation

Suggested reviewers

  • evanpelle
  • scottanderson

Poem

A host sets a cap, a gentle sign,
Seats counted neat: "six of six" align.
When doors are closed the message rings,
Translated words and helpful things. 🎮

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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature being added: a player limit capability for private lobbies, which aligns with all the changes across localization, UI components, and server logic.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about the player limit feature, its implementation across host and join modals, server-side enforcement, and warning logic.

📜 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 c84b2bc and a161027.

📒 Files selected for processing (1)
  • src/client/HostLobbyModal.ts
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
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.
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2689
File: src/client/PublicLobby.ts:245-245
Timestamp: 2025-12-26T22:21:21.904Z
Learning: In public lobbies with HumansVsNations mode in src/client/PublicLobby.ts, maxPlayers represents only human player slots (already halved in DefaultConfig.ts). The nation NPCs are added automatically server-side and don't count toward maxPlayers. Therefore, getTeamSize correctly returns maxPlayers directly for HumansVsNations to display the proper team size (e.g., maxPlayers=5 yields "5 Humans vs 5 Nations").
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.
📚 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/HostLobbyModal.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/HostLobbyModal.ts
📚 Learning: 2026-01-02T18:11:06.832Z
Learnt from: ryanbarlow97
Repo: openfrontio/OpenFrontIO PR: 2740
File: src/client/HostLobbyModal.ts:821-821
Timestamp: 2026-01-02T18:11:06.832Z
Learning: In src/client/HostLobbyModal.ts, the ?s=xxxxx URL suffix used in lobby URLs is purely for cache-busting platform previews (e.g., Discord, WhatsApp, x.com) and is not used by the join logic. The join flow ignores the suffix value, so regenerating it via updateUrlWithSuffix() on configuration changes will not break existing shared URLs; it only prompts platforms to refresh preview metadata. Treat the suffix as non-functional for join behavior and ensure any related changes preserve that invariant.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-12-26T22:21:21.904Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2689
File: src/client/PublicLobby.ts:245-245
Timestamp: 2025-12-26T22:21:21.904Z
Learning: In public lobbies with HumansVsNations mode in src/client/PublicLobby.ts, maxPlayers represents only human player slots (already halved in DefaultConfig.ts). The nation NPCs are added automatically server-side and don't count toward maxPlayers. Therefore, getTeamSize correctly returns maxPlayers directly for HumansVsNations to display the proper team size (e.g., maxPlayers=5 yields "5 Humans vs 5 Nations").

Applied to files:

  • src/client/HostLobbyModal.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/HostLobbyModal.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/client/HostLobbyModal.ts
📚 Learning: 2025-08-17T20:48:49.411Z
Learnt from: woodydrn
Repo: openfrontio/OpenFrontIO PR: 1836
File: src/client/Main.ts:482-482
Timestamp: 2025-08-17T20:48:49.411Z
Learning: In PR #1836, user woodydrn prefers to keep changes minimal and focused on the specific issue (clientID persistence) rather than refactoring redundant code in JoinLobbyEvent dispatchers. They want to avoid scope creep in focused bug fix PRs.

Applied to files:

  • src/client/HostLobbyModal.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/HostLobbyModal.ts
📚 Learning: 2025-10-20T20:15:28.858Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:51-51
Timestamp: 2025-10-20T20:15:28.858Z
Learning: In src/core/execution/FakeHumanExecution.ts, game balance constants like MIRV_COOLDOWN_TICKS, MIRV_HESITATION_ODDS, VICTORY_DENIAL_TEAM_THRESHOLD, VICTORY_DENIAL_INDIVIDUAL_THRESHOLD, and STEAMROLL_CITY_GAP_MULTIPLIER are experimental tuning parameters subject to frequent change during balance testing. Do not flag changes to these values as issues or compare them against previous values.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (2)
src/client/Utils.ts (1)
  • translateText (92-147)
src/client/LangSelector.ts (1)
  • translateText (258-278)
🔇 Additional comments (9)
src/client/HostLobbyModal.ts (9)

26-26: LGTM!

Clean import for the reusable CheckboxWithInput component.


66-70: LGTM!

Clean state design: null for unlimited is idiomatic, and the min/max constants (2–1000) provide sensible guardrails.


497-518: LGTM!

The CheckboxWithInput refactor is clean, and the checkbox-change handler now correctly assigns the default value when enabled (line 508), which addresses the previous review feedback.


521-543: LGTM!

Consistent implementation: the pattern mirrors the max-timer block, correctly toggles between a numeric limit and null (unlimited), and keeps the warning in sync.


544-553: LGTM!

Clean conditional warning display with proper translation key usage.


579-579: LGTM!

The display correctly shows the limit only when set and applies to human player count, not NPCs. This aligns with the codebase pattern where maxPlayers represents human slots. Based on learnings, in HumansVsNations mode, NPCs are added server-side and don't count toward the limit.


682-685: LGTM!

Clear and correct: the short-circuit check prevents null comparison, and the logic correctly identifies when current players exceed the set limit.


803-803: LGTM!

Proper use of ?? to convert null (unlimited) to undefined (omitted) in the config payload.


871-871: LGTM!

Correctly refreshes the warning state after polling player updates, ensuring the UI stays in sync when players join or leave.


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.

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

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3de3a09 and e056006.

📒 Files selected for processing (7)
  • resources/lang/debug.json (2 hunks)
  • resources/lang/en.json (2 hunks)
  • src/client/ClientGameRunner.ts (1 hunks)
  • src/client/HostLobbyModal.ts (6 hunks)
  • src/client/JoinPrivateLobbyModal.ts (4 hunks)
  • src/core/Schemas.ts (1 hunks)
  • src/server/GameServer.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (12)
📚 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/core/Schemas.ts
  • src/server/GameServer.ts
📚 Learning: 2025-05-21T04:10:33.435Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 784
File: src/core/game/StatsImpl.ts:34-38
Timestamp: 2025-05-21T04:10:33.435Z
Learning: In the codebase, PlayerStats is defined as `z.infer<typeof PlayerStatsSchema>` where PlayerStatsSchema has `.optional()` applied at the object level, making PlayerStats a union type that already includes undefined (PlayerStats | undefined).

Applied to files:

  • src/core/Schemas.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/core/Schemas.ts
  • src/client/ClientGameRunner.ts
  • src/server/GameServer.ts
  • src/client/HostLobbyModal.ts
📚 Learning: 2025-05-21T04:10:33.435Z
Learnt from: scottanderson
Repo: openfrontio/OpenFrontIO PR: 784
File: src/core/game/StatsImpl.ts:34-38
Timestamp: 2025-05-21T04:10:33.435Z
Learning: In the codebase, PlayerStats is defined as a type inferred from a Zod schema that is marked as optional, which means PlayerStats already includes undefined as a possible type (PlayerStats | undefined).

Applied to files:

  • src/core/Schemas.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/JoinPrivateLobbyModal.ts
  • src/client/ClientGameRunner.ts
  • src/client/HostLobbyModal.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/JoinPrivateLobbyModal.ts
  • src/client/ClientGameRunner.ts
  • src/server/GameServer.ts
  • src/client/HostLobbyModal.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/ClientGameRunner.ts
  • src/server/GameServer.ts
📚 Learning: 2025-10-26T15:37:07.732Z
Learnt from: GlacialDrift
Repo: openfrontio/OpenFrontIO PR: 2298
File: src/client/graphics/layers/TerritoryLayer.ts:200-210
Timestamp: 2025-10-26T15:37:07.732Z
Learning: In GameImpl.ts lines 124-139, team assignment logic varies by number of teams: when numPlayerTeams < 8, teams are assigned ColoredTeams values (Red, Blue, Yellow, Green, Purple, Orange, Teal); when numPlayerTeams >= 8, teams are assigned generic string identifiers like "Team 1", "Team 2", etc., which are not members of ColoredTeams.

Applied to files:

  • src/server/GameServer.ts
📚 Learning: 2025-10-20T20:15:28.858Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:51-51
Timestamp: 2025-10-20T20:15:28.858Z
Learning: In src/core/execution/FakeHumanExecution.ts, game balance constants like MIRV_COOLDOWN_TICKS, MIRV_HESITATION_ODDS, VICTORY_DENIAL_TEAM_THRESHOLD, VICTORY_DENIAL_INDIVIDUAL_THRESHOLD, and STEAMROLL_CITY_GAP_MULTIPLIER are experimental tuning parameters subject to frequent change during balance testing. Do not flag changes to these values as issues or compare them against previous values.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 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-08-16T10:52:08.292Z
Learnt from: TheGiraffe3
Repo: openfrontio/OpenFrontIO PR: 884
File: resources/lang/en.json:456-461
Timestamp: 2025-08-16T10:52:08.292Z
Learning: In OpenFrontIO, translation files in resources/lang/*.json (except en.json) should not be updated in regular PRs. Only dedicated translation PRs titled "mls" and made by Aotumori should update non-English locale files. Regular PRs should only update en.json when adding or modifying translation keys.

Applied to files:

  • resources/lang/en.json
📚 Learning: 2025-07-12T08:41:35.101Z
Learnt from: Aotumuri
Repo: openfrontio/OpenFrontIO PR: 1357
File: resources/lang/de.json:523-540
Timestamp: 2025-07-12T08:41:35.101Z
Learning: In OpenFrontIO project localization files, always check the en.json source file before flagging potential spelling errors in other language files, as some keys may intentionally use non-standard spellings that need to be consistent across all translations.

Applied to files:

  • resources/lang/en.json
🧬 Code graph analysis (2)
src/client/ClientGameRunner.ts (2)
src/client/LangSelector.ts (1)
  • translateText (258-278)
src/client/Utils.ts (1)
  • translateText (92-147)
src/client/HostLobbyModal.ts (2)
src/client/LangSelector.ts (1)
  • translateText (258-278)
src/client/Utils.ts (1)
  • translateText (92-147)
🔇 Additional comments (14)
src/server/GameServer.ts (2)

132-134: LGTM! Consistent pattern for config updates.

The maxPlayers update follows the same conditional pattern used for all other config properties in this method. Clean and straightforward.


153-174: Enhanced lobby-full diagnostics look good.

The updated error handling includes structured player count data (currentPlayers, maxPlayers) which the client parses to show user-friendly error messages. The JSON-stringified message field establishes a clear client-server contract that the client properly handles with try/catch blocks.

src/core/Schemas.ts (1)

173-173: LGTM! Proper validation bounds for player limits.

The schema now enforces integer values in the range [2, 1000], which aligns perfectly with the client-side constants (MIN_PLAYER_LIMIT and MAX_PLAYER_LIMIT in HostLobbyModal). The minimum of 2 ensures viable multiplayer games, while the maximum of 1000 is generous yet prevents abuse.

resources/lang/en.json (1)

267-267: LGTM! Clear and user-friendly localization strings.

The new localization entries support the player limit feature with clear, actionable messages. The {limit} placeholder in lobby_full will be properly replaced using the ICU message format, and the warning message clearly explains the consequence of setting a limit below the current player count.

Also applies to: 321-322

src/client/JoinPrivateLobbyModal.ts (4)

21-21: LGTM! Proper state management for optional player limit.

The maxPlayers: number | null type correctly represents an optional limit, where null means unlimited. Proper use of @state() decorator for Lit reactivity.


79-81: LGTM! Clean conditional rendering of player limit.

The ternary expression cleanly handles the optional limit display, showing "X/Y" format when maxPlayers is set. This matches the pattern used in HostLobbyModal for consistency.


133-134: LGTM! Proper state cleanup on lobby exit.

Resetting maxPlayers to null and clearing the players array ensures no stale data persists when leaving the lobby.


323-323: LGTM! Proper polling synchronization for maxPlayers.

The polling logic correctly updates maxPlayers from the server's gameConfig, using optional chaining and nullish coalescing to handle undefined values gracefully.

resources/lang/debug.json (1)

131-131: LGTM! Proper debug localization entries.

The debug locale entries correctly mirror the en.json additions with self-referential values, following the standard debug pattern used throughout the file.

Also applies to: 159-160

src/client/HostLobbyModal.ts (5)

62-66: LGTM! Proper state management and constants.

The state properties correctly model the player limit feature (number | null for optional limit, boolean for warning state). The static readonly constants (MIN=2, MAX=1000) match the schema validation exactly.


535-582: LGTM! Clean UI implementation matching existing patterns.

The player limit UI follows the same checkbox + conditional input pattern as the max timer feature, ensuring consistency. The warning display is appropriately styled and conditionally rendered. The player count display ("X/Y") mirrors the pattern in JoinPrivateLobbyModal.

Also applies to: 608-608


706-736: LGTM! Robust input validation and state management.

The event handlers properly prevent invalid input (blocking e/+/- keys and sanitizing values), enforce the [MIN, MAX] range, and update both the state and warning appropriately. The warning correctly triggers when the limit is set below the current player count.


877-877: LGTM! Clean config persistence.

Using this.playerLimit ?? undefined correctly converts null (unlimited) to undefined for the optional schema field. Clean and idiomatic TypeScript.


946-946: LGTM! Proper warning synchronization during polling.

Calling updatePlayerLimitWarning() after updating the clients array ensures the warning state stays current as players join or leave the lobby.

@abdallahbahrawi1 abdallahbahrawi1 marked this pull request as draft December 21, 2025 15:56
@abdallahbahrawi1 abdallahbahrawi1 marked this pull request as ready for review December 21, 2025 16:02
@iiamlewis
Copy link
Contributor

Very nice work, great feature. I have tagged this up and will assign for peer review

@iiamlewis iiamlewis moved this from Development to Final Review in OpenFront Release Management Dec 21, 2025
@iiamlewis iiamlewis added this to the v29 milestone Dec 21, 2025
Copy link
Contributor

@Lavodan Lavodan left a comment

Choose a reason for hiding this comment

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

Looks quite good, the new error message is a lot nicer, I left some minor comments.

@github-project-automation github-project-automation bot moved this from Final Review to Development in OpenFront Release Management Dec 21, 2025
@abdallahbahrawi1
Copy link
Contributor Author

Looks quite good, the new error message is a lot nicer, I left some minor comments.

Thanks i will take a look when i have time

@abdallahbahrawi1 abdallahbahrawi1 removed the request for review from VariableVince December 24, 2025 20:47
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)
resources/lang/en.json (1)

269-270: LGTM! Clear error message for full lobbies.

The new lobby_full key provides users with clear feedback when they attempt to join a full lobby. The placeholder format {limit} is consistent with other localization keys in the file, and the trailing comma on line 269 is correct JSON syntax.

Optional: Consider making the limit reference even more explicit

If you want to make it crystal clear that {limit} refers to the number of players, you could use:

-    "lobby_full": "Lobby is full (limit: {limit})."
+    "lobby_full": "Lobby is full ({limit} players max)."

However, the current wording is clear and concise, so this is purely optional.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8e3e1c and dabeb6c.

📒 Files selected for processing (3)
  • resources/lang/en.json
  • src/client/JoinPrivateLobbyModal.ts
  • src/core/Schemas.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/client/JoinPrivateLobbyModal.ts
  • src/core/Schemas.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-08-16T10:52:08.292Z
Learnt from: TheGiraffe3
Repo: openfrontio/OpenFrontIO PR: 884
File: resources/lang/en.json:456-461
Timestamp: 2025-08-16T10:52:08.292Z
Learning: In OpenFrontIO, translation files in resources/lang/*.json (except en.json) should not be updated in regular PRs. Only dedicated translation PRs titled "mls" and made by Aotumori should update non-English locale files. Regular PRs should only update en.json when adding or modifying translation keys.

Applied to files:

  • resources/lang/en.json
🔇 Additional comments (1)
resources/lang/en.json (1)

327-328: LGTM! Clear labels and helpful warning for hosts.

The new localization keys for the player limit feature are well-crafted:

  • player_limit: Simple, clear label for the UI control
  • player_limit_warning: Effectively warns the host about the consequence of setting a limit below the current player count without causing alarm (existing players remain, only new joins are blocked)

The semicolon usage is grammatically correct and keeps the message concise.

coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 31, 2025
coderabbitai[bot]
coderabbitai bot previously approved these changes Dec 31, 2025
@evanpelle
Copy link
Collaborator

looks like it's out of date

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: 2

Caution

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

⚠️ Outside diff range comments (1)
src/client/HostLobbyModal.ts (1)

774-831: Eliminate code duplication: extract config object.

The configuration object is built twice—once for detail.config (lines 775–801) and again for the JSON body (lines 804–830). Extract it into a variable and reuse it.

🔎 Proposed refactor
 private async putGameConfig() {
+  const config = {
+    gameMap: this.selectedMap,
+    gameMapSize: this.compactMap
+      ? GameMapSize.Compact
+      : GameMapSize.Normal,
+    difficulty: this.selectedDifficulty,
+    bots: this.bots,
+    infiniteGold: this.infiniteGold,
+    donateGold: this.donateGold,
+    infiniteTroops: this.infiniteTroops,
+    donateTroops: this.donateTroops,
+    instantBuild: this.instantBuild,
+    randomSpawn: this.randomSpawn,
+    gameMode: this.gameMode,
+    disabledUnits: this.disabledUnits,
+    playerTeams: this.teamCount,
+    ...(this.gameMode === GameMode.Team &&
+    this.teamCount === HumansVsNations
+      ? { disableNations: false }
+      : { disableNations: this.disableNations }),
+    maxTimerValue:
+      this.maxTimer === true ? this.maxTimerValue : undefined,
+    maxPlayers: this.playerLimit ?? undefined,
+  } satisfies Partial<GameConfig>;
+
   this.dispatchEvent(
     new CustomEvent("update-game-config", {
-      detail: {
-        config: {
-          gameMap: this.selectedMap,
-          // ... (all the repeated properties)
-        } satisfies Partial<GameConfig>,
-      },
-      body: JSON.stringify({
-        gameMap: this.selectedMap,
-        // ... (all the repeated properties)
-      } satisfies Partial<GameConfig>),
+      detail: { config },
+      body: JSON.stringify(config),
       bubbles: true,
       composed: true,
     }),
   );
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6e5b92 and 8f6f67e.

📒 Files selected for processing (4)
  • resources/lang/en.json
  • src/client/HostLobbyModal.ts
  • src/core/Schemas.ts
  • src/server/GameServer.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/Schemas.ts
  • src/server/GameServer.ts
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
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.
📚 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-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:

  • resources/lang/en.json
  • src/client/HostLobbyModal.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/HostLobbyModal.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/HostLobbyModal.ts
📚 Learning: 2025-12-26T22:21:21.904Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2689
File: src/client/PublicLobby.ts:245-245
Timestamp: 2025-12-26T22:21:21.904Z
Learning: In public lobbies with HumansVsNations mode in src/client/PublicLobby.ts, maxPlayers represents only human player slots (already halved in DefaultConfig.ts). The nation NPCs are added automatically server-side and don't count toward maxPlayers. Therefore, getTeamSize correctly returns maxPlayers directly for HumansVsNations to display the proper team size (e.g., maxPlayers=5 yields "5 Humans vs 5 Nations").

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2026-01-02T18:11:06.832Z
Learnt from: ryanbarlow97
Repo: openfrontio/OpenFrontIO PR: 2740
File: src/client/HostLobbyModal.ts:821-821
Timestamp: 2026-01-02T18:11:06.832Z
Learning: In src/client/HostLobbyModal.ts, the ?s=xxxxx URL suffix used in lobby URLs is purely for cache-busting platform previews (e.g., Discord, WhatsApp, x.com) and is not used by the join logic. The join flow ignores the suffix value, so regenerating it via updateUrlWithSuffix() on configuration changes will not break existing shared URLs; it only prompts platforms to refresh preview metadata. Treat the suffix as non-functional for join behavior and ensure any related changes preserve that invariant.

Applied to files:

  • src/client/HostLobbyModal.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/client/HostLobbyModal.ts
📚 Learning: 2025-10-20T20:15:28.858Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:51-51
Timestamp: 2025-10-20T20:15:28.858Z
Learning: In src/core/execution/FakeHumanExecution.ts, game balance constants like MIRV_COOLDOWN_TICKS, MIRV_HESITATION_ODDS, VICTORY_DENIAL_TEAM_THRESHOLD, VICTORY_DENIAL_INDIVIDUAL_THRESHOLD, and STEAMROLL_CITY_GAP_MULTIPLIER are experimental tuning parameters subject to frequent change during balance testing. Do not flag changes to these values as issues or compare them against previous values.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (4)
src/client/Utils.ts (1)
  • translateText (92-147)
src/client/LangSelector.ts (1)
  • translateText (258-278)
src/core/game/Game.ts (1)
  • HumansVsNations (56-56)
src/core/Schemas.ts (1)
  • GameConfig (90-90)
🪛 GitHub Actions: 🧪 CI
src/client/HostLobbyModal.ts

[error] 832-834: Transform failed: Expected ')' but found ':' during esbuild transformation at line 832. Likely a syntax error in an object literal (misplaced/missing braces or property).

🔇 Additional comments (9)
resources/lang/en.json (2)

272-273: LGTM! Clear error message with helpful context.

The new lobby_full entry is well-structured: the comma on line 272 properly extends the JSON, and the message clearly communicates why the user can't join while showing the limit value.


330-331: LGTM! Clear and user-friendly text.

Both entries follow existing localization patterns:

  • The label is concise
  • The warning clearly explains the consequence (new players can't join)

The text is grammatical and informative for hosts managing their lobby settings.

src/client/HostLobbyModal.ts (7)

26-26: LGTM! Component import added correctly.

The CheckboxWithInput component import is properly added and used consistently for both the max-timer and player-limit features.


66-70: LGTM! Clean state and constants.

The playerLimit uses null for unlimited (clear and idiomatic), and the constants are properly typed as private static readonly. The range 2–1000 is sensible.


497-516: LGTM! Consistent refactor to Lit component.

Replacing the inline timer UI with CheckboxWithInput improves consistency and addresses previous feedback about componentizing checkboxes. The logic correctly preserves the enable/disable behavior and updates the config.


519-551: LGTM! Player limit UI implemented correctly.

The checkbox/input logic correctly initializes the limit on enable and clears it on disable. The warning display is conditional and appropriately styled. Both handlers update the warning state and persist changes.


577-577: LGTM! Lobby header shows limit correctly.

The conditional formatting displays "X/Y Players" when a limit is set and "X Players" when unlimited, matching the expected behavior.


680-683: LGTM! Warning logic is sound.

The method correctly triggers the warning only when a limit exists and current players exceed it. The call sites (limit changes and player polling) ensure the warning stays synchronized.


897-897: LGTM! Warning refresh on player poll.

Calling updatePlayerLimitWarning() after updating the clients list ensures the warning state reflects the latest player count.

@abdallahbahrawi1
Copy link
Contributor Author

looks like it's out of date

i resolved the 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: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8f6f67e and c84b2bc.

📒 Files selected for processing (1)
  • src/client/HostLobbyModal.ts
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
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.
📚 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/HostLobbyModal.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/HostLobbyModal.ts
📚 Learning: 2026-01-02T18:11:06.832Z
Learnt from: ryanbarlow97
Repo: openfrontio/OpenFrontIO PR: 2740
File: src/client/HostLobbyModal.ts:821-821
Timestamp: 2026-01-02T18:11:06.832Z
Learning: In src/client/HostLobbyModal.ts, the ?s=xxxxx URL suffix used in lobby URLs is purely for cache-busting platform previews (e.g., Discord, WhatsApp, x.com) and is not used by the join logic. The join flow ignores the suffix value, so regenerating it via updateUrlWithSuffix() on configuration changes will not break existing shared URLs; it only prompts platforms to refresh preview metadata. Treat the suffix as non-functional for join behavior and ensure any related changes preserve that invariant.

Applied to files:

  • src/client/HostLobbyModal.ts
📚 Learning: 2025-12-26T22:21:21.904Z
Learnt from: FloPinguin
Repo: openfrontio/OpenFrontIO PR: 2689
File: src/client/PublicLobby.ts:245-245
Timestamp: 2025-12-26T22:21:21.904Z
Learning: In public lobbies with HumansVsNations mode in src/client/PublicLobby.ts, maxPlayers represents only human player slots (already halved in DefaultConfig.ts). The nation NPCs are added automatically server-side and don't count toward maxPlayers. Therefore, getTeamSize correctly returns maxPlayers directly for HumansVsNations to display the proper team size (e.g., maxPlayers=5 yields "5 Humans vs 5 Nations").

Applied to files:

  • src/client/HostLobbyModal.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/HostLobbyModal.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/client/HostLobbyModal.ts
📚 Learning: 2025-08-17T20:48:49.411Z
Learnt from: woodydrn
Repo: openfrontio/OpenFrontIO PR: 1836
File: src/client/Main.ts:482-482
Timestamp: 2025-08-17T20:48:49.411Z
Learning: In PR #1836, user woodydrn prefers to keep changes minimal and focused on the specific issue (clientID persistence) rather than refactoring redundant code in JoinLobbyEvent dispatchers. They want to avoid scope creep in focused bug fix PRs.

Applied to files:

  • src/client/HostLobbyModal.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/HostLobbyModal.ts
📚 Learning: 2025-10-20T20:15:28.858Z
Learnt from: sambokai
Repo: openfrontio/OpenFrontIO PR: 2225
File: src/core/execution/FakeHumanExecution.ts:51-51
Timestamp: 2025-10-20T20:15:28.858Z
Learning: In src/core/execution/FakeHumanExecution.ts, game balance constants like MIRV_COOLDOWN_TICKS, MIRV_HESITATION_ODDS, VICTORY_DENIAL_TEAM_THRESHOLD, VICTORY_DENIAL_INDIVIDUAL_THRESHOLD, and STEAMROLL_CITY_GAP_MULTIPLIER are experimental tuning parameters subject to frequent change during balance testing. Do not flag changes to these values as issues or compare them against previous values.

Applied to files:

  • src/client/HostLobbyModal.ts
🧬 Code graph analysis (1)
src/client/HostLobbyModal.ts (2)
src/client/Utils.ts (1)
  • translateText (92-147)
src/client/LangSelector.ts (1)
  • translateText (258-278)
🔇 Additional comments (6)
src/client/HostLobbyModal.ts (6)

66-70: Clean state design for the player limit feature.

Using null to represent "unlimited" is clear and idiomatic. The min/max constants prevent magic numbers throughout the component.


518-551: Player limit implementation is solid.

The checkbox toggle correctly assigns the default value when enabled and resets to null (unlimited) when disabled. The warning updates synchronously after state changes. Good work.


577-577: Simple and clear display logic.

Shows "X/Y Players" when a limit is set, and just "X Players" when unlimited. Easy to understand.


680-683: Warning logic is correct.

Only triggers when current players strictly exceed the limit. This matches the expected behavior where existing players are not kicked but the host is warned.


801-801: Config payload correctly converts null to undefined.

The nullish coalescing operator keeps the config object clean by using undefined for optional fields.


869-869: Good placement for warning update.

Calling updatePlayerLimitWarning() after fetching the latest client list ensures the warning stays accurate when players join or leave.

@ryanbarlow97
Copy link
Contributor

i resolved the conflicts

looks like there is more 😢

@abdallahbahrawi1
Copy link
Contributor Author

i resolved the conflicts

looks like there is more 😢

no i fixed it, but every time another PR changes the same file/lines, the conflict comes back

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.

5 participants