From 025fa958549b4d63e4888a810f780c53e6fb89a9 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 26 Nov 2022 13:55:48 +1100 Subject: [PATCH] Remove dupe grid serialization (#3522) --- .../EntitySystems/MapLoaderSystem.cs | 157 +---------------- Robust.Server/Maps/GridSerializer.cs | 2 +- Robust.Shared/Map/MapSerializationContext.cs | 163 ++++++++++++++++++ Robust.Shared/Physics/FixtureSerializer.cs | 86 +++++++++ Robust.Shared/Physics/FixturesComponent.cs | 3 +- .../Physics/Systems/FixtureSystem.cs | 2 +- 6 files changed, 254 insertions(+), 159 deletions(-) create mode 100644 Robust.Shared/Map/MapSerializationContext.cs create mode 100644 Robust.Shared/Physics/FixtureSerializer.cs diff --git a/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs b/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs index 4116ef99b..40d53ff90 100644 --- a/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs +++ b/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs @@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; -using JetBrains.Annotations; using Robust.Server.Maps; using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; @@ -19,9 +18,7 @@ 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.Timing; using Robust.Shared.Utility; using YamlDotNet.Core; @@ -41,7 +38,7 @@ public sealed class MapLoaderSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IResourceManager _resourceManager = default!; [Dependency] private readonly ISerializationManager _serManager = default!; - private IServerEntityManagerInternal _serverEntityManager = default!; + private IServerEntityManagerInternal _serverEntityManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly MetaDataSystem _meta = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -60,7 +57,7 @@ public sealed class MapLoaderSystem : EntitySystem base.Initialize(); _serverEntityManager = (IServerEntityManagerInternal)EntityManager; _logLoader = Logger.GetSawmill("loader"); - _context = new MapSerializationContext(_factory, EntityManager, _serManager); + _context = new MapSerializationContext(_factory, _serManager); } #region Public @@ -997,156 +994,6 @@ public sealed class MapLoaderSystem : EntitySystem #endregion - internal sealed class MapSerializationContext : ISerializationContext, IEntityLoadContext, - ITypeSerializer - { - private readonly IComponentFactory _factory; - private readonly IEntityManager _entityManager; - private readonly ISerializationManager _serializationManager; - - public Dictionary<(Type, Type), object> TypeReaders { get; } - public Dictionary TypeWriters { get; } - public Dictionary TypeCopiers => TypeWriters; - public Dictionary<(Type, Type), object> TypeValidators => TypeReaders; - - // Run-specific data - public Dictionary? TileMap; - public readonly Dictionary CurrentReadingEntityComponents = new(); - public HashSet CurrentlyIgnoredComponents = new(); - public string? CurrentWritingComponent; - public EntityUid? CurrentWritingEntity; - - private Dictionary _uidEntityMap = new(); - private Dictionary _entityUidMap = new(); - - public MapSerializationContext(IComponentFactory factory, IEntityManager entityManager, ISerializationManager serializationManager) - { - _factory = factory; - _entityManager = entityManager; - _serializationManager = serializationManager; - - TypeWriters = new Dictionary() - { - {typeof(EntityUid), this} - }; - TypeReaders = new Dictionary<(Type, Type), object>() - { - {(typeof(EntityUid), typeof(ValueDataNode)), this} - }; - } - - public void Set(Dictionary uidEntityMap, Dictionary entityUidMap) - { - _uidEntityMap = uidEntityMap; - _entityUidMap = entityUidMap; - } - - public void Clear() - { - CurrentReadingEntityComponents.Clear(); - CurrentlyIgnoredComponents.Clear(); - CurrentWritingComponent = null; - CurrentWritingEntity = null; - } - - // Create custom object serializers that will correctly allow data to be overriden by the map file. - MappingDataNode IEntityLoadContext.GetComponentData(string componentName, - MappingDataNode? protoData) - { - if (CurrentReadingEntityComponents == null) - { - throw new InvalidOperationException(); - } - - - if (CurrentReadingEntityComponents.TryGetValue(componentName, out var mapping)) - { - if (protoData == null) return mapping.Copy(); - - return _serializationManager.PushCompositionWithGenericNode( - _factory.GetRegistration(componentName).Type, new[] { protoData }, mapping, this); - } - - return protoData ?? new MappingDataNode(); - } - - public IEnumerable GetExtraComponentTypes() - { - return CurrentReadingEntityComponents!.Keys; - } - - public bool ShouldSkipComponent(string compName) - { - return CurrentlyIgnoredComponents.Contains(compName); - } - - ValidationNode ITypeValidator.Validate(ISerializationManager serializationManager, - ValueDataNode node, IDependencyCollection dependencies, ISerializationContext? context) - { - if (node.Value == "null") - { - return new ValidatedValueNode(node); - } - - if (!int.TryParse(node.Value, out var val) || !_uidEntityMap.ContainsKey(val)) - { - return new ErrorNode(node, "Invalid EntityUid", true); - } - - return new ValidatedValueNode(node); - } - - public DataNode Write(ISerializationManager serializationManager, EntityUid value, - IDependencyCollection dependencies, bool alwaysWrite = false, - ISerializationContext? context = null) - { - if (!_entityUidMap.TryGetValue(value, out var entityUidMapped)) - { - // Terrible hack to mute this warning on the grids themselves when serializing blueprints. - if (CurrentWritingComponent != "Transform") - { - Logger.WarningS("map", "Cannot write entity UID '{0}'.", value); - } - - return new ValueDataNode("null"); - } - - return new ValueDataNode(entityUidMapped.ToString(CultureInfo.InvariantCulture)); - } - - EntityUid ITypeReader.Read(ISerializationManager serializationManager, - ValueDataNode node, - IDependencyCollection dependencies, - bool skipHook, - ISerializationContext? context, EntityUid _) - { - if (node.Value == "null") - { - return EntityUid.Invalid; - } - - var val = int.Parse(node.Value); - - if (!_uidEntityMap.TryGetValue(val, out var entity)) - { - Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.", val); - return EntityUid.Invalid; - } - else - { - return entity; - } - } - - [MustUseReturnValue] - public EntityUid Copy(ISerializationManager serializationManager, EntityUid source, EntityUid target, - bool skipHook, - ISerializationContext? context = null) - { - return new((int)source); - } - } - /// /// Does basic pre-deserialization checks on map file load. /// For example, let's not try to use maps with multiple grids as blueprints, shall we? diff --git a/Robust.Server/Maps/GridSerializer.cs b/Robust.Server/Maps/GridSerializer.cs index f658de819..72ec7a213 100644 --- a/Robust.Server/Maps/GridSerializer.cs +++ b/Robust.Server/Maps/GridSerializer.cs @@ -51,7 +51,7 @@ namespace Robust.Server.Maps IReadOnlyDictionary? tileMap = null; - if (context is MapLoaderSystem.MapSerializationContext serContext) + if (context is MapSerializationContext serContext) { tileMap = serContext.TileMap; } diff --git a/Robust.Shared/Map/MapSerializationContext.cs b/Robust.Shared/Map/MapSerializationContext.cs new file mode 100644 index 000000000..a6183fd87 --- /dev/null +++ b/Robust.Shared/Map/MapSerializationContext.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Validation; +using Robust.Shared.Serialization.Markdown.Value; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; + +namespace Robust.Shared.Map; + +internal sealed class MapSerializationContext : ISerializationContext, IEntityLoadContext, + ITypeSerializer +{ + private readonly IComponentFactory _factory; + private readonly ISerializationManager _serializationManager; + + public Dictionary<(Type, Type), object> TypeReaders { get; } + public Dictionary TypeWriters { get; } + public Dictionary TypeCopiers => TypeWriters; + public Dictionary<(Type, Type), object> TypeValidators => TypeReaders; + + // Run-specific data + public Dictionary? TileMap; + public readonly Dictionary CurrentReadingEntityComponents = new(); + public HashSet CurrentlyIgnoredComponents = new(); + public string? CurrentWritingComponent; + public EntityUid? CurrentWritingEntity; + + private Dictionary _uidEntityMap = new(); + private Dictionary _entityUidMap = new(); + + public MapSerializationContext(IComponentFactory factory, ISerializationManager serializationManager) + { + _factory = factory; + _serializationManager = serializationManager; + + TypeWriters = new Dictionary() + { + {typeof(EntityUid), this} + }; + TypeReaders = new Dictionary<(Type, Type), object>() + { + {(typeof(EntityUid), typeof(ValueDataNode)), this} + }; + } + + public void Set(Dictionary uidEntityMap, Dictionary entityUidMap) + { + _uidEntityMap = uidEntityMap; + _entityUidMap = entityUidMap; + } + + public void Clear() + { + CurrentReadingEntityComponents.Clear(); + CurrentlyIgnoredComponents.Clear(); + CurrentWritingComponent = null; + CurrentWritingEntity = null; + } + + // Create custom object serializers that will correctly allow data to be overriden by the map file. + MappingDataNode IEntityLoadContext.GetComponentData(string componentName, + MappingDataNode? protoData) + { + if (CurrentReadingEntityComponents == null) + { + throw new InvalidOperationException(); + } + + + if (CurrentReadingEntityComponents.TryGetValue(componentName, out var mapping)) + { + if (protoData == null) return mapping.Copy(); + + return _serializationManager.PushCompositionWithGenericNode( + _factory.GetRegistration(componentName).Type, new[] { protoData }, mapping, this); + } + + return protoData ?? new MappingDataNode(); + } + + public IEnumerable GetExtraComponentTypes() + { + return CurrentReadingEntityComponents!.Keys; + } + + public bool ShouldSkipComponent(string compName) + { + return CurrentlyIgnoredComponents.Contains(compName); + } + + ValidationNode ITypeValidator.Validate(ISerializationManager serializationManager, + ValueDataNode node, IDependencyCollection dependencies, ISerializationContext? context) + { + if (node.Value == "null") + { + return new ValidatedValueNode(node); + } + + if (!int.TryParse(node.Value, out var val) || !_uidEntityMap.ContainsKey(val)) + { + return new ErrorNode(node, "Invalid EntityUid", true); + } + + return new ValidatedValueNode(node); + } + + public DataNode Write(ISerializationManager serializationManager, EntityUid value, + IDependencyCollection dependencies, bool alwaysWrite = false, + ISerializationContext? context = null) + { + if (!_entityUidMap.TryGetValue(value, out var entityUidMapped)) + { + // Terrible hack to mute this warning on the grids themselves when serializing blueprints. + if (CurrentWritingComponent != "Transform") + { + Logger.WarningS("map", "Cannot write entity UID '{0}'.", value); + } + + return new ValueDataNode("null"); + } + + return new ValueDataNode(entityUidMapped.ToString(CultureInfo.InvariantCulture)); + } + + EntityUid ITypeReader.Read(ISerializationManager serializationManager, + ValueDataNode node, + IDependencyCollection dependencies, + bool skipHook, + ISerializationContext? context, EntityUid _) + { + if (node.Value == "null") + { + return EntityUid.Invalid; + } + + var val = int.Parse(node.Value); + + if (!_uidEntityMap.TryGetValue(val, out var entity)) + { + Logger.ErrorS("map", "Error in map file: found local entity UID '{0}' which does not exist.", val); + return EntityUid.Invalid; + } + else + { + return entity; + } + } + + [MustUseReturnValue] + public EntityUid Copy(ISerializationManager serializationManager, EntityUid source, EntityUid target, + bool skipHook, + ISerializationContext? context = null) + { + return new((int)source); + } +} diff --git a/Robust.Shared/Physics/FixtureSerializer.cs b/Robust.Shared/Physics/FixtureSerializer.cs new file mode 100644 index 000000000..8b3b84f3b --- /dev/null +++ b/Robust.Shared/Physics/FixtureSerializer.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Sequence; +using Robust.Shared.Serialization.Markdown.Validation; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; + +namespace Robust.Shared.Physics; + +/// +/// Special-case to avoid writing grid fixtures. +/// +public sealed class FixtureSerializer : ITypeSerializer, SequenceDataNode> +{ + public ValidationNode Validate(ISerializationManager serializationManager, SequenceDataNode node, + IDependencyCollection dependencies, ISerializationContext? context = null) + { + var seq = new List(node.Count); + + foreach (var subNode in node) + { + seq.Add(serializationManager.ValidateNode(subNode, context)); + } + + return new ValidatedSequenceNode(seq); + } + + public List Read(ISerializationManager serializationManager, SequenceDataNode node, IDependencyCollection dependencies, + bool skipHook, ISerializationContext? context = null, List? value = default) + { + value ??= new List(node.Count); + + foreach (var subNode in node) + { + var fixture = serializationManager.Read(subNode, context, skipHook); + value.Add(fixture); + } + + return value; + } + + public DataNode Write(ISerializationManager serializationManager, List value, IDependencyCollection dependencies, + bool alwaysWrite = false, ISerializationContext? context = null) + { + var seq = new SequenceDataNode(); + + if (value.Count == 0) + return seq; + + if (context is MapSerializationContext mapContext) + { + // Don't serialize mapgrid fixtures because it's bloat and we'll just generate them at runtime. + if (dependencies.Resolve().HasComponent(mapContext.CurrentWritingEntity)) + { + return seq; + } + } + + foreach (var fixture in value) + { + seq.Add(serializationManager.WriteValue(fixture, alwaysWrite, context)); + } + + return seq; + } + + public List Copy(ISerializationManager serializationManager, List source, List target, bool skipHook, + ISerializationContext? context = null) + { + target.Clear(); + target.EnsureCapacity(source.Count); + + foreach (var fixture in source) + { + var nFixture = serializationManager.Copy(fixture, context, skipHook); + target.Add(nFixture); + } + + return target; + } +} diff --git a/Robust.Shared/Physics/FixturesComponent.cs b/Robust.Shared/Physics/FixturesComponent.cs index af4612dc4..71a2aab61 100644 --- a/Robust.Shared/Physics/FixturesComponent.cs +++ b/Robust.Shared/Physics/FixturesComponent.cs @@ -17,7 +17,6 @@ namespace Robust.Shared.Physics /// [RegisterComponent] [NetworkedComponent] - [ComponentProtoName("Fixtures")] public sealed class FixturesComponent : Component { // This is a snowflake component whose main job is making physics states smaller for massive bodies @@ -31,7 +30,7 @@ namespace Robust.Shared.Physics [Access(typeof(FixtureSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends public readonly Dictionary Fixtures = new(); - [DataField("fixtures")] + [DataField("fixtures", customTypeSerializer:typeof(FixtureSerializer))] [NeverPushInheritance] [Access(typeof(FixtureSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends internal List SerializedFixtures diff --git a/Robust.Shared/Physics/Systems/FixtureSystem.cs b/Robust.Shared/Physics/Systems/FixtureSystem.cs index 800220f95..7a9141331 100644 --- a/Robust.Shared/Physics/Systems/FixtureSystem.cs +++ b/Robust.Shared/Physics/Systems/FixtureSystem.cs @@ -244,7 +244,7 @@ namespace Robust.Shared.Physics.Systems // hence we'll just make sure its body is set and SharedBroadphaseSystem will deal with it later. if (EntityManager.TryGetComponent(uid, out PhysicsComponent? body)) { - foreach (var (_, fixture) in component.Fixtures) + foreach (var fixture in component.Fixtures.Values) { fixture.Body = body; }