diff --git a/Assets/Scripts/Editor/GON/StageEditor/StageInspectorPanel.cs b/Assets/Scripts/Editor/GON/StageEditor/StageInspectorPanel.cs index 5a41b1b..04de5b9 100644 --- a/Assets/Scripts/Editor/GON/StageEditor/StageInspectorPanel.cs +++ b/Assets/Scripts/Editor/GON/StageEditor/StageInspectorPanel.cs @@ -138,6 +138,45 @@ namespace Editor.GON.StageEditor Add(new HelpBox("The grid is infinite/sparse. The stage size emerges from painted cells and logical objects.", HelpBoxMessageType.Info)); Add(new HelpBox("Editor plane uses X/Y. Later, the stage baker can map editor Y to Unity Z and floor index to Unity Y.", HelpBoxMessageType.Info)); + BuildWorldSyncInspector(); + } + + private void BuildWorldSyncInspector() + { + var spacer = new VisualElement + { + style = + { + height = 10 + } + }; + Add(spacer); + + var section = new Label("World Sync") + { + style = + { + unityFontStyleAndWeight = FontStyle.Bold + } + }; + Add(section); + + var sync = stage.worldSync; + if (sync is null || !sync.HasSync) + { + Add(new HelpBox("Not synced.", HelpBoxMessageType.Info)); + return; + } + + sync.EnsureDefaults(); + AddReadOnlyLine("World Stage", string.IsNullOrEmpty(sync.worldStageDisplayName) + ? sync.worldStageId + : sync.worldStageDisplayName); + AddReadOnlyLine("Stage Id", sync.worldStageId); + AddReadOnlyLine("Kind", sync.worldStageKind.ToString()); + AddReadOnlyLine("Layer", sync.worldLayer.ToString()); + AddReadOnlyLine("Synced At", string.IsNullOrEmpty(sync.syncedAt) ? "-" : sync.syncedAt); + AddReadOnlyLine("Hash", ShortHash(sync.contractHash)); } private void BuildObjectInspector(StageObjectDefinition obj) @@ -313,5 +352,15 @@ namespace Editor.GON.StageEditor Add(row); } + + private static string ShortHash(string hash) + { + if (string.IsNullOrEmpty(hash)) + { + return "-"; + } + + return hash.Length <= 12 ? hash : hash[..12]; + } } } diff --git a/Assets/Scripts/Editor/GON/WorldEditor/WorldLayoutValidator.cs b/Assets/Scripts/Editor/GON/WorldEditor/WorldLayoutValidator.cs index 10d8fba..50fdae4 100644 --- a/Assets/Scripts/Editor/GON/WorldEditor/WorldLayoutValidator.cs +++ b/Assets/Scripts/Editor/GON/WorldEditor/WorldLayoutValidator.cs @@ -51,6 +51,8 @@ namespace Editor.GON.WorldEditor } stage.EnsureDefaults(); + ValidateStageDefinitionSync(layout, stage, messages); + var endpointCount = 0; foreach (var endpoint in layout.endpoints) { @@ -88,6 +90,46 @@ namespace Editor.GON.WorldEditor } } + private static void ValidateStageDefinitionSync( + WorldLayoutSO layout, + WorldStageAuthoring stage, + List messages) + { + if (stage.stageDefinition is null) + { + messages.Add(new WorldValidationMessage(WorldValidationSeverity.Warning, + "Stage has no StageDefinition. Use Create StageDefinition from the stage inspector.", + stage.id)); + return; + } + + stage.stageDefinition.EnsureDefaults(); + var sync = stage.stageDefinition.worldSync; + if (sync is null || !sync.HasSync) + { + messages.Add(new WorldValidationMessage(WorldValidationSeverity.Warning, + "StageDefinition is not synced. Use Sync StageDefinition from the stage inspector.", + stage.id)); + return; + } + + if (!string.IsNullOrEmpty(sync.worldStageId) && sync.worldStageId != stage.id) + { + messages.Add(new WorldValidationMessage(WorldValidationSeverity.Error, + "StageDefinition is synced to another world stage id. Reassign or recreate the StageDefinition.", + stage.id)); + return; + } + + var expectedHash = StageDefinitionSyncer.ComputeExpectedContractHash(layout, stage); + if (sync.contractHash != expectedHash) + { + messages.Add(new WorldValidationMessage(WorldValidationSeverity.Warning, + "StageDefinition sync contract is outdated. Use Sync StageDefinition from the stage inspector.", + stage.id)); + } + } + private static void ValidateEndpoints(WorldLayoutSO layout, List messages) { var nonSocketByTile = new Dictionary();