mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Remove dupe grid serialization (#3522)
This commit is contained in:
@@ -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?
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
163
Robust.Shared/Map/MapSerializationContext.cs
Normal file
163
Robust.Shared/Map/MapSerializationContext.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
86
Robust.Shared/Physics/FixtureSerializer.cs
Normal file
86
Robust.Shared/Physics/FixtureSerializer.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user