Conversation
BOIII is a second client for Black Ops III, so it is added next to t7x rather than in place of it: PLUTAINER_GAME=boiii. The two share the gamefiles mount, the zone/ engine config dir, the Dss0/t7-server-config seed bundle and the default port, which makes switching a server between them one line of compose. Staging is written once as cod_stage_bo3 and both hooks call it; only the binary and its launch flags differ. Verified end to end on a fresh volume: healthy, map loaded, rcon-cli answering, config symlinks fanned out into zone/, rcon_password written from the environment. usermaps/ is mirrored into the volume when the mount has one, which is how Workshop maps reach either server. It is tested for rather than passed straight to link_dir_contents, since most installs have none and the helper would warn about it on every start. Verified by hosting a Workshop map through the container. BOIII launches with -quiet-crash, so a crash cannot stop on a dialog nothing in a container can dismiss, and -watchdog, which reports a hung script VM to the log instead of leaving a server that holds its port and answers nothing. PLUTAINER_AUTO_UPDATE=false does more here than elsewhere. BOIII ships an in-game updater as well as the binary Plutainer downloads, so the variable has to turn off both halves - it skips the wget and passes -noupdate - or a build placed in app/runtime/gamefiles/boiii.exe by hand is replaced at the next start. That is not hypothetical: the build under test is a local one. The engine field is renamed from `alterware` to `t7x`, and cod_update_alterware and cod_launch_alterware with it. T7x is no longer an Alterware project and nothing else in the table used that engine, so the name resolved to exactly one game and misdescribed it. Nothing about hook resolution changes: t7x's game tag, base game and engine were already all "t7x". Known limitation, not fixed here: the boiii.exe published at r2.ezz.lol cannot cold-start as a dedicated server. It gates a launcher-UI file check on a file a server never fetches, so a fresh container dies with "BOIII needs an active internet connection for the first time you launch it" however good the connection is. Fixed upstream but not yet published, so a first run currently needs a binary supplied by hand.
BOIII shares the Dss0/t7-server-config bundle with t7x, but the bundle is zone/*.cfg plus a t7x/ tree - bots.txt, gamesettings, lobby scripts - and seed_configs places everything that is not a lifted cfg under the asset root. So a clean BOIII volume came up with a t7x/ directory in its game files that nothing ever opens: BOIII reads boiii/, which it unpacks from its own binary. COD_SEED_CFG_ONLY=true takes the configs and leaves the rest, so the bundle can be shared without the parts that are not shared. Verified on a clean volume: configs seeded, rcon_password written from the environment, config symlinks fanned into zone/, healthy in 30 s, and no t7x/.
BOIII hosts multiplayer and zombies only, so server_cp.cfg is a config for a mode it cannot run - and a seeded config is a suggestion that it can. COD_SEED_SKIP_CFGS names the cfgs a shared bundle carries that a given game has no use for; t7x still gets all three. Verified on a clean volume: configs/ holds server.cfg and server_zm.cfg only.
BOIII replaces t7x rather than joining it: Black Ops III has one supported
client here now. `PLUTAINER_GAME=t7x` is retired, `boiii` takes its place, and
the engine field is `ezz` after the client's owner - the Alterware equivalent -
with `Ezz BOIII` as the client name.
Nothing about a volume changes. BOIII reads the same gamefiles mount, the same
app/configs/, and the same zone/ config directory, so moving a server across is
one line of compose. A t7x tag is caught before the "unknown game" message and
told exactly that, because a tag that used to work is not the same as one that
never did and "unknown" sends someone hunting for a typo.
cod_stage_boiii symlinks the BO3 server files, mirrors zone/ as a real
directory, and mirrors usermaps/ when the mount has one -
which is how Workshop maps reach the server. usermaps/ is
tested for rather than passed to link_dir_contents, since
most installs have none and it would warn on every start.
cod_update_boiii wget -N from r2.ezz.lol, so nothing is re-downloaded
unless upstream is newer.
cod_launch_boiii -headless -dedicated -quiet-crash -watchdog, +map_rotate.
-headless is what removes the X dependency, exactly as it
did for t7x; -quiet-crash keeps a crash off a dialog
nothing in a container can dismiss; -watchdog reports a
wedged script VM instead of leaving a server that holds
its port and answers nothing.
PLUTAINER_AUTO_UPDATE=false has to disable two updaters here, not one: BOIII
ships an in-game updater as well as the binary Plutainer downloads, so the
variable skips the wget and passes -noupdate. Without the second half a
hand-built boiii.exe in the volume is replaced at the next start, which is not
hypothetical - that is how it is developed.
The seed bundle is re-vendored as seed-configs/boiii from the same upstream
commit, carrying the Dss0 zone/ configs and nothing else. The bundle's t7x/
tree is another client's data directory, and server_cp.cfg is a config for a
mode BOIII cannot host - a seeded config is a suggestion that the mode works.
Both are dropped by tools/refresh-seeds.sh rather than filtered at runtime, so
what ships is what is in the repo.
Verified on a clean volume holding nothing but boiii.exe: fresh v2 layout,
configs seeded (server.cfg and server_zm.cfg only), rcon_password written from
the environment, config symlinks fanned into zone/, healthy in 30 s, rcon-cli
answering, default map zm_tomb, and a Workshop map hosted from usermaps/.
After an ERR_DROP a BO3 server can shut the game down, fail to rebuild its lobby, and never host anything again - while getinfo keeps answering with the LAST map's name and sv_running reads 0. Measured on boiii before the rotation recovery landed: the port answered, the reply looked right, and the server was dead. The health check accepted it, so Docker called the container healthy for as long as it sat there. Where the engine reports sv_running, believe it over the name. Engines that do not report it are unaffected - the key is simply absent and the map-name test decides as before. Verified three ways against a stub that replies exactly as a stranded server does, plus the live container: sv_running 0, map name present exit 1 "answered but sv_running is 0" sv_running 1, map name present exit 0 real boiii server exit 0
A clean volume with auto-update at its default fetches
r2.ezz.lol/boiii/boiii.exe and produces a container that is Up, silent and
useless. That build has two independent faults, both fixed in the client's
source and neither in the artifact being served:
* the launcher-UI check runs before the client/server split, so a server
fails a check for a file only a client downloads and exits rc=1 blaming
the network, which is not the cause.
* the headless console calls AllocConsole() at static-init. Under Wine with
no display that starts conhost.exe and the process blocks on its pipe:
one thread, 0% CPU, 36MB RSS, wchan pipe_read, no UDP socket, and nothing
in `docker logs` for the container's whole lifetime.
The obvious workaround does not work. The first check is a bare file_exists
on %LOCALAPPDATA%/boiii/data/launcher/main.html, so an empty stub clears it -
verified - and the run then lands on the console hang, which no file can
satisfy.
The second fault is why this refuses rather than warns. A warning would be
followed by exactly the silent container described above, which is the least
debuggable thing this image can produce; hold_indefinitely at least says why.
Pinned by the sha256 of the known-BAD artifact rather than of a known-good
one, so the day anything else is published the check stops firing on its own
and no release of this image is needed to unblock it.
Measured both directions on 10.10.1.22: published binary on a clean volume
refuses with the explanation and holds, restarts=0; a build carrying both
fixes is untouched by the check and reaches healthy, hosting zm_tomb. Also
fills the "No cod_validate_* hook for: boiii" warning, which was real.
parse_packet split the response into type and payload at the first newline.
That is right for query replies, which are framed "statusResponse\n\key\value",
and wrong for rcon replies, which are framed "print <text>" with the first line
of the answer on the same line as the token. The token therefore swallowed it.
How much that cost depended on the command, and the worst case was silent:
status lost its "map: <name>" banner - cosmetic
<dvar> lost the value line, which is the entire answer. Querying a
dvar printed only " Domain is any text"
<typo> lost 'Unknown command "..."' completely, so rcon-cli printed
nothing at all and a mistyped command was indistinguishable
from a server that had stopped answering
Split on the first whitespace of any kind instead, since the separator is
framing rather than payload and which one appears varies by reply. A reply with
no separator at all - t5 zombies answers an unexpected connectionless packet
with a bare "disconnect" - still parses as "type, empty body".
This was never BOIII-specific. Measured on 10.10.1.22 across t6zm, iw5mp, iw4x,
t4mp, cod4x and boiii: all six now print the first line that was being dropped
("map: zm_buried", "hostname: My Server Name"), and all six still pass the
health check afterwards - including iw5, which answers getinfo rather than
getstatus, and so exercises the fallback path the same parser backs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Black Ops III is served by Ezz BOIII now, and
t7xis removed rather than kept alongside it.PLUTAINER_GAME=boiii, engine fieldezzafter the client's owner (the Alterware equivalent), client nameEzz BOIII.Breaking, but nothing about a volume changes. BOIII reads the same gamefiles mount, the same
app/configs/and the samezone/config directory, so moving a server across is one line of compose. At7xtag is caught before the "unknown game" message and told exactly that — a tag that used to work is not the same as one that never did, and "unknown" sends someone hunting for a typo:The hooks
cod_stage_boiiizone/as a real directory, and mirrorsusermaps/when the mount has one — which is how Workshop maps reach the server.usermaps/is tested for rather than handed tolink_dir_contents, since most installs have none and it would warn on every start.cod_update_boiiiwget -Nfromr2.ezz.lol, so nothing is re-downloaded unless upstream is newer.cod_launch_boiii-headless -dedicated -quiet-crash -watchdog, plus+map_rotate.-headlessis what removes the X dependency, exactly as it did for t7x;-quiet-crashkeeps a crash off a dialog nothing in a container can dismiss;-watchdogreports a wedged script VM instead of leaving a server that holds its port and answers nothing.PLUTAINER_AUTO_UPDATE=falsedisables two updaters here, not one. BOIII ships an in-game updater as well as the binary Plutainer downloads, so the variable skips thewgetand passes-noupdate. Without the second half, a hand-builtboiii.exein the volume is replaced at the next start — not hypothetical, that is how it is developed.Seeds
Re-vendored as
seed-configs/boiiifrom the same upstream commit, carrying the Dss0zone/configs and nothing else:t7x/tree is another client's data directory, and BOIII unpacks its ownboiii/from the binary;server_cp.cfgis a config for a mode BOIII cannot host, and a seeded config is a suggestion that the mode works.Both are dropped by
tools/refresh-seeds.shrather than filtered at runtime, so what ships is what is in the repo.Verified
On a clean volume holding nothing but
boiii.exe:configs/holdsserver.cfgandserver_zm.cfgonly,zone/*.cfgsymlink back toconfigs/,rcon_passwordcomes from the environment, five logs are symlinked intoapp/logs/,rcon-cli statusanswers, default mapzm_tomb, and a Workshop map fromusermaps/hosts and reports healthy. A real client joined, played and cycled maps against this build.Known limitation, not fixed here
The
boiii.exepublished atr2.ezz.lolcannot cold-start a dedicated server: it gates a launcher-UI file check on a file a server never downloads, so a fresh container dies with "BOIII needs an active internet connection for the first time you launch it" however good the connection is. Fixed upstream but not yet published, so a first run currently needs a binary supplied by hand. Worth holding the merge for, or worth a caveat in the docs — your call.