From 19564a421b3d6a65698c6a1e2db7f0e7caac8fd7 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 13 Nov 2023 05:22:20 +1100 Subject: [PATCH] Fix deserialization of empty grid chunks (#4565) --- .../EntitySystems/MapLoaderSystem.cs | 22 +++++++++ Robust.Server/Maps/MapChunkSerializer.cs | 2 + .../Map/Enumerators/GridTileEnumerator.cs | 46 ++++++++++--------- 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs b/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs index 12c14b1c3..86f2526eb 100644 --- a/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs +++ b/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs @@ -290,6 +290,9 @@ public sealed class MapLoaderSystem : EntitySystem ReadGrids(data); + // grids prior to engine v175 might've been serialized with empty chunks which now throw debug asserts. + RemoveEmptyChunks(data); + // Then, go hierarchically in order and do the entity things. StartupEntities(data); @@ -305,6 +308,25 @@ public sealed class MapLoaderSystem : EntitySystem return true; } + private void RemoveEmptyChunks(MapData data) + { + var gridQuery = _serverEntityManager.GetEntityQuery(); + foreach (var uid in data.EntitiesToDeserialize.Keys) + { + if (!gridQuery.TryGetComponent(uid, out var gridComp)) + continue; + + foreach (var (index, chunk) in gridComp.Chunks) + { + if (chunk.FilledTiles > 0) + continue; + + Log.Warning($"Encountered empty chunk while deserializing map. Grid: {ToPrettyString(uid)}. Chunk index: {index}"); + gridComp.Chunks.Remove(index); + } + } + } + private bool VerifyEntitiesExist(MapData data, BeforeEntityReadEvent ev) { _stopwatch.Restart(); diff --git a/Robust.Server/Maps/MapChunkSerializer.cs b/Robust.Server/Maps/MapChunkSerializer.cs index dff28e561..d06e45679 100644 --- a/Robust.Server/Maps/MapChunkSerializer.cs +++ b/Robust.Server/Maps/MapChunkSerializer.cs @@ -12,6 +12,7 @@ using Robust.Shared.Serialization.Markdown.Mapping; using Robust.Shared.Serialization.Markdown.Validation; using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Interfaces; +using Robust.Shared.Utility; namespace Robust.Server.Maps; @@ -93,6 +94,7 @@ internal sealed class MapChunkSerializer : ITypeSerializer 0, "Attempting to write an empty chunk"); var root = new MappingDataNode(); var ind = new ValueDataNode($"{value.X},{value.Y}"); root.Add("ind", ind); diff --git a/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs b/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs index 159d11a47..76eedfff8 100644 --- a/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs +++ b/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; using Robust.Shared.Maths; +using Robust.Shared.Utility; namespace Robust.Shared.Map.Enumerators; @@ -26,33 +27,34 @@ public struct GridTileEnumerator } public bool MoveNext([NotNullWhen(true)] out TileRef? tileRef) + { + while (true) { - while (true) + if (_index == _chunkSize * _chunkSize) { - if (_index == _chunkSize * _chunkSize) + if (!_chunkEnumerator.MoveNext()) { - if (!_chunkEnumerator.MoveNext()) - { - tileRef = null; - return false; - } - - _index = 0; + tileRef = null; + return false; } - var (chunkOrigin, chunk) = _chunkEnumerator.Current; - var x = (ushort) (_index / _chunkSize); - var y = (ushort) (_index % _chunkSize); - var tile = chunk.GetTile(x, y); - _index++; - - if (_ignoreEmpty && tile.IsEmpty) - continue; - - var gridX = x + chunkOrigin.X * _chunkSize; - var gridY = y + chunkOrigin.Y * _chunkSize; - tileRef = new TileRef(_gridUid, gridX, gridY, tile); - return true; + _index = 0; } + + var (chunkOrigin, chunk) = _chunkEnumerator.Current; + DebugTools.Assert(chunk.FilledTiles > 0, $"Encountered empty chunk while enumerating tiles"); + var x = (ushort) (_index / _chunkSize); + var y = (ushort) (_index % _chunkSize); + var tile = chunk.GetTile(x, y); + _index++; + + if (_ignoreEmpty && tile.IsEmpty) + continue; + + var gridX = x + chunkOrigin.X * _chunkSize; + var gridY = y + chunkOrigin.Y * _chunkSize; + tileRef = new TileRef(_gridUid, gridX, gridY, tile); + return true; } + } }