console/relay: document it properly + fix two regressions the review caught
DOCS (the ask: after a compaction this session lost track of how the console
works and launched the wrong program, twice).
* NEW context/operator-console.md -- the dedicated topic that was missing.
Leads with the thing I got wrong: btoperator.py is the PySide6 GUI the
operator uses; btconsole.py is the headless relay it spawns. Then ports,
the route table, the roster/seat/identity model, the full round lifecycle
with every launch gate, liveness, mode-specific traps, and log locations.
* NEW docs/OPERATOR_GUIDE.md -- sysop-facing: start the console, set up a
mission, watch pods arrive, launch, run back-to-back rounds, what to press
when LAUNCH looks dead, a troubleshooting table keyed on the exact log
lines, and what to save BEFORE restarting a session (Start Session
truncates operator_relay.log, so restarting to clear a problem destroys the
evidence of it).
* CLAUDE.md: two Quick Lookup rows + a DO-NOT entry naming the two programs,
so the distinction survives the next compaction.
* context/multiplayer.md: a pointer out of the scattered console notes to the
new topic (they were buried across ~8 places in a large file, which is
exactly why they evaporated).
FIXES -- both are regressions in my own previous commit, found by the review
pass, and one would have made a games night WORSE:
* THE REAPER WOULD HAVE KILLED HEALTHY PLAYERS. It was gated only on "no
mission running", which a round RESET satisfies -- so it was armed for the
whole BETWEEN-ROUNDS wait, and that is a period when a pod is legitimately
byte-silent: its seat beacon is write-only for the process lifetime
(L4NET.CPP: "the relay ignores its silence") and its console pad has no egg
yet so it cannot ACK. A real night showed 12-minute and 5-minute gaps; the
180s deadline would have dropped healthy pods and forced their clients to
relaunch. It now runs ONLY in the active staging window, skips pads with no
egg and conns that have not HELLO'd, and last_seen is also stamped from
inbound UDP (a pod streaming updates while its TCP idles was being counted
as silent). Half-open detection is keepalive's job; this is just a backstop.
* UnboundLocalError in the operator UI. My end_sent reset was an `elif` in
the chain that assigns `head`, so that branch left `head` unbound -- a crash
on the first status refresh after End Mission, which is exactly the path the
relay's "StopMission sent" line produces. Moved out of the chain.
Plus one pre-existing wedge with the same symptom as the reported bug, live-
proven in operator_relay.log (~5 minutes of a night lost): _abort_round clears
eggs_released BEFORE the survivors' sockets close, so _maybe_reset_round's own
`if not self.eggs_released: return` skips the template restore forever -- the
roster stays trimmed, the release gates can never be met, and walk-ups get
ROSTER FULL. _rearm_for_new_round's restore is therefore now UNCONDITIONAL (it
was gated on eggs_released, which made Re-arm useless in the one state that most
needs it) and it clears round_hold_until so an abort's settle window is not
inherited. Aborts are ordinary: nothing on the wire distinguishes a straggler's
late FIN from a pod dying mid-load.
scratchpad/test_relay_rearm.py now 19 checks, all passing, including the two new
regression guards (a 15-minute-silent waiting pod is NOT reaped; a pad that was
never sent an egg is NOT reaped) and the mid-pair no-re-arm guard.
Still open, recorded in the new topic's frontmatter: the UDP endpoint map trusts
the sender's self-declared fromHost; the egg-ACK is a fixed-offset parse of one
recv with no reassembly; remote-operator mode can never enable LAUNCH.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4736cba1ca
commit
1cda880c6d
@@ -0,0 +1,189 @@
|
||||
# BattleTech 4.11 — Operator Console Guide
|
||||
|
||||
How to run a games night: set up a mission, get pods seated, launch rounds, and recover when
|
||||
something sticks. Written for the sysop at the console, not for players.
|
||||
|
||||
*Engineering detail (ports, protocol, state machine) lives in `context/operator-console.md`.
|
||||
Player-facing instructions ship in the zip as `README.txt`.*
|
||||
|
||||
---
|
||||
|
||||
## 1. Starting the console
|
||||
|
||||
```
|
||||
cd C:\git\bt411
|
||||
python tools\btoperator.py
|
||||
```
|
||||
|
||||
A window titled **BT411 Operator Console** opens. That is the console.
|
||||
|
||||
> **One thing worth knowing:** there are two programs. `btoperator.py` is the window you use.
|
||||
> `btconsole.py` is the headless relay it starts for you in the background — all the actual
|
||||
> networking. You never launch that one directly; its output is what scrolls in the log pane at the
|
||||
> bottom of the window. When this guide says "the relay said…", that is where to look.
|
||||
|
||||
---
|
||||
|
||||
## 2. Set up the mission, then start the session
|
||||
|
||||
Work top to bottom:
|
||||
|
||||
1. **Pick the mission settings** — map, mission length, weather, experience level, and the seat
|
||||
(pod) count. Every dropdown is populated live from `BTL4.RES`, so anything offered is valid.
|
||||
2. **Set the seat count to roughly who you expect.** It does not have to be exact — you can launch
|
||||
with fewer (see §4) — but a wildly oversized roster makes the staging messages confusing.
|
||||
3. **Choose the mode**: **Relay (internet)** is normal — players dial out to you, so *they* need no
|
||||
port forwarding. **Mesh (LAN)** is the original cabinet direction for a local network.
|
||||
4. **Press Start Session.** The relay comes up and begins accepting pods.
|
||||
|
||||
**For internet play, you must be reachable.** Forward/allow these TCP ports to this machine
|
||||
(default console port 1500):
|
||||
|
||||
| Port | Why |
|
||||
|---|---|
|
||||
| 1500/tcp | the console channel (hands out the mission egg, sends the launch) |
|
||||
| 1501/tcp | game traffic between pods |
|
||||
| 1501/udp | the fast update channel (position/motion) |
|
||||
|
||||
Port 1507/tcp is the operator control channel — only needed if someone drives this relay remotely.
|
||||
Players give their client `BT_RELAY=<your address>:1500`; `players/join.bat` handles that.
|
||||
|
||||
---
|
||||
|
||||
## 3. Watch the pods arrive
|
||||
|
||||
Each pilot row lights up as they progress. In order:
|
||||
|
||||
**waiting** → **seated** (claimed a seat, callsign and mech shown) → **egg sent** →
|
||||
**registered** → **ready** (mission loaded) → **LAUNCHED**.
|
||||
|
||||
You will see this in the log and it is **normal, not an error**:
|
||||
|
||||
```
|
||||
console conn 1.2.3.4:5678: egg HELD (3/8 pods present)
|
||||
```
|
||||
|
||||
The mission file is deliberately held until every seat is present, so that every pod's copy
|
||||
contains everyone's callsign and mech choice. If you are not waiting for the rest, just launch —
|
||||
see next.
|
||||
|
||||
---
|
||||
|
||||
## 4. Launch a round
|
||||
|
||||
Press **🚀 LAUNCH MISSION**.
|
||||
|
||||
- If everyone has loaded, the round starts within a few seconds.
|
||||
- **If some seats are empty, launching is still correct** — the relay shrinks the session to the
|
||||
players who are actually here and starts. You do not need to edit the roster down first.
|
||||
- The relay holds the start until every pod reports **ready**, so nobody loads into a mission
|
||||
already in progress. If one pod is slow you will see who, every 10 seconds; after 3 minutes it
|
||||
starts anyway rather than letting one wedged client hold up the night.
|
||||
|
||||
The mission then runs on its own clock (the mission length from the egg). **⏹ END MISSION** stops
|
||||
it early.
|
||||
|
||||
---
|
||||
|
||||
## 5. Running back-to-back rounds
|
||||
|
||||
When a round ends, players' clients relaunch themselves and reconnect. Once they are back, press
|
||||
**LAUNCH** again. You can change mission settings between rounds and press **Apply** first.
|
||||
|
||||
### If LAUNCH looks dead — press ↻ Re-arm
|
||||
|
||||
This is the fix for the most annoying failure this console had. If a round has ended and the LAUNCH
|
||||
button is greyed out, or pressing it does nothing:
|
||||
|
||||
> **Press ↻ Re-arm, then LAUNCH.**
|
||||
|
||||
Re-arm clears the finished round's state and re-opens the launcher **while keeping everyone who is
|
||||
already connected**. You do **not** need Stop Session / Start Session, which disconnects everybody.
|
||||
|
||||
**Why this happens:** the console used to only consider itself ready for a new round if *every*
|
||||
player from the last round came back, or if *all* of them left. In between — the normal case when
|
||||
one person closes their window or their client crashes — it got stuck, and said nothing. That is
|
||||
fixed (an explicit LAUNCH now re-arms by itself), and Re-arm is the belt-and-braces button. If you
|
||||
ever see the old behaviour, it is a bug worth reporting.
|
||||
|
||||
---
|
||||
|
||||
## 6. Known limitations — buttons that are not what they appear
|
||||
|
||||
Read this once; each of these is a control that looks live but does nothing in that mode.
|
||||
|
||||
**Mesh (LAN legacy) mode: the mission buttons do not work.** LAUNCH, END MISSION and Re-arm all log
|
||||
">> operator … sent" and are then **silently discarded** — the mesh console has no command channel.
|
||||
`Apply mission settings` is also inert (mesh reads the mission file once at startup). Mesh launches
|
||||
its rounds by itself once the pods have the mission. **If you want to control launches, use Relay
|
||||
mode.**
|
||||
|
||||
**Remote relay mode** (driving another machine's relay): LAUNCH and END MISSION **can never
|
||||
enable**, the pilot lights stay blank, and `Launch local instances` is refused. You get Apply,
|
||||
Re-arm and Stop. Treat remote mode as monitoring-plus-settings, not as a full console.
|
||||
|
||||
**Other sharp edges:**
|
||||
- **Start Session overwrites your egg file** on disk, without asking. Keep a copy of any mission you
|
||||
care about under a different name.
|
||||
- **`Apply` only pushes the six mission settings** (map, time, weather, scenario, temperature,
|
||||
length). Callsign / mech / colour / badge / patch / experience edits need **Save**. Changing the
|
||||
**seat count** mid-session is refused outright — that needs a session restart.
|
||||
- **The Colour dropdown offers colours the game cannot paint.** Only Black, Brown, Crimson, Green,
|
||||
Grey, Tan and White work; anything else silently comes out grey. The relay warns in the log.
|
||||
- **If the relay dies on its own**, `Launch local instances` stays clickable and will start clients
|
||||
against nothing. Press Start Session first.
|
||||
- **`Public host`** is used only when exporting player scripts; it does not affect what the relay
|
||||
binds or advertises.
|
||||
|
||||
## 7. Troubleshooting
|
||||
|
||||
| What you see | What it means | What to do |
|
||||
|---|---|---|
|
||||
| `egg HELD (n/N pods present)` | normal staging — waiting for the rest of the seats | wait, or press LAUNCH to start with who is here |
|
||||
| LAUNCH greyed out after a round | the launcher is still holding the last round | press **↻ Re-arm** |
|
||||
| `LAUNCH pressed but NO players are seated yet` | nobody has claimed a seat | check the players are pointed at the right address/port |
|
||||
| `LAUNCH pressed but NOT all seats have ACKed` | some pods are still taking the mission file | wait a few seconds; if one is never coming, press **Re-arm** |
|
||||
| `launch HELD -- still loading: PLAYER n` | that pod has not finished loading | wait; it force-starts after 3 minutes |
|
||||
| `*** WARNING: ... EMPTY seat(s) ... the mission will STALL` | **can be a false alarm** after the roster is trimmed | if the round starts and plays fine, ignore it |
|
||||
| `seat RECLAIMED by identity` | a player who dropped got their seat back | nothing — this is working as intended |
|
||||
| `game[...] dropped: closed` | a pod disconnected | normal at round end; mid-round it means they crashed or lost connection |
|
||||
| A player gets **ROSTER FULL** | no free seat in the current round | Re-arm between rounds so the roster re-opens, then let them join |
|
||||
| Console window stops responding | the relay stalled on a dead connection | Stop Session, Start Session; please keep the log (below) |
|
||||
| A player says they cannot connect at all | ports, or wrong address | confirm 1500/1501 TCP + 1501 UDP reach this machine |
|
||||
|
||||
### The one thing to do before restarting a session
|
||||
|
||||
**Copy `content\operator_relay.log` somewhere first.** Start Session **overwrites** it — so
|
||||
restarting to clear a problem also destroys the evidence of that problem. If something misbehaved,
|
||||
save that file and hand it over; it is timestamped line-by-line and it is what makes these bugs
|
||||
diagnosable.
|
||||
|
||||
---
|
||||
|
||||
## 8. Match reports
|
||||
|
||||
At the end of each clean round, every player's client **uploads its own match report to you
|
||||
automatically** (relay mode only). They land in `matchlogs\` named by the sender's address. You do
|
||||
not need to ask players to send anything.
|
||||
|
||||
Exceptions worth knowing: a client that **crashes mid-round** loses its report, and players on
|
||||
**solo** or **Steam** have no relay to upload through. In those cases ask them for
|
||||
`content\matchlog_*.txt` directly. Their crash log, `content\join.log`, is **never** uploaded — ask
|
||||
for it explicitly when something went wrong on their end.
|
||||
|
||||
---
|
||||
|
||||
## 9. Local test instances
|
||||
|
||||
**Launch local instances** starts game clients on this machine against your own session — useful
|
||||
for testing alone or filling a seat. **Stop games** closes them. In relay mode the session must
|
||||
already be started, or a local instance has nothing to dial.
|
||||
|
||||
---
|
||||
|
||||
## 10. Which build everyone needs
|
||||
|
||||
**All pods must run the same build as each other.** Build **4.11.554** changed what pods tell each
|
||||
other about scores, so a mixed session shows disagreeing KILLS/DEATHS columns (nothing crashes —
|
||||
the remote numbers just do not move). When you hand out a new zip, make sure everyone takes it
|
||||
before the session.
|
||||
Reference in New Issue
Block a user