Time-limit pick lists go to 23 entries: 1-15, 20, 25, 30, 45, 60, 120, 180, 240. Content + console only, no MW4.exe rebuild -- nothing between the dropdown and the mission timer clamps the value, and m_gameLength is 8 bits on the wire (MWApplication.cpp:790), so 255 was always the ceiling. Hand-applied rather than merged. LYLT revertedaa500be7(9aa317ea) and built on the original 9-entry list, while this branch carries the fixed 18-entry version from456e1978, so the two sides have different bases. Merging the branch would also drag in that revert of the Battlemaster and Behemoth loadout work. Two deliberate deviations from3514acf6: - Its max_displayed restructure is NOT taken. Both sides independently reached the same logic (i==5 -> 10, i==6 -> 4, else 16), but LYLT wrote the outer else without braces. That bare else-then-if is the construct behind the null-reference console lobby crash in theaa500be7regression;456e1978already fixed it here. Ours is kept. - HostLobbyMission.script was untouched on this branch so LYLT's version is taken wholesale, but its else block (i==5 / i==11) is re-braced for the same reason before it can bite the PC host lobby. Fixes a real bug this branch already had: TIME_LIST_DEFAULT was still the C++ global g_nTimeList_Index (3), which indexed "7" in the old 9-entry list but indexes "4" in the expanded one -- the default mission time had silently become 4 minutes. Now the literal 6, which is "7" in both the 18- and 23-entry lists. Kept as a literal so the script stays independent of the exe; the reset-to-defaults path matches by value (g_nTimeList_Value = 7) and needed no change. The rest scales on its own: drop_list_size[5], the doh loop bounds and the nselected fallbacks in ConLobbyMission all already used TIME_LIST_COUNT / TIME_LIST_DEFAULT symbolically. The DEMO_CODE list (11 entries, default 5) is untouched. Verified 23 contiguous entries and balanced braces in both files, no unbraced else-if left, CRLF and us-ascii preserved. Not yet repacked: these live in props.mw4, so run build-env\build-resources.ps1 on the Windows box, deleting resource\props.mw4 + props.dep first to force a full repack (incremental carries stale entries forward). Not run on a real bay. Co-authored-by: Claude Opus 5 (Anthropic) <noreply@anthropic.com> Co-authored-by: GitHub Copilot <copilot@github.com>
9.3 KiB
Mission time limits — how long a match can be set for
Reference for the MP mission time limit (m_gameLength): what the operator can pick,
what the engine can carry, why "No Limit" is not currently usable, and what it would
take to go past 255 minutes. Written alongside the LYLT branch, which extended the
pick lists to the engine's existing ceiling.
The short version
| Value | |
|---|---|
| Console / PC lobby list (after LYLT) | 1-15, 20, 25, 30, 45, 60, 120, 180, 240 min |
| Wire ceiling, no code change | 255 min (4 h 15 m) |
| Default | 7 min |
0 on the wire |
"no time limit" — do not ship; see below |
| Beyond 255 | needs a wire-format change — see Plan B |
How the value flows
- The operator picks minutes from a dropdown in
ConLobbyMission.script(venue console) orHostLobbyMission.script(PC host lobby). FSConsole reads the same list fromvenue/fsconsole/content/FSConfig.xml. - The script calls
SetNetworkMissionParamater(GAME_LENGTH_PARAMETER, minutes)→params->m_gameLength(MW4Shell.cpp:2066). No clamp anywhere on this path. - The value ships to the pods inside the NMP blob as 8 bits:
stream->WriteBits(&m_gameLength, 8); // 256 minutes(MWApplication.cpp:790, read back at:910). That is the entire ceiling: 0-255. - At mission start the pod does
mission->SetEndMissionTime(m_gameLength * 60.0f)(MWApplication.cpp:3537) — and only whenm_gameLength > 0— plusCTCL_SetGameTime(m_gameLength * 60)for the console clock (int32 seconds, no limit of its own).
Nothing between the dropdown and the mission timer cares how large the number is. The
mission timer itself is double (Stuff::Time, Scalar.hpp:225),
so long matches do not lose time accuracy; only the Scalar (float) countdown view does,
and only by ~8 ms at the 24-hour mark.
Displays are already wide enough
| Where | Format | At 240 min |
|---|---|---|
MP scoreboard clock, hudscore.cpp:443 |
%d:%02d |
240:00 |
Radar/shell HUD, GUIRadarManager.cpp:1981 |
%02d:%02d |
240:00 (6 glyphs in a slot laid out for 5 — watch for clipping) |
Script GetGameTime, MW4Shell.cpp:3910 |
%02d:%02d:%02d |
04:00:00 |
FSConsole pane, FSGamePane.cs |
<min>:00 |
240:00 |
What LYLT changed
Content and console only — no MW4.exe rebuild:
ConLobbyMission.script:TIME_LIST_COUNT9 → 23, the 23 list entries, andmax_displayed = 10for the time dropdown so it scrolls instead of overflowing the panel.TIME_LIST_DEFAULTwas$$g_nTimeList_Index$$(the C++ globalg_nTimeList_Index = 3,MW4Shell.cpp:159) — index 3 is"7"in the OLD list but"4"in the new one, so it is now the literal6, the index of"7". Changing the C++ global instead would have forced an exe rebuild for no other gain. The "reset to defaults" path is unaffected: it searches the list by value forTIME_VALUE_DEFAULT=g_nTimeList_Value= 7.HostLobbyMission.script: same list,drop_list_size[5]9 → 23,max_displayed = 10, and the two selection loops that hardcoded9(and thenselected = 3fallback →6).- FSConsole: the 23 entries in
catalog/generate_fsconfig.py(regenerated intocontent/FSConfig.xml),MaxDropDownItems = 10on the pane's combo, and a range guard inNmpBuilder— it used to writeTimeLimitMinutes & 0xFF, which silently turned 256 into 0. It now throws outside 1-255.SelfTest.CheckTimeLimits()proves every catalog entry encodes to the right value at the right bit, and that 0/256/1440 are refused.
Anything already in flight is unaffected: a stored value picks the first list entry >= it,
so old saved settings still resolve.
The branch carries sources only. The repacked resource\*.mw4 packages, their .deps, the
deployed MW4\resource copies and the staged MW4pro.exe are build output and are not
tracked on it — they get rebuilt from main once this merges. Until you repack, a checkout
still shows the old 9-entry list in game:
build-env\build-resources.ps1 # rebuilds core.mw4 + props.mw4
copy Gameleap\mw4\Resource\props.mw4 MW4\resource\ # and core.mw4
The "No Limit" trap — why 0 is not shipped
SetEndMissionTime is only called when m_gameLength > 0, so 0 looks like a free
"unlimited match" option. It is not usable as the code stands:
- With the end timer stopped,
GetEndMissionTime()returns 0 (MWMission.hpp:602-608), soCheckEndMissionTime()returns 0 too. - The networked fade-out reads that as "under 3 seconds left" and fades the screen to
full black three seconds into the mission
(
MWGUIManager.cpp:1633-1649).
Nothing sets 0 today (CTCL_DefaultHostSetup forces 7,
MW4Shell.cpp:13332), which is consistent
with the path never having been exercised. Read from code, not yet reproduced in game.
To make "No Limit" real: guard that fade block on EndMissionTimerRunning(), then audit every
other consumer that treats a 0 countdown as "expired" (TestForGameShutDown, the 60 s/30 s
voice cues at MWMission.cpp:1263-1288,
scoreboard and mission-review). That is an MW4.exe rebuild plus a pod redeploy — cheap in
code, but it needs the same lockstep rollout discipline as Plan B, and the venue gets most of
the benefit from 240 minutes anyway.
Plan B — going past 255 minutes
Everything above is configuration. Past 255 it becomes a wire-format change, which is a
different risk class: the NMP has no version field, so a pod on a different build mis-parses
every field after m_gameLength (map name, MapClientCRC) and refuses the lobby.
B1 — widen the field. WriteBits/ReadBits for m_gameLength
(MWApplication.cpp:790 and :910).
Use 11 bits (2047 min): the blob is 3777 fixed bits with 7 pad bits before
WriteByteAlign, so up to 15 extra bits are absorbed without changing the 473-byte fixed
length, and FSConsole's length assert keeps working. The bit layout still shifts — that is
what forces B3.
B2 — update every mirror, identically.
mw4dummy.cpp:433/552—mw4printcarries a full duplicate of the serialization for score printouts. Miss it and printouts garble silently.venue/fsconsole/protocol/NmpBuilder.cs(+testdata/vectors/expected.json, the golden cross-validation vectors, and theCheckTimeLimitsbit offset).- Rebuild
MW4.exeRelease and Profile (Profile is the resource builder).
B3 — lockstep deployment. All pods, both consoles (legacy MW4.exe console and
FSConsole) and mw4print ship together. The …A7 legacy-console rollback path is only valid
against equally rolled-back pods.
B4 — display formats. %d:%02d and %02d:%02d print 1440:00; both want an h:mm:ss
form above 60 minutes, with column and clip re-checks. FSConsole's <min>:00 likewise.
MW4Shell::GetGameTime already emits hh:mm:ss.
B5 — the actual unknown: a day-long match. The format change is a day's work; proving the
engine survives 24 hours is the project. In order: memory growth and heap fragmentation
(debris, effects, chat, net-stat buffers); the .mr mission-review recording, which
accumulates per frame — establish whether it has a cap or rollover before assuming a 24 h
recording is even writable, and what that does to the printout pipeline; DirectPlay session
stability; score and stat counters; and the 8-bit sibling timers — m_joinInProgressCutOffTime
is also 8-bit minutes (the game writes 1000 and wires 232), so join-in-progress cutoff
cannot express a long match either.
B6 — test ladder. vpod bench → 4 h bay run → 12 h → 24 h, sampling process memory and confirming end-of-mission, review and printout at each rung. Expect the failures in B5, not B1.
Verifying a time-limit change
python venue\fsconsole\catalog\generate_fsconfig.py— regeneratesFSConfig.xmland fails loudly if the authored blocks drift from the sources.dotnet publish venue\fsconsole\FSConsole.csproj -c Release -o dist\App, thenFSConsole.exe -selftest -mission -site bench.iniagainstpython C:\VWE\showrunner\tools\vpod_ctcl.py --pods 2 --cam --write-ini bench.ini.CheckTimeLimitsruns in every self-test mode, network or not.build-env\build-resources.ps1to repackprops.mw4, then copy it toMW4\resource.- In game: pick the longest entry, confirm the lobby dropdown scrolls, the HUD clock reads 3-digit minutes without clipping, the mission ends on time, and the debriefing prints.