DecalGridChunkCollectionTypeSerializer.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. using System.Globalization;
  2. using System.Linq;
  3. using System.Numerics;
  4. using Robust.Shared.Map;
  5. using Robust.Shared.Serialization;
  6. using Robust.Shared.Serialization.Manager;
  7. using Robust.Shared.Serialization.Markdown;
  8. using Robust.Shared.Serialization.Markdown.Mapping;
  9. using Robust.Shared.Serialization.Markdown.Sequence;
  10. using Robust.Shared.Serialization.Markdown.Validation;
  11. using Robust.Shared.Serialization.Markdown.Value;
  12. using Robust.Shared.Serialization.TypeSerializers.Interfaces;
  13. using Robust.Shared.Utility;
  14. using static Content.Shared.Decals.DecalGridComponent;
  15. namespace Content.Shared.Decals
  16. {
  17. [TypeSerializer]
  18. public sealed partial class DecalGridChunkCollectionTypeSerializer : ITypeSerializer<DecalGridChunkCollection, MappingDataNode>
  19. {
  20. public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
  21. IDependencyCollection dependencies, ISerializationContext? context = null)
  22. {
  23. return serializationManager.ValidateNode<Dictionary<Vector2i, Dictionary<uint, Decal>>>(node, context);
  24. }
  25. public DecalGridChunkCollection Read(ISerializationManager serializationManager,
  26. MappingDataNode node,
  27. IDependencyCollection dependencies, SerializationHookContext hookCtx, ISerializationContext? context = null,
  28. ISerializationManager.InstantiationDelegate<DecalGridChunkCollection>? _ = default)
  29. {
  30. node.TryGetValue(new ValueDataNode("version"), out var versionNode);
  31. var version = ((ValueDataNode?) versionNode)?.AsInt() ?? 1;
  32. Dictionary<Vector2i, DecalChunk> dictionary;
  33. uint nextIndex = 0;
  34. var ids = new HashSet<uint>();
  35. // TODO: Dump this when we don't need support anymore.
  36. if (version > 1)
  37. {
  38. var nodes = (SequenceDataNode) node["nodes"];
  39. dictionary = new Dictionary<Vector2i, DecalChunk>();
  40. foreach (var dNode in nodes)
  41. {
  42. var aNode = (MappingDataNode) dNode;
  43. var data = serializationManager.Read<DecalData>(aNode["node"], hookCtx, context);
  44. var deckNodes = (MappingDataNode) aNode["decals"];
  45. foreach (var (decalUidNode, decalData) in deckNodes)
  46. {
  47. var dUid = serializationManager.Read<uint>(decalUidNode, hookCtx, context);
  48. var coords = serializationManager.Read<Vector2>(decalData, hookCtx, context);
  49. var chunkOrigin = SharedMapSystem.GetChunkIndices(coords, SharedDecalSystem.ChunkSize);
  50. var chunk = dictionary.GetOrNew(chunkOrigin);
  51. var decal = new Decal(coords, data.Id, data.Color, data.Angle, data.ZIndex, data.Cleanable);
  52. nextIndex = Math.Max(nextIndex, dUid);
  53. // Re-used ID somehow
  54. // This will bump all IDs by up to 1 but will ensure the map is still readable.
  55. if (!ids.Add(dUid))
  56. {
  57. dUid = nextIndex++;
  58. ids.Add(dUid);
  59. }
  60. chunk.Decals[dUid] = decal;
  61. }
  62. }
  63. }
  64. else
  65. {
  66. dictionary = serializationManager.Read<Dictionary<Vector2i, DecalChunk>>(node, hookCtx, context, notNullableOverride: true);
  67. foreach (var decals in dictionary.Values)
  68. {
  69. foreach (var uid in decals.Decals.Keys)
  70. {
  71. nextIndex = Math.Max(uid, nextIndex);
  72. }
  73. }
  74. }
  75. nextIndex++;
  76. return new DecalGridChunkCollection(dictionary) { NextDecalId = nextIndex };
  77. }
  78. public DataNode Write(ISerializationManager serializationManager,
  79. DecalGridChunkCollection value, IDependencyCollection dependencies,
  80. bool alwaysWrite = false,
  81. ISerializationContext? context = null)
  82. {
  83. var lookup = new Dictionary<DecalData, List<uint>>();
  84. var decalLookup = new Dictionary<uint, Decal>();
  85. var allData = new MappingDataNode();
  86. // Want consistent chunk + decal ordering so diffs aren't mangled
  87. var nodes = new SequenceDataNode();
  88. // Assuming decal indices stay consistent:
  89. // We'll write decals by
  90. // - decaldata
  91. // - decal uid
  92. // - additional decal data
  93. // Build all of the decal lookups first.
  94. foreach (var chunk in value.ChunkCollection.Values)
  95. {
  96. foreach (var (uid, decal) in chunk.Decals)
  97. {
  98. var data = new DecalData(decal);
  99. var existing = lookup.GetOrNew(data);
  100. existing.Add(uid);
  101. decalLookup[uid] = decal;
  102. }
  103. }
  104. var lookupNodes = lookup.Keys.ToList();
  105. lookupNodes.Sort();
  106. foreach (var data in lookupNodes)
  107. {
  108. var uids = lookup[data];
  109. var lookupNode = new MappingDataNode { { "node", serializationManager.WriteValue(data, alwaysWrite, context) } };
  110. var decks = new MappingDataNode();
  111. uids.Sort();
  112. foreach (var uid in uids)
  113. {
  114. var decal = decalLookup[uid];
  115. // Inline coordinates
  116. decks.Add(serializationManager.WriteValue(uid, alwaysWrite, context), serializationManager.WriteValue(decal.Coordinates, alwaysWrite, context));
  117. }
  118. lookupNode.Add("decals", decks);
  119. nodes.Add(lookupNode);
  120. }
  121. allData.Add("version", 2.ToString(CultureInfo.InvariantCulture));
  122. allData.Add("nodes", nodes);
  123. return allData;
  124. }
  125. [DataDefinition]
  126. private readonly partial struct DecalData : IEquatable<DecalData>, IComparable<DecalData>
  127. {
  128. [DataField("id")]
  129. public string Id { get; init; } = string.Empty;
  130. [DataField("color")]
  131. public Color? Color { get; init; }
  132. [DataField("angle")]
  133. public Angle Angle { get; init; } = Angle.Zero;
  134. [DataField("zIndex")]
  135. public int ZIndex { get; init; }
  136. [DataField("cleanable")]
  137. public bool Cleanable { get; init; }
  138. public DecalData(string id, Color? color, Angle angle, int zIndex, bool cleanable)
  139. {
  140. Id = id;
  141. Color = color;
  142. Angle = angle;
  143. ZIndex = zIndex;
  144. Cleanable = cleanable;
  145. }
  146. public DecalData(Decal decal)
  147. {
  148. Id = decal.Id;
  149. Color = decal.Color;
  150. Angle = decal.Angle;
  151. ZIndex = decal.ZIndex;
  152. Cleanable = decal.Cleanable;
  153. }
  154. public bool Equals(DecalData other)
  155. {
  156. return Id == other.Id &&
  157. Nullable.Equals(Color, other.Color) &&
  158. Angle.Equals(other.Angle) &&
  159. ZIndex == other.ZIndex &&
  160. Cleanable == other.Cleanable;
  161. }
  162. public override bool Equals(object? obj)
  163. {
  164. return obj is DecalData other && Equals(other);
  165. }
  166. public override int GetHashCode()
  167. {
  168. return HashCode.Combine(Id, Color, Angle, ZIndex, Cleanable);
  169. }
  170. public int CompareTo(DecalData other)
  171. {
  172. var idComparison = string.Compare(Id, other.Id, StringComparison.Ordinal);
  173. if (idComparison != 0)
  174. return idComparison;
  175. var colorComparison = string.Compare(Color?.ToHex(), other.Color?.ToHex(), StringComparison.Ordinal);
  176. if (colorComparison != 0)
  177. return colorComparison;
  178. var angleComparison = Angle.Theta.CompareTo(other.Angle.Theta);
  179. if (angleComparison != 0)
  180. return angleComparison;
  181. var zIndexComparison = ZIndex.CompareTo(other.ZIndex);
  182. if (zIndexComparison != 0)
  183. return zIndexComparison;
  184. return Cleanable.CompareTo(other.Cleanable);
  185. }
  186. }
  187. }
  188. }