Skip to content

Remove game options of unused mods from game launch info - #7057

Merged
lL1l1 merged 12 commits into
FAForever:developfrom
lL1l1:Fix/long-game-options
Jul 24, 2026
Merged

Remove game options of unused mods from game launch info#7057
lL1l1 merged 12 commits into
FAForever:developfrom
lL1l1:Fix/long-game-options

Conversation

@lL1l1

@lL1l1 lL1l1 commented Mar 1, 2026

Copy link
Copy Markdown
Contributor

Description of the proposed changes

Removes unused mod options from scenario info that was causing live replays to not work according to this message on zulip #general > Live replay @ 💬

On initialization:

  1. Records all the default lobby options.
  2. During mod option import, record what option keys are used by what mods.

On game launch, remove all options that are not a default option and are not used by any enabled mods.

Testing done on the proposed changes

Playing the game without any mods gives a small ScenarioInfo.Options table (1320 bytes).

ConExecute([[SimLua reprsl(ScenarioInfo.Options)]])

Playing the game with a mod that has lots of options such as "Swarm Survival Vanilla" produces no errors in the log from the mod and printing the scenario info shows the expected options. The debugging also does not show that any removed options that were used by the mod.

M28AI adds an option with the key "UnitCap" which is the same as the base game unit cap option's key. M28AI's option is not removed and its value of 10000 works as expected as shown by the scoreboard.

Ending the game and entering a new skirmish with adjusted mods and without restarting the application does not cause any issues as long as the lobby file was not dirtied in the session.

Checklist

Summary by CodeRabbit

Bug Fixes

  • Removed lobby game options associated with unused mods during game launch, preventing incorrect option sets and improving live replay compatibility.

Documentation

  • Updated type annotations for lobby communication and host-related APIs to clarify the expected game launch data.

This fixes the removal logic incorrectly removing options with the same key as a default option that is not from the AIOpts table.
This fixes M28AI's UnitCap option.
@lL1l1
lL1l1 requested a review from speed2CZ March 1, 2026 08:21
@lL1l1 lL1l1 added area: ui Anything to do with the User Interface of the Game area: lobby related to lobby: options, chat, etc labels Mar 1, 2026
@lL1l1
lL1l1 marked this pull request as ready for review March 1, 2026 08:22
@coderabbitai

coderabbitai Bot commented Mar 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@lL1l1, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dbf7c2cb-6abb-4fde-a0c6-7c042e825c93

📥 Commits

Reviewing files that changed from the base of the PR and between ae6da0c and e7fc311.

📒 Files selected for processing (1)
  • lua/ui/lobby/lobby.lua
📝 Walkthrough

Walkthrough

The lobby records default and mod-owned game option keys, removes non-default options contributed only by disabled mods during launch, reuses the computed simulation mod list, and adds related LuaDoc annotations. A changelog entry documents the fix.

Changes

Mod-aware game option filtering

Layer / File(s) Summary
Option ownership tracking
lua/ui/lobby/lobby.lua
Tracks default option keys and associates imported AI options with their supplying mods, including duplicate-key diagnostics.
Launch-time filtering and typing updates
lua/ui/lobby/lobby.lua, lua/ui/lobby/lobbyComm.lua, changelog/snippets/fix.7057.md
Filters unused non-default options before launch, assigns gameInfo.GameMods from simMods, updates LuaDoc annotations, and documents the fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LobbyLaunch
  participant Mods
  participant ModOptionMapping
  participant GameInfo
  LobbyLaunch->>Mods: Get selected simulation mods
  LobbyLaunch->>ModOptionMapping: Check option ownership
  ModOptionMapping->>GameInfo: Remove options unused by selected mods
  LobbyLaunch->>GameInfo: Set GameMods from simMods
  LobbyLaunch->>GameInfo: Broadcast Launch
Loading

Possibly related PRs

  • FAForever/fa#6997: Adds the UnionWhenDisconnected lobby option that is evaluated by the new default-option filtering logic.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: removing unused mod game options from launch info.
Description check ✅ Passed The description covers the change, testing, and checklist, and follows the template well.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
lua/ui/lobby/lobby.lua (1)

154-154: Prefer stable mod identifiers over display names for matching.

Using name for ModOptionMapping and enabledModNames can misclassify options when different mods share a display name. Use a stable identifier (uid/UID) when available.

💡 Proposed hardening
-                ModOptionMapping[t.key][ModData.name] = true
+                local modIdentifier = ModData.uid or ModData.UID or ModData.name
+                ModOptionMapping[t.key][modIdentifier] = true
-        for _, modInfo in gameInfo.GameMods do
-            enabledModNames[modInfo.name] = true
+        for _, modInfo in gameInfo.GameMods do
+            local modIdentifier = modInfo.uid or modInfo.UID or modInfo.name
+            enabledModNames[modIdentifier] = true
         end

Also applies to: 2355-2358

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lua/ui/lobby/lobby.lua` at line 154, The code uses ModData.name when keying
ModOptionMapping and enabledModNames which can collide across mods; change the
keys to use a stable identifier (e.g., ModData.uid or ModData.UID) wherever
ModOptionMapping[t.key][ModData.name] and enabledModNames are set or checked
(also update the similar occurrences around the enabledModNames block at the
other location), and add a safe fallback to ModData.name only if uid/UID is nil
to preserve behavior for older mods; ensure all lookups and assignments that
previously used ModData.name are updated to the chosen stable identifier to keep
mapping consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lua/ui/lobby/lobby.lua`:
- Around line 2353-2389: The GameOptions filtering runs too late; move the whole
filtering block (the code that builds enabledModNames, iterates
gameInfo.GameOptions, uses IsDefaultOption, ModOptionMapping and
IsOptionUsedByGivenMods, collects keysToRemove and nils them from
gameInfo.GameOptions) so it executes before the launch data broadcast is
created/sent (i.e. before the function that prepares/broadcasts the launch
payload to clients). Also ensure any mod resolution this filtering depends on
(the population of gameInfo.GameMods and ModOptionMapping) is performed
beforehand so enabledModNames is accurate; after moving, confirm the broadcast
uses the now-filtered gameInfo.GameOptions.

---

Nitpick comments:
In `@lua/ui/lobby/lobby.lua`:
- Line 154: The code uses ModData.name when keying ModOptionMapping and
enabledModNames which can collide across mods; change the keys to use a stable
identifier (e.g., ModData.uid or ModData.UID) wherever
ModOptionMapping[t.key][ModData.name] and enabledModNames are set or checked
(also update the similar occurrences around the enabledModNames block at the
other location), and add a safe fallback to ModData.name only if uid/UID is nil
to preserve behavior for older mods; ensure all lookups and assignments that
previously used ModData.name are updated to the chosen stable identifier to keep
mapping consistent.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9868135 and bd1d0ab.

📒 Files selected for processing (2)
  • changelog/snippets/fix.7057.md
  • lua/ui/lobby/lobby.lua

Comment thread lua/ui/lobby/lobby.lua
@lL1l1
lL1l1 merged commit 56ea5f9 into FAForever:develop Jul 24, 2026
2 of 3 checks passed
@lL1l1
lL1l1 deleted the Fix/long-game-options branch July 24, 2026 21:02
NoryGit pushed a commit to NoryGit/FAForever-Rust-Client that referenced this pull request Sep 5, 2026
Four things players reported, in the order they were hit.

**"Ack! Unable to load replay from gpgnet" when watching a live replay.**
Live spectating never updated the install it launches. `play_file` stages the
exact build its replay pins, so the replay install sits on whatever old replay
was watched last, and nothing else ever touches that directory. A live stream
is always the *current* build, and FA refuses one whose build does not match.
`watch_live` now runs `ensure_latest_game_version` for the game's featured mod
before the stream is opened, deliberately before: the replay server starts
sending on the handshake, so a patch download after that point would be spent
buffering frames nobody is reading yet.

**"Live Replays Workaround", ported from the Python client.**
Settings, Game: stream a live replay through a Windows named pipe instead of
the local TCP proxy (`game/pipe_live_replay` there). Off by default, because
the pipe freezes the window while catching up and ends the replay abruptly with
no army selection or statistics. It exists because the TCP path still hits the
engine's oversized-`ScenarioInfo` bug until `FAForever/fa#7057` lands upstream,
and the pipe does not. Includes the Python client's 2048-byte terminator,
without which FA simply hangs when the stream ends.

The Python client's sibling switch, "Run in own process", is deliberately not
ported: replays already run on the separate `replaydata` install here, so the
switch could only take that away.

**"could not prepare simulation mods: <folder> is already installed".**
FAF mod uids are per *version*, so a host on an older release asks for a uid
nobody holding the newer one has. `ensure_game_mods` downloaded it and the
extraction then refused, because the folder was occupied. It now reads the
archive's own top-level folder without extracting, and a collision stops the
join having changed nothing on disk, reporting the versions involved. The user
answers a prompt, and only then is the installed version removed, through
`uninstall_mod` so its uid also leaves `game.prefs`. Mods that do not collide
are still installed straight away; only the destructive step waits. Same
posture as the Python client's `downloadMod`.

**Installed mods were not clickable.**
The description lives in the vault, so reading it meant leaving the client.
The card body now opens a detail dialog with the description, version, folder
and uid, and a link through to the mod's vault entry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: lobby related to lobby: options, chat, etc area: ui Anything to do with the User Interface of the Game

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants