Add Ctrl+Z/Ctrl+Y undo/redo with command-based network sync
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>
This commit is contained in:
1 parent
ee482905f9
commit
301f7e64cb
8 files changed
+619
-78
No files matched your search
@@ -303,6 +303,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
// HUD library (MeshPainter.Persistence), never auto-loaded on start.
|
||||
SetupBackend(useGpu, _seedCanonical);
|
||||
ConfigureMaterialPbr();
|
||||
InitUndoHistory(); // baseline = material seed, captured before any stroke
|
||||
|
||||
EnsureProceduralMask();
|
||||
ResolveActiveMask();
|
||||
@@ -592,10 +593,22 @@ public partial class MeshPainter : MonoBehaviour
|
||||
// Painting disabled by the game mode (e.g. player-control mode): do nothing.
|
||||
if (!PaintingEnabled)
|
||||
{
|
||||
EndStrokeIfActive(); // finalize an in-progress stroke if the mode was left mid-paint
|
||||
_hasLastScreen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Undo / redo (Ctrl+Z / Ctrl+Y): only here, since PaintingEnabled is true only on the local
|
||||
// owner in paint mode. Commit any in-progress stroke first so it becomes its own history step.
|
||||
GetUndoRedoInput(out bool wantUndo, out bool wantRedo);
|
||||
if (wantUndo || wantRedo)
|
||||
{
|
||||
EndStrokeIfActive();
|
||||
if (wantUndo) Undo();
|
||||
else Redo();
|
||||
return;
|
||||
}
|
||||
|
||||
if (updateColliderEveryFrame && _skinned)
|
||||
{
|
||||
BakeColliderMesh();
|
||||
@@ -617,6 +630,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
// entirely (no painting, no brush preview ring while active).
|
||||
if (pipette)
|
||||
{
|
||||
EndStrokeIfActive(); // switching to the eyedropper ends any active stroke
|
||||
if (pressed)
|
||||
{
|
||||
if (!_pickHeld) PickAtScreen(screenPos);
|
||||
@@ -635,6 +649,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
|
||||
if (!pressed)
|
||||
{
|
||||
EndStrokeIfActive(); // mouse released -> commit the stroke as one undo step
|
||||
_hasLastScreen = false;
|
||||
return;
|
||||
}
|
||||
@@ -717,6 +732,7 @@ public partial class MeshPainter : MonoBehaviour
|
||||
else PaintStamp3D(pLocal, radiusLocal, u, v, nLocal);
|
||||
|
||||
EmitStamp(pLocal, radiusLocal, u, v, nLocal);
|
||||
MarkStrokePainted(); // a stamp landed -> this stroke is undoable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in new issue
Block a user