Skip to content

v0.3.0 — two people, one game directory

Choose a tag to compare

@musharna musharna released this 12 Aug 03:26
· 123 commits to master since this release

0.2.0 handed another mod the responder. This one stops two sessions using it from erasing each other.

Two developers can now point the harness at the same tModLoader save directory and work at the same time. Before, one of them silently destroyed the other's work — and would have gone on doing so, because a suite that only ever runs one client cannot see it.

Per-player artifact naming — a client's ANSWERS carry a token

Requests could already be told apart (shot@n43n), but everything a client wrote back was namespaced by side only. Two clients shared one heartbeat, one diag dump, one capture reply and one shot drop box, so B's answer overwrote A's and A read B's as its own. Each client's artifacts now carry a token derived from its player name.

Behaviour change: a client that has not loaded a character has an empty name, matches no target, and can no longer be asked anything. Use heartbeat, which reads off disk and needs no cooperation from the game.

The trigger is CLAIMED, not written

One file, and os.replace wrote into it unconditionally — so B's request flattened A's while A's game was still polling for it, and A waited out its whole timeout being told the game might not be polling.

os.link onto the trigger name has the same atomicity plus an EEXIST refusal, so the loser of a race learns it lost instead of winning by erasure. A blocked caller retries out of its own budget rather than a fresh one, and the two exhaustion cases say different things: somebody else holds the slot, here is whose and how old versus you hold it, your time ran out, the game may still answer.

Everything that used to delete the shared trigger now releases it — leaving alone exactly what the mod leaves alone — so a launch or a stop no longer takes another session's in-flight request. Both sides state the rule identically, down to reading the same bytes the same way: C#'s File.ReadAllText substitutes U+FFFD rather than throwing, so this side does too.

A save directory whose filesystem cannot support an exclusive claim is refused at startup, naming the reason, rather than falling back to the write that loses requests.

What two real clients proved that reading could not

Both requests answered, 1.2s and 1.3s, where previously one timed out at 120s. Eight concurrent OS processes contend for the slot in the suite; exactly one wins.

Several of the sharpest defects here were regressions this work introduced, found only because it was run rather than argued.

Known, and documented rather than quietly carried

Two simultaneous capture requests still collide on one filename; diag and shot can spend up to ~2x the caller's stated budget; and a server_client session's server side carries no address, so two of those remain indistinguishable on disk. All three need a mod-side change — see docs/MOD_CONTRACT.md.

Full changelog: CHANGELOG.md · v0.2.0...v0.3.0