diff --git a/Dockerfile b/Dockerfile
index 0941d9c..270e70d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -390,6 +390,7 @@ COPY container/ ${ARENA_HOME}/container/
COPY suramadu/web/ ${ARENA_HOME}/web/
COPY megamek/clientsettings.xml.template ${ARENA_HOME}/config/
+COPY megamek/gameoptions.xml.template ${ARENA_HOME}/config/
COPY megamek/log4j2-quiet.xml ${ARENA_HOME}/config/
COPY suramadu/suramadu.config.template ${ARENA_HOME}/config/
COPY suramadu/jetty.properties.template ${ARENA_HOME}/config/
diff --git a/README.md b/README.md
index c76ecee..bb5107e 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ a rendering bug, everything MegaMek-shaped is in one directory.
| | |
|---|---|
-| `megamek/` | configuring and patching MegaMek: the nine patches, client settings, logging config, `units.cache` priming, `prune-data.sh` — one documented line per thing removed from the shipped data, and why a match cannot reach it — and `skin/` — `lanceBlueSkin.xml`, the lance.blue board backgrounds and window icons, and the install script that checks every image the skin references exists |
+| `megamek/` | configuring and patching MegaMek: the nine patches, client settings, game options, logging config, `units.cache` priming, `prune-data.sh` — one documented line per thing removed from the shipped data, and why a match cannot reach it — and `skin/` — `lanceBlueSkin.xml`, the lance.blue board backgrounds and window icons, and the install script that checks every image the skin references exists |
| `suramadu/` | configuring Suramadu: its server config, Jetty properties, the fontconfig generator its toolkit requires, `web/` — the themed loading page served in place of the war's — and `patches/`, applied to the frontend bundle inside the war at build time |
| `src/` | our own Java — `MatchHost`, `MatchWatcher`, `TextRenderer` — and the script that jars it |
| `container/` | process management inside a running container: `entrypoint.sh`, `init/`, `watch/`, `exit/`, `lib/` |
@@ -106,6 +106,25 @@ the lounge nobody starts, and the game everyone left or stopped playing; a
finished game is exempt, because the host is deliberately lingering then. A
manifest that omits the limit, or sets it to 0, gets no timeout.
+## Rules defaults
+
+Two files are seeded into MegaMek's `mmconf/` before every launch, and they are
+the whole of what arena says about how a match plays.
+`megamek/clientsettings.xml.template` is one player's client — what is docked,
+what is drawn, what makes a noise. `megamek/gameoptions.xml.template` is the
+rules, for everyone in the match: MegaMek's game options, listed only where they
+differ from upstream's defaults.
+
+Two differ today. Ejected crews leave the field the moment they get out, rather
+than staying as units their owner then moves every round — MegaMek's own
+`ejected_pilots_flee`, which some of the shipped scenarios already set for
+themselves. And a unit that cannot fire — shut down, crew out — gets no firing
+turn, which is the choice MegaMek already makes for the physical phase.
+
+Both are defaults, not rules arena imposes. A scenario that names a
+`GameOptionsFile` of its own is loaded with that file and none of this, so a
+scenario author still decides their own match.
+
## Patches
Nine, in `megamek/patches/patchset/`. Each is a MegaMek bug rather than an
diff --git a/TODO.md b/TODO.md
index 6a9d4ff..8cff023 100644
--- a/TODO.md
+++ b/TODO.md
@@ -350,6 +350,27 @@ and the p90 tail fell from ~470ms to ~330ms. The proxy was ruled out early
the window icons and the `GUIPreferences` that go with them. What is
left is verification of the window icons, tracked under render
performance.
+- [x] **Matches ran on MegaMek's stock rules** — there was no game options file
+ at all, so every match got upstream's defaults whether or not they suited
+ a browser match. `megamek/gameoptions.xml.template` is now seeded into
+ `mmconf/` beside the client settings, and `ScenarioV1` picks it up for any
+ scenario that does not name a `GameOptionsFile` of its own.
+
+ Two options differ from stock: `ejected_pilots_flee`, so a dead mek's
+ MechWarrior does not stay on the board as a unit its owner moves every
+ round, and `skip_ineligible_firing`, so a shut-down unit gets no firing
+ turn it can only press Done on.
+
+ Verified by mounting the file into the shipped image and loading with
+ `ScenarioReport`: MegaMek logs both as set, and all 51 scenarios load
+ exactly as they did without it. What has not been seen is the effect in
+ play — a mek ejecting in a hosted match and the MechWarrior not appearing.
+
+ Considered and left alone: `skip_ineligible_movement`, because the
+ movement phase is where a shut-down mek starts up and where TacOps:AR
+ abandonment happens; the nag dialogs, which are protective rather than
+ noise; and turn timers, which are a feature rather than a default worth
+ correcting.
- [ ] **The minimap cannot dock** — unlike the unit display and mini report it
has no location preference and is dialog-only. Hidden by default (patch
0003 made that possible at all). Docking it properly needs more patching.
diff --git a/container/init/40-render-config.sh b/container/init/40-render-config.sh
index 2c2d8fb..c3895fe 100755
--- a/container/init/40-render-config.sh
+++ b/container/init/40-render-config.sh
@@ -110,6 +110,12 @@ sed -e "s|@PORT_WS@|$PORT_WS|g" \
"$ARENA_HOME/config/jetty.properties.template" > "$ARENA_RUN/suramadu/jetty.properties"
cp "$ARENA_HOME/config/clientsettings.xml.template" "$MM_HOME/mmconf/clientsettings.xml"
+# The rules half of the same pair, read by the host rather than the client:
+# ScenarioV1 loads mmconf/gameoptions.xml for any scenario that does not name a
+# GameOptionsFile of its own. Re-seeded per launch for the same reason
+# clientsettings.xml is - a player who opens the game options dialog and
+# presses OK makes the client write this file back.
+cp "$ARENA_HOME/config/gameoptions.xml.template" "$MM_HOME/mmconf/gameoptions.xml"
# A leftover placeholder means the client would try to log in as "@PLAYER_NAME@"
# and sit in the lobby forever. Easy to catch here, hard to diagnose live.
diff --git a/megamek/gameoptions.xml.template b/megamek/gameoptions.xml.template
new file mode 100644
index 0000000..639f067
--- /dev/null
+++ b/megamek/gameoptions.xml.template
@@ -0,0 +1,77 @@
+
+
+
+
+
+ ejected_pilots_flee
+ true
+
+
+
+
+ skip_ineligible_firing
+ true
+
+
diff --git a/tests/shell/test-render-config.sh b/tests/shell/test-render-config.sh
index c520945..3326168 100755
--- a/tests/shell/test-render-config.sh
+++ b/tests/shell/test-render-config.sh
@@ -18,6 +18,7 @@ printf 'did:plc:aaa\tTraineeA\n' > "$TMP/run/clients.tsv"
cp "$ROOT/suramadu/suramadu.config.template" "$TMP/home/config/"
cp "$ROOT/suramadu/jetty.properties.template" "$TMP/home/config/"
cp "$ROOT/megamek/clientsettings.xml.template" "$TMP/home/config/"
+cp "$ROOT/megamek/gameoptions.xml.template" "$TMP/home/config/"
ln -s "$ROOT/container" "$TMP/home/container"
ARENA_HOME="$TMP/home" ARENA_RUN="$TMP/run" MM_HOME="$TMP/mm" \
@@ -33,6 +34,7 @@ check() { #
CFG="$TMP/run/suramadu/suramadu.config"
JETTY="$TMP/run/suramadu/jetty.properties"
SETTINGS="$TMP/mm/mmconf/clientsettings.xml"
+OPTIONS="$TMP/mm/mmconf/gameoptions.xml"
check "suramadu.config is valid JSON" jq -e . "$CFG"
check "player name substituted" grep -q 'player_name TraineeA' "$CFG"
@@ -92,4 +94,20 @@ check "fov darkening off" grep -q 'name="FovDarken" value="fa
# the first unit rather than one that sorts oddly.
check "weapons sort by damage, big first" grep -q 'name="DefaultWeaponSortOrder" value="DAMAGE_HIGH_LOW"' "$SETTINGS"
+# --- game options ------------------------------------------------------------
+# ScenarioV1 reads mmconf/gameoptions.xml only for scenarios that name no
+# GameOptionsFile, and only from the host's working directory. An absent file is
+# not an error there - loadOptions() returns quietly - so a render that stopped
+# seeding it would give every match MegaMek's stock rules with no sign of it.
+check "gameoptions seeded into mmconf" test -f "$OPTIONS"
+check "gameoptions is well-formed XML" python3 -c "import sys,xml.etree.ElementTree as E; E.parse(sys.argv[1])" "$OPTIONS"
+# The one rule a player would notice going missing: without it a dead mek's
+# MechWarrior stays on the board and its owner moves it every round.
+check "ejected crews leave the field" bash -c "python3 - '$OPTIONS' <<'PY'
+import sys, xml.etree.ElementTree as E
+opts = {o.findtext('optionname'): o.findtext('optionvalue')
+ for o in E.parse(sys.argv[1]).getroot()}
+sys.exit(0 if opts.get('ejected_pilots_flee') == 'true' else 1)
+PY"
+
exit "$fail"