Something went wrong. Try again.
Backend environment for match hosting for lance.blue
Something went wrong. Try again.
Shell
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138#!/usr/bin/env bash# 40-render-config.sh turns the templates into the files Suramadu actually# reads. A leftover @PLACEHOLDER@ there is a match where the client tries to log# in under a literal "@PLAYER_NAME@" and sits in the lobby until it times out -# easy to catch here, hard to diagnose in a running match.set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"TMP="$(mktemp -d)"trap 'rm -rf "$TMP"' EXIT
mkdir -p "$TMP/run" "$TMP/mm/mmconf" "$TMP/home/config" "$TMP/home/instrument"echo 'ARENA_CLIENT_SLOT=TraineeA' > "$TMP/run/players.env"printf 'did:plc:aaa\tTraineeA\n' > "$TMP/run/clients.tsv"# Staged so that "no javaagent" below is the switch's doing and not a missing# file - 40-render-config.sh refuses to render if ARENA_LAZY_PNG is set and the# jar is absent. Its content is never read here, only its presence.touch "$TMP/home/instrument/lazy-png-agent.jar"
# The repo groups templates by component; the image collects them into one# config/ directory. Stage that layout so the test exercises what ships.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"# Staged, not served from here: the script copies this into the run directory# so watch/results.sh can write the result into it mid-match.cp -r "$ROOT/suramadu/web" "$TMP/home/web"
ARENA_HOME="$TMP/home" ARENA_RUN="$TMP/run" MM_HOME="$TMP/mm" \ JAVA="/opt/arena/jre/bin/java" ARENA_PORT_WS=9080 ARENA_PORT_MM=9850 \ bash "$ROOT/container/init/40-render-config.sh" >/dev/null 2>&1 || { echo "FAIL: 40-render-config.sh exited non-zero"; exit 1; }
fail=0check() { # <description> <test-command...> if "${@:2}"; then echo "ok $1"; else echo "FAIL $1"; fail=1; fi}
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"check "game port substituted" grep -q 'port 9850' "$CFG"check "web port substituted" grep -q 'http.port=9080' "$JETTY"check "no placeholder survives" bash -c "! grep -q '@[A-Z_]*@' '$CFG' '$JETTY'"check "the client app is at the seat's DID" bash -c "jq -re '.\"/did:plc:aaa\".path' '$CFG' | grep -qx '/did:plc:aaa'"check "the template app does not survive" bash -c "! jq -e '.\"/megamek\"' '$CFG' >/dev/null"check "webFolder points at the web dir" bash -c "jq -re '.\"/did:plc:aaa\".webFolder' '$CFG' | grep -q '/web$'"# Not the image's copy. results.sh writes the result into this directory when# the game is decided, and the image tree is read-only to the user that runs.check "the web folder is staged as writable" bash -c "test -w '$TMP/run/web' && test -f '$TMP/run/web/report.js'"check "webFolder is the staged copy" bash -c "jq -re '.\"/did:plc:aaa\".webFolder' '$CFG' | grep -qx '$TMP/run/web'"check "decorator theme colours substituted" grep -q 'org.webswing.window.theme.accentColor=#4c8dff' "$CFG"check "clientsettings seeded into mmconf" test -f "$SETTINGS"# On by default - it is shipped rendering behaviour, not instrumentation.# tests/shell/test-profiling.sh owns the switch itself.check "lazy-png-agent on by default" bash -c "jq -re '.\"/did:plc:aaa\".swingConfig.vmArgs' '$CFG' | grep -q -- '-javaagent:.*/instrument/lazy-png-agent.jar'"# The tick is the largest server-side term in a warm input-to-paint, so it is a# knob rather than a rebuild. 16 is what PERFORMANCE.md measured as better.check "draw delay defaults to 16ms" bash -c "jq -re '.\"/did:plc:aaa\".swingConfig.vmArgs' '$CFG' | grep -q -- '-Dwebswing.drawDelayMs=16'"# All three of MegaMek's sounds, because only SoundMuteChat is muted upstream:# dropping either turn flag brings the bing back on every round.check "all three sounds muted" bash -c "test \"\$(grep -c 'name=\"SoundMute[A-Za-z]*\" value=\"true\"' '$SETTINGS')\" -eq 3"# Upstream has no setDefault for MoveEnvelope, so dropping the line does not# restore a sensible default - it turns movement ranges off for everyone.check "movement envelope on" grep -q 'name="MoveEnvelope" value="true"' "$SETTINGS"
# --- every human seat renders a client path at /<did>, one segment -----------mkdir -p "$TMP/run2" "$TMP/mm2/mmconf"echo 'ARENA_CLIENT_SLOT=@one.example' > "$TMP/run2/players.env"printf 'did:plc:aaa\t@one.example\ndid:plc:bbb\t@two.example\n' > "$TMP/run2/clients.tsv"
ARENA_HOME="$TMP/home" ARENA_RUN="$TMP/run2" MM_HOME="$TMP/mm2" \ JAVA="/opt/arena/jre/bin/java" ARENA_PORT_WS=9080 ARENA_PORT_MM=9850 \ bash "$ROOT/container/init/40-render-config.sh" >/dev/null 2>&1 || { echo "FAIL: 40-render-config.sh exited non-zero with two human seats"; exit 1; }
CFG2="$TMP/run2/suramadu/suramadu.config"
check "two-seat config is valid JSON" jq -e . "$CFG2"check "each seat has its own app" bash -c "jq -re '.\"/did:plc:aaa\".path' '$CFG2' | grep -qx '/did:plc:aaa' && jq -re '.\"/did:plc:bbb\".path' '$CFG2' | grep -qx '/did:plc:bbb'"check "each app logs in as its own seat" bash -c "jq -re '.\"/did:plc:aaa\".swingConfig.launcherConfig.args' '$CFG2' | grep -q -- '-player_name @one.example$' && jq -re '.\"/did:plc:bbb\".swingConfig.launcherConfig.args' '$CFG2' | grep -q -- '-player_name @two.example$'"# The clones must carry the template's tuning by construction, not a copy# that can drift: everything but path, name and login has to be identical.check "the clones keep the client tuning" bash -c "jq -e '.\"/did:plc:aaa\".swingConfig.vmArgs == .\"/did:plc:bbb\".swingConfig.vmArgs' '$CFG2' >/dev/null"# Both halves of taking a seat back, on every seat. Suramadu tests maxClients# before it tries to reconnect, so a 1 there refuses a player their own match# while the frozen tab still holds the socket; CONTINUE_FOR_USER is what makes# the new connection resolve to the instance already running rather than start# a second client under the same login. Either one alone is the wrong# behaviour, so they are checked together.check "every seat allows a takeover" bash -c "jq -e 'to_entries | map(select(.key | startswith(\"/did:\"))) | length == 2 and all(.[]; .value.maxClients < 0 and .value.sessionMode == \"CONTINUE_FOR_USER\")' '$CFG2' >/dev/null"check "and the template app is gone" bash -c "! jq -e '.\"/megamek\"' '$CFG2' >/dev/null"check "no placeholder survives either" bash -c "! grep -q '@[A-Z_]*@' '$CFG2'"
# An empty client list is a container nobody can reach; the render dies# loudly instead of serving the template's login to whoever finds it.mkdir -p "$TMP/run3" "$TMP/mm3/mmconf"echo 'ARENA_CLIENT_SLOT=TraineeA' > "$TMP/run3/players.env": > "$TMP/run3/clients.tsv"if ARENA_HOME="$TMP/home" ARENA_RUN="$TMP/run3" MM_HOME="$TMP/mm3" \ JAVA="/opt/arena/jre/bin/java" ARENA_PORT_WS=9080 ARENA_PORT_MM=9850 \ bash "$ROOT/container/init/40-render-config.sh" >/dev/null 2>&1; then echo "FAIL an empty client list should refuse to render"; fail=1else echo "ok an empty client list refuses to render"fi# Upstream defaults FovDarken to true, so dropping the line brings LOS shading -# and with it a hex-cache clear and full recomposite per selection - back.check "fov darkening off" grep -q 'name="FovDarken" value="false"' "$SETTINGS"# WeaponSortOrder.valueOf throws on a name it does not know, and MegaMek reads# this one while building every Entity, so a typo here is a client that dies on# 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 Eopts = {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"