From c9a7d9c2a40690d7209648df5eff5e391341b357 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 14 Sep 2026 14:36:57 +0200 Subject: [PATCH] Drop the Studio edit copy whenever a frame does not finish drawing The copy was dropped only when a claimed drawer threw. A built-in drawing exception (BuildDictionary, SetValue, a throwing constructor, Clone) or an ExitGUIException escaped OnGUI with the copy alive, and the next change committed the half-made edit. The window now takes the copy out of _edit before drawing and puts it back only when the document drew to the end with no drawer failing. Every abnormal exit, ExitGUIException included, leaves the next frame to clone afresh from the cache. ExitGUIException is dropped too, not exempted: Unity's pickers exit on the click that opens them and deliver the pick in a later event, which lands on the fresh copy and commits normally. Co-Authored-By: Claude Opus 5 --- .../Caching/Editor/CultCacheStudioWindow.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs b/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs index 5d980cb7..a3dc49a4 100644 --- a/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs +++ b/src/GameCult.Unity/Assets/Caching/Editor/CultCacheStudioWindow.cs @@ -208,17 +208,22 @@ namespace GameCult.Unity.Caching.Editor _inspectorScroll = EditorGUILayout.BeginScrollView(_inspectorScroll); // Drawers edit the edit's private copy. A commit spends the edit, admitted or refused, and the next frame - // begins a new one from whatever record the cache then holds. A frame in which a drawer threw is discarded - // the same way, uncommitted: its copy may hold a half-made mutation. - if (_edit == null || !_edit.IsFor(record)) _edit = _model.BeginEdit(record); + // begins a new one from whatever record the cache then holds. The copy is held here and kept only by a frame + // that drew to the end with no drawer failing: a claimed drawer that threw, a built-in drawing exception, or + // an ExitGUIException all leave _edit null, so a half-made in-place mutation is never committed later. + var edit = _edit != null && _edit.IsFor(record) ? _edit : _model.BeginEdit(record); + _edit = null; var readOnly = ReadOnly; using (new EditorGUI.DisabledScope(readOnly)) { EditorGUI.BeginChangeCheck(); - var drawn = _inspector.DrawDocument(_edit); + var drawn = _inspector.DrawDocument(edit); var changed = EditorGUI.EndChangeCheck(); - if (!drawn) _edit = null; - else if (changed && !readOnly && !_edit.Commit(_cache, out var error)) SetStatus(error, MessageType.Error); + if (drawn) + { + _edit = edit; + if (changed && !readOnly && !edit.Commit(_cache, out var error)) SetStatus(error, MessageType.Error); + } } EditorGUILayout.EndScrollView();