]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Compress decal data for serialization (#14264)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sat, 11 Mar 2023 02:46:50 +0000 (13:46 +1100)
committerGitHub <noreply@github.com>
Sat, 11 Mar 2023 02:46:50 +0000 (13:46 +1100)
Content.Shared/Decals/DecalGridChunkCollectionTypeSerializer.cs

index 2fdb92d8c7c8786f18ac1ccfee5245de3d6c6ee2..ad6b803e767d6a29b47908ad023fe5638d9e57dc 100644 (file)
@@ -1,9 +1,14 @@
+using System.Globalization;
+using Robust.Shared.Map;
 using Robust.Shared.Serialization;
 using Robust.Shared.Serialization.Manager;
 using Robust.Shared.Serialization.Markdown;
 using Robust.Shared.Serialization.Markdown.Mapping;
+using Robust.Shared.Serialization.Markdown.Sequence;
 using Robust.Shared.Serialization.Markdown.Validation;
+using Robust.Shared.Serialization.Markdown.Value;
 using Robust.Shared.Serialization.TypeSerializers.Interfaces;
+using Robust.Shared.Utility;
 using static Content.Shared.Decals.DecalGridComponent;
 
 namespace Content.Shared.Decals
@@ -22,7 +27,38 @@ namespace Content.Shared.Decals
             IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null,
             ISerializationManager.InstantiationDelegate<DecalGridChunkCollection>? _ = default)
         {
-            var dictionary = serializationManager.Read<Dictionary<Vector2i, DecalChunk>>(node, hookCtx, context, notNullableOverride: true);
+            node.TryGetValue(new ValueDataNode("version"), out var versionNode);
+            var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1;
+            Dictionary<Vector2i, DecalChunk> dictionary;
+
+            // TODO: Dump this when we don't need support anymore.
+            if (version > 1)
+            {
+                var nodes = (SequenceDataNode) node["nodes"];
+                dictionary = new Dictionary<Vector2i, DecalChunk>();
+
+                foreach (var dNode in nodes)
+                {
+                    var aNode = (MappingDataNode) dNode;
+                    var data = serializationManager.Read<DecalData>(aNode["node"], hookCtx, context);
+                    var deckNodes = (MappingDataNode) aNode["decals"];
+
+                    foreach (var (decalUidNode, decalData) in deckNodes)
+                    {
+                        var dUid = serializationManager.Read<uint>(decalUidNode, hookCtx, context);
+                        var coords = serializationManager.Read<Vector2>(decalData, hookCtx, context);
+
+                        var chunkOrigin = SharedMapSystem.GetChunkIndices(coords, SharedDecalSystem.ChunkSize);
+                        var chunk = dictionary.GetOrNew(chunkOrigin);
+                        var decal = new Decal(coords, data.Id, data.Color, data.Angle, data.ZIndex, data.Cleanable);
+                        chunk.Decals.Add(dUid, decal);
+                    }
+                }
+            }
+            else
+            {
+                dictionary = serializationManager.Read<Dictionary<Vector2i, DecalChunk>>(node, hookCtx, context, notNullableOverride: true);
+            }
 
             var uids = new SortedSet<uint>();
             var uidChunkMap = new Dictionary<uint, Vector2i>();
@@ -59,7 +95,116 @@ namespace Content.Shared.Decals
             bool alwaysWrite = false,
             ISerializationContext? context = null)
         {
-            return serializationManager.WriteValue(value.ChunkCollection, alwaysWrite, context, notNullableOverride: true);
+            var lookup = new Dictionary<DecalData, List<uint>>();
+            var decalLookup = new Dictionary<uint, Decal>();
+
+            var allData = new MappingDataNode();
+            // Want consistent chunk + decal ordering so diffs aren't mangled
+            var chunks = new List<Vector2i>(value.ChunkCollection.Keys);
+            chunks.Sort((x, y) => x.X == y.X ? x.Y.CompareTo(y.Y) : x.X.CompareTo(y.X));
+            var nodes = new SequenceDataNode();
+
+            // Assuming decal indices stay consistent:
+            // We'll write decals by
+            // - decaldata
+            // - decal uid
+            // - additional decal data
+
+            // Build all of the decal lookups first.
+            foreach (var chunk in value.ChunkCollection.Values)
+            {
+                var sortedDecals = new List<uint>(chunk.Decals.Keys);
+                sortedDecals.Sort();
+
+                foreach (var (uid, decal) in chunk.Decals)
+                {
+                    var data = new DecalData(decal);
+                    var existing = lookup.GetOrNew(data);
+                    existing.Add(uid);
+                    decalLookup[uid] = decal;
+                }
+            }
+
+            var lookupIndex = 0;
+
+            foreach (var (data, uids) in lookup)
+            {
+                var lookupNode = new MappingDataNode { { "node", serializationManager.WriteValue(data, alwaysWrite, context) } };
+                var decks = new MappingDataNode();
+
+                uids.Sort();
+
+                foreach (var uid in uids)
+                {
+                    var decal = decalLookup[uid];
+                    // Inline coordinates
+                    decks.Add(serializationManager.WriteValue(uid, alwaysWrite, context), serializationManager.WriteValue(decal.Coordinates, alwaysWrite, context));
+                }
+
+                lookupNode.Add("decals", decks);
+                nodes.Add(lookupNode);
+            }
+
+            allData.Add("version", 2.ToString(CultureInfo.InvariantCulture));
+            allData.Add("nodes", nodes);
+
+            return allData;
+        }
+
+        [DataDefinition]
+        private readonly struct DecalData : IEquatable<DecalData>
+        {
+            [DataField("id")]
+            public readonly string Id = string.Empty;
+
+            [DataField("color")]
+            public readonly Color? Color;
+
+            [DataField("angle")]
+            public readonly Angle Angle = Angle.Zero;
+
+            [DataField("zIndex")]
+            public readonly int ZIndex;
+
+            [DataField("cleanable")]
+            public readonly bool Cleanable;
+
+            public DecalData(string id, Color? color, Angle angle, int zIndex, bool cleanable)
+            {
+                Id = id;
+                Color = color;
+                Angle = angle;
+                ZIndex = zIndex;
+                Cleanable = cleanable;
+            }
+
+            public DecalData(Decal decal)
+            {
+                Id = decal.Id;
+                Color = decal.Color;
+                Angle = decal.Angle;
+                ZIndex = decal.ZIndex;
+                Cleanable = decal.Cleanable;
+            }
+
+            public bool Equals(DecalData other)
+            {
+                return Id == other.Id &&
+                       Nullable.Equals(Color, other.Color) &&
+                       Angle.Equals(other.Angle) &&
+                       ZIndex == other.ZIndex &&
+                       Cleanable == other.Cleanable;
+            }
+
+            public override bool Equals(object? obj)
+            {
+                return obj is DecalData other && Equals(other);
+            }
+
+            public override int GetHashCode()
+            {
+                return HashCode.Combine(Id, Color, Angle, ZIndex, Cleanable);
+            }
         }
     }
 }