A postfix on vanilla Container.Save re-anchors the session content
hash after every save, covering host-side vanilla drags, in-chest
moves, loans and sorts that persist outside any Tx commit (proven by
decompilation: inventory.m_onChanged auto-saves for the owner).
0.6.27 (save-anchored session hash)
Every direct container save re-anchors the session content hash, so
host-side crafting loans, sorts, trash and restocks no longer trip
FOREIGN-WRITE quarantine on the next remote op.
0.6.26 (live-commit hash re-anchor)
Host live commits re-anchor the session content hash after saving, so
the next remote job no longer reads our own save as a foreign write
(fixes FOREIGN-WRITE quarantine bricking host-mode chests).
0.6.25 (host viewer refresh)
Hosts holding a managed chest open now poll-refresh like remote
viewers (the manager skip assumed RAM==truth, false when remotes
commit via session/ZDO). Dedicated/single-player unaffected.
0.6.24 (delta pricing + pocket refund)
Upgrades charge target-minus-current per resource (no double-paying
overlap); unreadable current recipe degrades to full price, never free.
Previous-tier-unique mats (e.g. iron for reinforced-to-blackmetal) are
refunded to the upgrader's pockets in the upgrade response (refund-once
guarded, ground fallback when full).
0.6.23 (server dims follow chest tier)
Server chest sessions grow their working inventory from the tier prefab
when a tier marker is present — moves/adds into upgrade-added slots are
accepted instead of rejected.
0.6.22 (keep native colliders on tier-matching chests)
Prefab/tier-matching chests no longer get a visual overlay (native look,
dims and colliders are already correct) — fixes upgraded chests becoming
unopenable (no hover prompt).
0.6.21 (upgrade look for non-wood chests)
In-place upgraded non-wood chests take the target-tier look via the
legacy-visual path (prefab/marker mismatch branch); prefab-matching
chests keep the compact path byte-identical.
0.6.20 (natural chest tiers)
Tier resolution: explicit marker always wins; unmarked chests resolve by
prefab (natural reinforced/blackmetal/grausten are tiers 1/2/3) — upgrade
buttons and server validation work for them.
Mediated upgrade gather registers staged mats (NoteUpgradeStaged) so
the ahead-stage ledger holds them for the second press instead of
returning them immediately (fetch-return loop fixed).
0.6.17 (server marker upgrade + split costs)
Server executes UpgradeRequest in place: pure tier-path check, costs
verified + deducted from chest, marker key set, receipt=self. No
structural replace on dedicated; ghost path kept for host/legacy.
Split costs: player covers shortfalls via claimed snapshots (shared
Add-claim dedup, removed only on terminal Accept); server deducts the
remainder strictly. Full chests upgrade without deposit space.
UpgradeRequest carries player-cost items (tolerant trailing wire field);
host deposits first, then ghost as before.
Client applies marker + resize + effects on self-receipt.
0.6.16 (feed flight visuals)
Autofeed renders local flight visuals (chest to animal mouth) on
successful feed; watchers already covered by server broadcast.
0.6.15 (upgrade-missing diagnostics)
Upgrade missing-resources check logs need/have/invStacks per requirement
(diagnoses stale-viewer vs genuinely-short materials).
0.6.14 (feeder-anchored range)
Range enforced against the server-resolved actor position (+15m lag
slack); client-stamped position drives visuals only (fixes autofeed
pos-mismatch storm while moving).
Feeder Takes carry the hungry animal ZDO (FeederId, tolerant trailing
wire field); range anchors at the server-verified animal
(Tameable prefab + AutoFeedRange), player position irrelevant for AfK
farms. Identity/privacy/wards unchanged.
0.6.13 (server-mediated chest destroy)
Hammer-remove of managed chests goes through the server: spill from
server truth (Take-shaped bodies, zero new codec), ZDO delete,
session drop, txId-keyed tombstone replays, idempotent gone-handling.
Guards: eligibility, decode, m_canBeRemoved, privacy+nonempty, full
access (range+slack/wards/creator), sender binding + compat, claim-
verified delete. Deny/timeout keep the chest with a message.
OnRequest gate order mirrors production (binding, replay, transient,
ring-corrupt, unsupported, access, virgin, owner, revalidation,
quarantine, flagged, floor, fence). AnswerQuery consults the transient
set; undecodable requests answer UnknownTx.
Response sender authenticated (server peer only); range checked against
server-resolved position with 8m slack; guard/ward faults fail closed;
prune throttled (10s); foreign-write refusals go through the durable
quarantine matrix.
Virgin birth converges ownership to 0 (verified by read-back): new chests
servable immediately, broadcast open-routing intact, no reassignment.
Ownership guards allow uid==0 everywhere (relinquish is never a takeover).
Content-hash (FNV-1a-64) revalidation replaces revision compare: benign
bumps (lid toggles) pass, real s_items divergence quarantines. Baselines
stamped on every server save + at session creation.
Virgin attempt runs before the owner gate (superseded in 0.6.11 by
clear-to-0: bypass fields removed).
Bounded trust via content hash (supersedes the revision-stamp scheme:
ServerZdoSet/VirginRev removed in 0.6.11).
0.6.9 (virgin veto refinement)
Virgin veto refined: only committed ring entries (Accepted/Partial/
Duplicate via TxRing.IsCommittedStatus) block; Rejected-only rings and
floor keys no longer self-poison virgin chests. Corrupt rings still veto.
0.6.8 (virgin init, submit truth, honest Take diagnostics)
Virgin-chest initialization: ownerless s_items-null with no ring/floor
history materializes a vanilla-empty blob and serves (new chests usable);
any history/live owner keeps the quarantine.
SEND line carries items=/expected= submit truth (open AddBatch arity case).
Answer() logs takes= blob count for Take ops instead of a fake int sum.
Server broadcasts TxFlights after Add/AddBatch/TakeBatch commits: watchers
see other players' quick-stack icons (same packet, ZDO-sourced).
Ownership guards pass through no-op changes (owner==requested) silently;
real changes still blocked + counted.
Arity diagnostics: client logs expected/bodyLen/bodyFirst on add-body
mismatch; server logs op/acceptedTotal per response (open AddBatch case).
0.6.6 (Move/Sort served, settle removed)
Server manager serves Move/Sort (pure-inventory shared Execute paths).
Settle-HOLD fully removed: requests execute immediately (strict parity with
the production Takeover path; the live-owner gate already excludes
concurrent writers before execution). Zero open-delay by construction.
Single-writer owner gate (code shipped in 0.6.3, documented here): serve
only owner==0/self/dead-peer, else ephemeral Rejected; ownership never mutated.
0.6.5 (ZDO-level ward evaluation)
Server access gate evaluates wards from ZDO data (sector scan, s_enabled,
prefab m_radius, s_creator + pu_id list); no covering ward allows exactly
like vanilla. Ward-opted fail-closed removed.
0.6.4 (server transport framing fix)
KindTx byte on server request/response outer frames (was misparsed as kind,
all tx traffic silently dropped; ping/pong unaffected).
0.6.3 (unreleased: option-B server manager, slices 1-3)
Slice 1 (transport): instance-independent global RPCs BestAutoSort_TxServerRequest/Response (ZDOID.None dispatch, Hello precedent); client ping every 60s, first pong logged unconditionally. Zero GameObjects needed.
Slice 2 (sessions + codec): ZDO-keyed ServerChestSession (dims from prefab Container, quarantine-first); decode/encode via vanilla Inventory.Load/Save (byte-identical by construction); zdo.Set auto-bumps revision; restart recovery via durable ring/floor reseed.
Slice 3 (manager): ZDO-only Take/Add(+Batch) through the SHARED ExecuteCall (rule/reserve ZDO overloads, ExecZdo context); shared idempotency (Processed/ProcOrder/Floor), durable ring/floor, quarantine matrix, spoof/binding gates mirrored; access mirrors ChestAuthority.CanUse (ZDO/prefab sources); new sessions HOLD 8s (rev-re-armed) with deferred answers; unsupported ops -> persisted Rejected; legacy chests never answered (silent drop).
Residuals: Move/Sort/Upgrade/SetRule on dedicated stay Rejected; ward-opted chests fail closed; slice-4 freshness barrier pending (settle narrows, does not close, the proven ClaimOwnership race); m_privacy read from prefab.
Server-only discovery sweep every 15s (PumpAuthoritySweep): live managed chests missing from States (Awake edge never fired — late zone activation on dedicated servers) get the exact Awake registration (RPCs + state + EnsureOnAwake adoption). Adoption stays metadata-only + quarantine-first; the sweep path skips the verified-init materialization (fromSweep, timer paths NEVER materialize holds literally); unconditional log on change + 5-min heartbeat (discovery sweep found=/managed=/registered=).
Known residual (proven by decompilation, fix deferred): ClaimOwnership is a local flag flip with no flush handshake — the server is not guaranteed the ex-owner's latest state at adoption. Mitigations active: quarantine on null/invalid, owner-gated writes, SetOwnerInternal guard; freshness handshake (ForceSendZDO + DataRevision compare) is the planned second stage.
0.6.2-DIAG (temporary dedicated-server silence diagnostics, revert after diagnosis)
Temporary verbose probes only, no behavior change: [ChestTX] config: startup dump (TxVerbose/mode/effective), authority awake probe at EnsureOnAwake entry, container awake: <name> at OnContainerAwake entry. Purpose: prove whether Container.Awake/adoption runs at all on the dedicated server (live symptom: client SENDs, zero server ChestTX lines, every op Indeterminate). Revert before any release.
0.6.1 (experimental: full review-loop fixes on 0.6.x)
Review-loop hardening: dead authority seams wired into production (remote-mutation ban, stale-route drop, structural single rule); quarantine gates on craft consume/loan and SortLocal paths (incl. priority loan); manifest 0.6.x packaging consistent; README experimental banner + wire/ops table; threat model stated; verification table narrowed to seam-confirmed with wiring-assumed + transient C15 row.
Residuals unchanged from 0.6.0 (live multi-peer traces outstanding).
0.6.0 (experimental: server-authoritative chests, invariant NOT claimed pending live multi-peer verification)
Server-authority migration (waves 1-3): canonical eligible-chest policy, authority UID, ServerAuthority (default) + LegacyDistributed modes with Hello/mode compat; source guards (open/stack/take-all without SetOwner, ReleaseNearbyZDOS exclusion, RPC_ZDOData boundary, ClaimOwnership block); server-only metadata adoption with quarantine-first; IsAuthorityManager routing for every mutation path with stale-route drop; host queue-serialized; remote Upgrade fail-closed; timer null-heals disabled in authority mode; ForceSendZDO documented as viewer-propagation hint.
Server-mediated remote chest upgrade (TxOp.UpgradeRequest): client sends a REQUEST (source ZDOID + authenticated sender + durable client nonce = fencing txId counter half + tier); the server executes the ghost protocol in its per-chest manager queue (pure core TxUpgradeManager/TxUpgradeGate in BestAutoSort.TxCore/TxUpgradeOp.cs, production host in src/Tx/TxRemoteUpgrade.cs). Spawn -> synchronous reverse-link write -> non-destructive copy -> validate Ready -> one-way pointer switch -> receipt -> idempotent retire; same op never spawns twice (record gate + reverse-link adoption); different ops queue behind the prepared head.
Ingredients pre-deposited in the source chest via normal ChestTX Takes; the op never charges the client inventory; spend recorded at-most-once, refunds only as an op-keyed entitlement via the idempotent ClaimRefund step. Old TxOp.Upgrade frames refused for managed chests in authority mode; host-local upgrades route through the same queue. UI closes the old view, shows locked-pending, and opens the new view only on a validated receipt; timeouts re-query the same op.
Residuals (documented in docs/remote-upgrade-mediated.md): hard-kill mint-link micro-window => unlinked ghost kept standing + quarantine + manual reconcile; copy-window duplication safe-direction; totals-only ring replay loses the receipt (manual check, never auto-open).
0.6.x-Dedicated-Test wave 1 (experimental slice, invariant NOT claimed)
Source guards for eligible chests only: access-checked open grant without SetOwner (lease still denies); request-stack/take-all denied regardless of IsShared; TakeAllResponse claim suppressed; ZDO.SetOwner/SetOwnerInternal ownership backstop (toward-authority only); mod/client ClaimOwnership blocked with diagnosis; ReleaseNearbyZDOS redistribution exclusion via verified transpiler (2/2 sites, fail-closed SetOwner backstop on IL change); GUI/TakeAll/StackAll view-only for non-owners outside the shared path.
Quick-stack duplication race fix (also on branch): ExecuteAdd credits clamped to the live source stack.
0.4.0
Legacy chests no longer shrink a larger runtime inventory applied by other mods (final = max(current, template), issue #8).
Eating food from a shared chest as non-owner no longer pulls the whole stack into inventory (take 1 unit, issue #6).
0.3.2
Staged-stock return no longer breaks crafting (craft stages untracked) and no longer loops on upgrades (recent-upgrade keep window).
0.3.1
Deferred building pipelines the next set (no more multi-click starvation); unused staged stock returns to chests on selection change, hammer put-away, or late landing.
0.3.0
Absolute tx idempotency (no re-apply after handoff), retry-or-loud client, no-void compensation.
In-flight Add deduplication, drag/grid-inventory fixes, monotonic viewer, GearSlots tag handling.
Verified against Valheim 1.0.12.
0.1.6
Wire protocol v2 (client-stamped actor position), build commit hash baked into the DLL.
0.1.5
Quick-stack outcome visibility and skip diagnostics, build tag in startup log.
0.1.4
Owner quick-stack through the tx queue (broadcasts for all), quick-stack entry logging.
0.1.3
Flight visuals broadcast (TxFlights) + manager-side play for remote batches.
0.1.2
Fix response body framing (nested package was read flat: Adds silently applied 0, Takes crashed) and short presence/query bodies (heartbeats died, lid flapped).
0.1.1
Wire protocol fix (ProtoVersion header) + version gate: mixed 0.1.0/0.1.1 peers are rejected loudly instead of silent garbage.
0.1.0
Rebrand to BestAutoSort (dev.maks2204.bestautosort): new GUID, name, version, own plugin folder.
Fresh config dev.maks2204.bestautosort.cfg, no legacy imports.
Console command renamed to bestautosort.
Full storage feature set (quick-stack, restock, rules, crafting/fuel from chests,
autofeed, shared chests, upgrades, sort, trash), no ModCore dependency.
Own save-data keys, no compatibility with other storage mods by design.
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.