Pointer-based per-painter history of the canonical maps with copy-on-write
sharing (only maps touched by a stroke are cloned). A stroke (press..release)
is one step; a Load resets the history to the loaded maps as the sole baseline.
Network sync replicates undo/redo as tiny checkpoint/restore commands instead
of broadcasting full map snapshots: every peer runs the same COW history keyed
by owner-assigned checkpoint ids and reproduces restores locally. A peer that
cannot reproduce a target (undo across its join boundary) requests a targeted
snapshot from the owner as fallback.
Hardening: ordered send FIFO keeps stamps + checkpoints + restores in
production order (a marker never overtakes the stamps it finalizes); stamp
overflow carries over to the next frame instead of being decimated, so remote
content matches the owner; remote always inserts the checkpoint id to stay
1:1 aligned. Owner-authoritative snapshot fallback replaces the silent
host no-op. Adds a logSync toggle (default on) tracing when each
checkpoint/restore/snapshot is sent, applied, or falls back.
Also includes paintings-panel layout tuning in PaintHudPanel.prefab
(inspector edits).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Persistence rework:
- Drop auto-load on setup and auto-save on teardown; the painter now always
seeds fresh from the material.
- Named slots under MeshPaint/Saved/<name>.paint via the existing CHMP
machinery: SavePaintAs / LoadPaintByName / DeletePaint / ListSavedPaintings /
PaintExists. Slot files carry the display name in the header.
- Remove the obsolete persistence partials (PlayerCharacterController,
PlayerPoseController, ThirdPersonOrbitInput) and their references.
Paintings HUD panel:
- Authored Panel_Paintings + Save/Load/Delete dialogs into PaintHudPanel.prefab
(TMP throughout); PaintHud.Library.cs is now a pure binder that clones the
entry template per slot.
- Save dialog blocks empty/existing names; Load and Delete confirm first.
- Fix dialog z-order: open dialogs SetAsLastSibling so a dragged panel can no
longer cover them.
- Lib_ListContent horizontal offsets corrected to 0 (was clipping entry labels
out of the masked viewport).
Multiplayer:
- Loading a painting re-syncs to all peers via the snapshot broadcast path
(BroadcastLoadedPaint), covering later late-joiners for free.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replicate MeshPainter.emissionIntensity (a global material multiplier, not
baked into the paint maps) as an owner-authoritative NetworkVariable<float> on
PlayerPaintSync. The owner publishes on change; remotes subscribe, seed the
current value on spawn (covers late-join) and let MeshPainter's per-frame
RefreshEmissionColor push it onto the material.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Send the accumulated paint maps as a one-time compressed baseline to
clients that join after painting has happened, instead of streaming raw
pixels.
- MeshPainter.Net.cs: TryCaptureSnapshot reads the canonical maps via
ReadCanonical and Deflate-compresses them; ApplyPaintSnapshot inflates
and re-seeds the backend through the same island/dilation path as
SwitchBackend; IsPaintReady gate.
- PlayerPaintSync.cs: tag late-join transfer and live stamps with
sequence numbers. Joiner requests the snapshot on spawn; server chunks
and throttles it (1200 B/chunk, 8 chunks/frame) to stay under the
6144-byte payload cap and the reliable window. Joiner buffers live
stamps during transfer and replays only those with seq > capturedSeq,
so no stroke is lost or applied twice across the join window. Disable
persistence on network-driven remotes to avoid shared-save-key clobber.
Also resets the dynamic TMP font atlas glyph table (editor artifact,
unrelated to networking).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replicate brush strokes as self-contained commands instead of texture
pixels: the owner emits each committed stamp (local placement plus brush
state, byte-quantized) and remotes replay it through the same draw path,
reproducing identical maps without streaming megabytes.
- MeshPainter.Net.cs: PaintStampNet serializable stamp, OnStampCommitted
owner-capture event (no-op without listener), ApplyNetworkStamp remote
replay, seedable spray for deterministic speckle.
- MeshPainter.cs: emit stamp after drawing, seed spray only when synced.
- PlayerPaintSync.cs: NetworkBehaviour batching owner stamps reliably
(ServerRpc -> ClientRpc) with echo guard; remote disables local painting.
- Player/Hider/Hunter prefabs: PlayerPaintSync plus NGO GlobalObjectIdHash.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>