Remove dupe grid serialization (#3522)

This commit is contained in:
metalgearsloth
2022-11-26 13:55:48 +11:00
committed by GitHub
parent 32bdc152d7
commit 025fa95854
6 changed files with 254 additions and 159 deletions

View File

@@ -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<EntityUid, ValueDataNode>
{
private readonly IComponentFactory _factory;
private readonly IEntityManager _entityManager;
private readonly ISerializationManager _serializationManager;
public Dictionary<(Type, Type), object> TypeReaders { get; }
public Dictionary<Type, object> TypeWriters { get; }
public Dictionary<Type, object> TypeCopiers => TypeWriters;
public Dictionary<(Type, Type), object> TypeValidators => TypeReaders;
// Run-specific data
public Dictionary<ushort, string>? TileMap;
public readonly Dictionary<string, MappingDataNode> CurrentReadingEntityComponents = new();
public HashSet<string> CurrentlyIgnoredComponents = new();
public string? CurrentWritingComponent;
public EntityUid? CurrentWritingEntity;
private Dictionary<int, EntityUid> _uidEntityMap = new();
private Dictionary<EntityUid, int> _entityUidMap = new();
public MapSerializationContext(IComponentFactory factory, IEntityManager entityManager, ISerializationManager serializationManager)
{
_factory = factory;
_entityManager = entityManager;
_serializationManager = serializationManager;
TypeWriters = new Dictionary<Type, object>()
{
{typeof(EntityUid), this}
};
TypeReaders = new Dictionary<(Type, Type), object>()
{
{(typeof(EntityUid), typeof(ValueDataNode)), this}
};
}
public void Set(Dictionary<int, EntityUid> uidEntityMap, Dictionary<EntityUid, int> 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<string> GetExtraComponentTypes()
{
return CurrentReadingEntityComponents!.Keys;
}
public bool ShouldSkipComponent(string compName)
{
return CurrentlyIgnoredComponents.Contains(compName);
}
ValidationNode ITypeValidator<EntityUid, ValueDataNode>.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<EntityUid, ValueDataNode>.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);
}
}
/// <summary>
/// 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?

View File

@@ -51,7 +51,7 @@ namespace Robust.Server.Maps
IReadOnlyDictionary<ushort, string>? tileMap = null;
if (context is MapLoaderSystem.MapSerializationContext serContext)
if (context is MapSerializationContext serContext)
{
tileMap = serContext.TileMap;
}

View File

@@ -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<EntityUid, ValueDataNode>
{
private readonly IComponentFactory _factory;
private readonly ISerializationManager _serializationManager;
public Dictionary<(Type, Type), object> TypeReaders { get; }
public Dictionary<Type, object> TypeWriters { get; }
public Dictionary<Type, object> TypeCopiers => TypeWriters;
public Dictionary<(Type, Type), object> TypeValidators => TypeReaders;
// Run-specific data
public Dictionary<ushort, string>? TileMap;
public readonly Dictionary<string, MappingDataNode> CurrentReadingEntityComponents = new();
public HashSet<string> CurrentlyIgnoredComponents = new();
public string? CurrentWritingComponent;
public EntityUid? CurrentWritingEntity;
private Dictionary<int, EntityUid> _uidEntityMap = new();
private Dictionary<EntityUid, int> _entityUidMap = new();
public MapSerializationContext(IComponentFactory factory, ISerializationManager serializationManager)
{
_factory = factory;
_serializationManager = serializationManager;
TypeWriters = new Dictionary<Type, object>()
{
{typeof(EntityUid), this}
};
TypeReaders = new Dictionary<(Type, Type), object>()
{
{(typeof(EntityUid), typeof(ValueDataNode)), this}
};
}
public void Set(Dictionary<int, EntityUid> uidEntityMap, Dictionary<EntityUid, int> 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<string> GetExtraComponentTypes()
{
return CurrentReadingEntityComponents!.Keys;
}
public bool ShouldSkipComponent(string compName)
{
return CurrentlyIgnoredComponents.Contains(compName);
}
ValidationNode ITypeValidator<EntityUid, ValueDataNode>.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<EntityUid, ValueDataNode>.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);
}
}

View File

@@ -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;
/// <summary>
/// Special-case to avoid writing grid fixtures.
/// </summary>
public sealed class FixtureSerializer : ITypeSerializer<List<Fixture>, SequenceDataNode>
{
public ValidationNode Validate(ISerializationManager serializationManager, SequenceDataNode node,
IDependencyCollection dependencies, ISerializationContext? context = null)
{
var seq = new List<ValidationNode>(node.Count);
foreach (var subNode in node)
{
seq.Add(serializationManager.ValidateNode<Fixture>(subNode, context));
}
return new ValidatedSequenceNode(seq);
}
public List<Fixture> Read(ISerializationManager serializationManager, SequenceDataNode node, IDependencyCollection dependencies,
bool skipHook, ISerializationContext? context = null, List<Fixture>? value = default)
{
value ??= new List<Fixture>(node.Count);
foreach (var subNode in node)
{
var fixture = serializationManager.Read<Fixture>(subNode, context, skipHook);
value.Add(fixture);
}
return value;
}
public DataNode Write(ISerializationManager serializationManager, List<Fixture> 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<IEntityManager>().HasComponent<MapGridComponent>(mapContext.CurrentWritingEntity))
{
return seq;
}
}
foreach (var fixture in value)
{
seq.Add(serializationManager.WriteValue(fixture, alwaysWrite, context));
}
return seq;
}
public List<Fixture> Copy(ISerializationManager serializationManager, List<Fixture> source, List<Fixture> 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;
}
}

View File

@@ -17,7 +17,6 @@ namespace Robust.Shared.Physics
/// </remarks>
[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<string, Fixture> Fixtures = new();
[DataField("fixtures")]
[DataField("fixtures", customTypeSerializer:typeof(FixtureSerializer))]
[NeverPushInheritance]
[Access(typeof(FixtureSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
internal List<Fixture> SerializedFixtures

View File

@@ -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;
}