Make SerializationManager.TryGetTypeNodeSerializer 65 times faster

This commit is contained in:
DrSmugleaf
2026-08-04 16:32:03 -07:00
parent 5749d7a6e5
commit 31bc3ddc06
4 changed files with 311 additions and 151 deletions
@@ -372,7 +372,7 @@ public sealed partial class SerializationManager
var generated = Unsafe.As<ISerializationGenerated<T>>(source);
var target = generated.Instantiate();
generated.Copy(ref target, this, hookCtx, context);
RunAfterHook(target, hookCtx);
TryRunAfterHook(target, hookCtx);
return target;
}
else
@@ -474,7 +474,7 @@ public sealed partial class SerializationManager
var generated = Unsafe.As<ISerializationGenerated<T>>(source);
target ??= generated.Instantiate();
generated.Copy(ref target, this, hookCtx, context);
RunAfterHook(target, hookCtx);
TryRunAfterHook(target, hookCtx);
return;
}
@@ -489,7 +489,7 @@ public sealed partial class SerializationManager
target = CreateCopy(source, hookCtx, context);
}
RunAfterHook(target, hookCtx);
TryRunAfterHook(target, hookCtx);
}
public void CopyTo<T>(ITypeCopier<T> copier, T source, ref T target, ISerializationContext? context = null,
@@ -534,7 +534,7 @@ public sealed partial class SerializationManager
}
copier.CopyTo(this, source, ref target, DependencyCollection, hookCtx, context);
RunAfterHook(target, hookCtx);
TryRunAfterHook(target, hookCtx);
}
public void CopyTo<T, TCopier>(T source, ref T target, ISerializationContext? context = null, bool skipHook = false, bool notNullableOverride = false)
@@ -607,13 +607,13 @@ public sealed partial class SerializationManager
var generated = Unsafe.As<ISerializationGenerated<T>>(source);
var target = generated.Instantiate();
generated.Copy(ref target, this, hookCtx, context);
RunAfterHook(target, hookCtx);
TryRunAfterHook(target, hookCtx);
return target;
}
var res = GetOrCreateCreateCopyGenericDelegate<T>()(source, hookCtx, context);
RunAfterHook(res, hookCtx);
TryRunAfterHook(res, hookCtx);
return res;
}
@@ -643,7 +643,7 @@ public sealed partial class SerializationManager
}
var res = copyCreator.CreateCopy(this, source, DependencyCollection, hookCtx, context);
RunAfterHook(res, hookCtx);
TryRunAfterHook(res, hookCtx);
return res;
}
@@ -193,6 +193,7 @@ namespace Robust.Shared.Serialization.Manager
{
var baseType = typeof(T);
var nullable = baseType.IsNullable();
var isArray = ReadTypeMetadata<T>.IsArray;
T val = default!;
@@ -321,7 +322,7 @@ namespace Robust.Shared.Serialization.Manager
if (!hasSerializer)
{
if (baseType.IsArray)
if (isArray)
{
val = node switch
{
@@ -381,7 +382,7 @@ namespace Robust.Shared.Serialization.Manager
throw new ArgumentException($"No mapping or value node provided for type {baseType}.");
}
RunAfterHook(val, hookCtx);
TryRunAfterHook(val, hookCtx);
}
}
}
@@ -581,7 +582,8 @@ namespace Robust.Shared.Serialization.Manager
}
var baseType = typeof(T);
if (baseType.IsEnum || baseType.IsArray ||
if (baseType.IsEnum ||
ReadTypeMetadata<T>.IsArray ||
(baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
return ((ReadGenericDelegate<T>)_readGenericDelegates.GetOrAdd((typeof(T), node.GetType()!, notNullableOverride),
@@ -771,7 +773,7 @@ namespace Robust.Shared.Serialization.Manager
throw new ArgumentException($"No mapping or value node provided for type {baseType}.");
}
RunAfterHook(val, hookCtx);
TryRunAfterHook(val, hookCtx);
}
}
}
@@ -992,7 +994,7 @@ namespace Robust.Shared.Serialization.Manager
throw new ArgumentException($"No mapping or value node provided for type {baseType}.");
}
RunAfterHook(val, hookCtx);
TryRunAfterHook(val, hookCtx);
}
}
}
@@ -1427,7 +1429,7 @@ namespace Robust.Shared.Serialization.Manager
throw new ArgumentException($"No mapping node provided for type {type} at line: {node.Start.Line}");
}
RunAfterHook(instance, hookCtx);
TryRunAfterHook(instance, hookCtx);
return instance;
}
@@ -1447,7 +1449,7 @@ namespace Robust.Shared.Serialization.Manager
definition.Populate(ref instance, node, this, hookCtx, context);
RunAfterHook(instance, hookCtx);
TryRunAfterHook(instance, hookCtx);
return instance;
}
@@ -1456,5 +1458,16 @@ namespace Robust.Shared.Serialization.Manager
{
throw new ArgumentException($"No type serializer or data definition found for type {typeof(TValue)} with node type {node.GetType()} when reading");
}
private static class ReadTypeMetadata<T>
{
// ReSharper disable once StaticMemberInGenericType
public static bool IsArray;
static ReadTypeMetadata()
{
IsArray = typeof(T).IsArray;
}
}
}
}
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
@@ -10,6 +11,9 @@ using Robust.Shared.Log;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.Manager.Exceptions;
using Robust.Shared.Serialization.Markdown;
using Robust.Shared.Serialization.Markdown.Mapping;
using Robust.Shared.Serialization.Markdown.Sequence;
using Robust.Shared.Serialization.Markdown.Value;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
using Robust.Shared.Utility;
@@ -31,6 +35,28 @@ public sealed partial class SerializationManager
typeof(ITypeWriter<>)
}.ToImmutableArray();
private static readonly ImmutableArray<Type> Nodes = new[]
{
typeof(MappingDataNode),
typeof(SequenceDataNode),
typeof(ValueDataNode),
}.ToImmutableArray();
/// <summary>
/// <see cref="SerializerInterfaces"/>
/// </summary>
private const int ReaderIndex = 0;
/// <summary>
/// <see cref="SerializerInterfaces"/>
/// </summary>
private const int InheritanceHandlerIndex = 1;
/// <summary>
/// <see cref="SerializerInterfaces"/>
/// </summary>
private const int ValidatorIndex = 2;
/// <summary>
/// <see cref="SerializerInterfaces"/>
/// </summary>
@@ -41,12 +67,42 @@ public sealed partial class SerializationManager
/// </summary>
private const int CopierIndex = 4;
/// <summary>
/// <see cref="SerializerInterfaces"/>
/// </summary>
private const int WriterIndex = 5;
/// <summary>
/// <see cref="Nodes"/>
/// </summary>
private const int MappingIndex = 0;
/// <summary>
/// <see cref="Nodes"/>
/// </summary>
private const int SequenceIndex = 1;
/// <summary>
/// <see cref="Nodes"/>
/// </summary>
private const int ValueIndex = 2;
private SerializerProvider _regularSerializerProvider = default!;
private ISawmill _serializerSawmill = default!;
private void InitializeTypeSerializers(IEnumerable<Type> typeSerializers)
{
DebugTools.AssertEqual(ReaderIndex, SerializerInterfaces.IndexOf(typeof(ITypeReader<,>)));
DebugTools.AssertEqual(InheritanceHandlerIndex, SerializerInterfaces.IndexOf(typeof(ITypeInheritanceHandler<,>)));
DebugTools.AssertEqual(ValidatorIndex, SerializerInterfaces.IndexOf(typeof(ITypeValidator<,>)));
DebugTools.AssertEqual(CopyCreatorIndex, SerializerInterfaces.IndexOf(typeof(ITypeCopyCreator<>)));
DebugTools.AssertEqual(CopierIndex, SerializerInterfaces.IndexOf(typeof(ITypeCopier<>)));
DebugTools.AssertEqual(MappingIndex, Nodes.IndexOf(typeof(MappingDataNode)));
DebugTools.AssertEqual(SequenceIndex, Nodes.IndexOf(typeof(SequenceDataNode)));
DebugTools.AssertEqual(ValueIndex, Nodes.IndexOf(typeof(ValueDataNode)));
_regularSerializerProvider = new(this, typeSerializers);
}
@@ -62,6 +118,10 @@ public sealed partial class SerializationManager
ser.SerMan = this;
ser.Log = _serializerSawmill;
}
if (result is IPostInjectInit postInject)
postInject.PostInject();
return result;
}
@@ -102,7 +162,7 @@ public sealed partial class SerializationManager
public sealed class SerializerProvider
{
private SerializationManager _ser;
private readonly SerializationManager _ser;
public SerializerProvider(ISerializationManager ser, IEnumerable<Type> typeSerializers) : this(ser)
{
@@ -122,8 +182,9 @@ public sealed partial class SerializationManager
}
}
private Dictionary<Type, Dictionary<(Type ObjectType, Type NodeType), object>> _typeNodeSerializers = new();
private Dictionary<Type, Dictionary<Type, object>> _typeSerializers = new();
private (object? Regular, object? Generic, bool Init)[] _typeNodeSerializersArray = [];
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<(Type ObjectType, Type NodeType), object>> _typeNodeSerializers = new();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, object>> _typeSerializers = new();
// TODO make this a 1d array containing the 6 interfaces
/// <summary>
@@ -131,15 +192,15 @@ public sealed partial class SerializationManager
/// that they serialize.
/// <see cref="SerializationManager.SerializerInterfaces"/> for the first index.
/// </summary>
private (object? Regular, object? Generic)[]?[] _typeSerializersArray = new (object? Regular, object? Generic)[]?[] { };
private (object? Regular, object? Generic)[]?[] _typeSerializersArray = [];
private Dictionary<Type, Dictionary<(Type ObjectType, Type NodeType), Type>> _genericTypeNodeSerializers = new();
private Dictionary<Type, Dictionary<Type, Type>> _genericTypeSerializers = new();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<(Type ObjectType, Type NodeType), Type>> _genericTypeNodeSerializers = new();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<Type, Type>> _genericTypeSerializers = new();
private List<Type> _typeNodeInterfaces = new();
private List<Type> _typeInterfaces = new();
private readonly List<Type> _typeNodeInterfaces = new();
private readonly List<Type> _typeInterfaces = new();
private readonly object _lock = new();
private readonly Lock _lock = new();
#region GetSerializerMethods
@@ -148,11 +209,51 @@ public sealed partial class SerializationManager
where TNode : DataNode
{
serializer = default;
if (!TryGetTypeNodeSerializer(typeof(TInterface).GetGenericTypeDefinition(), typeof(TType), typeof(TNode), out var rawSerializer))
return false;
object? rawSerializer;
var index = TypeSerializerType<TInterface, TType, TNode>.Index;
if (index < _typeNodeSerializersArray.Length)
{
ref var serializers = ref _typeNodeSerializersArray[index];
if (serializers.Init)
{
if (serializers.Regular != null)
{
serializer = (TInterface) serializers.Regular;
return true;
}
serializer = (TInterface)rawSerializer;
return true;
if (serializers.Generic != null)
{
serializer = (TInterface) serializers.Generic;
return true;
}
return false;
}
if (TryGetTypeNodeSerializer(typeof(TInterface).GetGenericTypeDefinition(),
typeof(TType),
typeof(TNode),
out rawSerializer))
{
serializer = (TInterface) rawSerializer;
return true;
}
serializers.Init = true;
return false;
}
if (TryGetTypeNodeSerializer(typeof(TInterface).GetGenericTypeDefinition(),
typeof(TType),
typeof(TNode),
out rawSerializer))
{
serializer = (TInterface) rawSerializer;
return true;
}
return false;
}
internal bool TryGetTypeNodeSerializerArray<TInterface, TType, TNode>([NotNullWhen(true)] out TInterface? serializer)
@@ -169,30 +270,28 @@ public sealed partial class SerializationManager
public bool TryGetTypeNodeSerializer(Type interfaceType, Type objectType, Type nodeType, [NotNullWhen(true)] out object? serializer)
{
lock (_lock)
if (_typeNodeSerializers.TryGetValue(interfaceType, out var typeNodeSerializers) &&
typeNodeSerializers.TryGetValue((objectType, nodeType), out serializer))
return true;
if (_genericTypeNodeSerializers.TryGetValue(interfaceType, out var genericTypeNodeSerializers) &&
objectType.IsGenericType)
{
if (_typeNodeSerializers.TryGetValue(interfaceType, out var typeNodeSerializers) &&
typeNodeSerializers.TryGetValue((objectType, nodeType), out serializer))
return true;
if (_genericTypeNodeSerializers.TryGetValue(interfaceType, out var genericTypeNodeSerializers) &&
objectType.IsGenericType)
var typeDef = objectType.GetGenericTypeDefinition();
foreach (var (key, val) in genericTypeNodeSerializers)
{
var typeDef = objectType.GetGenericTypeDefinition();
foreach (var (key, val) in genericTypeNodeSerializers)
{
if (typeDef.HasSameMetadataDefinitionAs(key.ObjectType) && nodeType == key.NodeType)
{
var serializerType = val.MakeGenericType(objectType.GetGenericArguments());
serializer = RegisterSerializer(serializerType)!;
return true;
}
}
}
if (!typeDef.HasSameMetadataDefinitionAs(key.ObjectType) || nodeType != key.NodeType)
continue;
serializer = null;
return false;
var serializerType = val.MakeGenericType(objectType.GetGenericArguments());
serializer = RegisterSerializer(serializerType)!;
RegisterIndexedNodeSerializer(interfaceType, objectType, key.NodeType, serializer, false);
return true;
}
}
serializer = null;
return false;
}
public TInterface GetTypeNodeSerializer<TInterface, TType, TNode>()
@@ -226,31 +325,34 @@ public sealed partial class SerializationManager
public bool TryGetTypeSerializer(Type interfaceType, Type objectType, [NotNullWhen(true)] out object? serializer)
{
lock (_lock)
if (_typeSerializers.TryGetValue(interfaceType, out var typeSerializers) &&
typeSerializers.TryGetValue(objectType, out serializer))
return true;
if (_genericTypeSerializers.TryGetValue(interfaceType, out var genericTypeSerializers) &&
objectType.IsGenericType)
{
if (_typeSerializers.TryGetValue(interfaceType, out var typeSerializers) &&
typeSerializers.TryGetValue(objectType, out serializer))
return true;
if (_genericTypeSerializers.TryGetValue(interfaceType, out var genericTypeSerializers) &&
objectType.IsGenericType)
var typeDef = objectType.GetGenericTypeDefinition();
foreach (var (key, val) in genericTypeSerializers)
{
var typeDef = objectType.GetGenericTypeDefinition();
foreach (var (key, val) in genericTypeSerializers)
{
if (typeDef.HasSameMetadataDefinitionAs(key))
{
var serializerType = val.MakeGenericType(objectType.GetGenericArguments());
serializer = RegisterSerializer(serializerType)!;
RegisterIndexedSerializer(objectType, SerializerInterfaces.IndexOf(interfaceType), serializer, false);
return true;
}
}
}
if (!typeDef.HasSameMetadataDefinitionAs(key))
continue;
serializer = null;
return false;
var serializerType = val.MakeGenericType(objectType.GetGenericArguments());
serializer = RegisterSerializer(serializerType)!;
RegisterIndexedSerializer(
objectType,
SerializerInterfaces.IndexOf(interfaceType),
serializer,
false
);
return true;
}
}
serializer = null;
return false;
}
internal bool TryGetCopierOrCreator<TType>(out ITypeCopier<TType>? copier, out ITypeCopyCreator<TType>? copyCreator)
@@ -311,95 +413,100 @@ public sealed partial class SerializationManager
private object RegisterSerializer(Type type, object obj)
{
lock (_lock)
foreach (var @interface in type.GetInterfaces())
{
foreach (var @interface in type.GetInterfaces())
if (!@interface.IsGenericType) continue;
foreach (var typeInterface in _typeInterfaces)
{
if (!@interface.IsGenericType) continue;
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
continue;
for (var i = 0; i < _typeInterfaces.Count; i++)
{
var typeInterface = _typeInterfaces[i];
if (@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
{
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 1)
throw new InvalidGenericParameterCountException();
_typeSerializers.GetOrNew(typeInterface).Add(arguments[0], obj);
RegisterIndexedSerializer(arguments[0], SerializerInterfaces.IndexOf(typeInterface), obj, true);
}
}
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 1)
throw new InvalidGenericParameterCountException();
foreach (var typeInterface in _typeNodeInterfaces)
{
if (@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
{
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 2)
throw new InvalidGenericParameterCountException();
_typeNodeSerializers.GetOrNew(typeInterface).Add((arguments[0], arguments[1]), obj);
}
}
_typeSerializers.GetOrNew(typeInterface).TryAdd(arguments[0], obj);
RegisterIndexedSerializer(
arguments[0],
SerializerInterfaces.IndexOf(typeInterface),
obj,
true
);
}
return obj;
foreach (var typeInterface in _typeNodeInterfaces)
{
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
continue;
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 2)
throw new InvalidGenericParameterCountException();
_typeNodeSerializers.GetOrAdd(typeInterface, _ => new())
.TryAdd((arguments[0], arguments[1]), obj);
RegisterIndexedNodeSerializer(
typeInterface,
arguments[0],
arguments[1],
obj,
true
);
}
}
return obj;
}
public T? RegisterSerializer<T>() => (T?)RegisterSerializer(typeof(T));
public object? RegisterSerializer(Type type)
{
lock (_lock)
if (!type.IsGenericTypeDefinition)
return RegisterSerializer(type, _ser.CreateSerializer(type));
var typeArguments = type.GetGenericArguments();
foreach (var @interface in type.GetInterfaces())
{
if (type.IsGenericTypeDefinition)
foreach (var typeInterface in _typeInterfaces)
{
var typeArguments = type.GetGenericArguments();
foreach (var @interface in type.GetInterfaces())
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
continue;
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 1)
throw new InvalidGenericParameterCountException();
var objArguments = arguments[0].GetGenericArguments();
for (var i = 0; i < typeArguments.Length; i++)
{
foreach (var typeInterface in _typeInterfaces)
{
if (@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
{
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 1)
throw new InvalidGenericParameterCountException();
var objArguments = arguments[0].GetGenericArguments();
for (int i = 0; i < typeArguments.Length; i++)
{
if (typeArguments[i] != objArguments[i])
throw new GenericParameterMismatchException();
}
_genericTypeSerializers.GetOrNew(typeInterface).Add(arguments[0], type);
}
}
foreach (var typeInterface in _typeNodeInterfaces)
{
if (@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
{
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 2)
throw new InvalidGenericParameterCountException();
var objArguments = arguments[0].GetGenericArguments();
for (int i = 0; i < typeArguments.Length; i++)
{
if (typeArguments[i] != objArguments[i])
throw new GenericParameterMismatchException();
}
_genericTypeNodeSerializers.GetOrNew(typeInterface)
.Add((arguments[0], arguments[1]), type);
}
}
if (typeArguments[i] != objArguments[i])
throw new GenericParameterMismatchException();
}
return null;
_genericTypeSerializers.GetOrNew(typeInterface).TryAdd(arguments[0], type);
}
return RegisterSerializer(type, _ser.CreateSerializer(type));
foreach (var typeInterface in _typeNodeInterfaces)
{
if (!@interface.GetGenericTypeDefinition().HasSameMetadataDefinitionAs(typeInterface))
continue;
var arguments = @interface.GetGenericArguments();
if (arguments.Length != 2)
throw new InvalidGenericParameterCountException();
var objArguments = arguments[0].GetGenericArguments();
for (var i = 0; i < typeArguments.Length; i++)
{
if (typeArguments[i] != objArguments[i])
throw new GenericParameterMismatchException();
}
_genericTypeNodeSerializers.GetOrNew(typeInterface).TryAdd((arguments[0], arguments[1]), type);
}
}
return null;
}
//todo paul serv3 is there a better way than comparing names here?
@@ -420,7 +527,7 @@ public sealed partial class SerializationManager
if (genericInterface.HasSameMetadataDefinitionAs(genericTypeNode))
{
var genericInterfaceParams = genericInterface.GetGenericArguments();
for (int i = 0; i < genericParams.Length; i++)
for (var i = 0; i < genericParams.Length; i++)
{
if (genericParams[i].Name != genericInterfaceParams[i].Name)
throw new GenericParameterMismatchException();
@@ -431,7 +538,7 @@ public sealed partial class SerializationManager
else if (genericInterface.HasSameMetadataDefinitionAs(genericType))
{
var genericInterfaceParams = genericInterface.GetGenericArguments();
for (int i = 0; i < genericParams.Length; i++)
for (var i = 0; i < genericParams.Length; i++)
{
if (genericParams[i].Name != genericInterfaceParams[i].Name)
throw new GenericParameterMismatchException();
@@ -447,9 +554,7 @@ public sealed partial class SerializationManager
{
var id = SerializedType.GetId(elementType);
if (id >= _typeSerializersArray.Length)
{
Array.Resize(ref _typeSerializersArray, (id + 1) * 2);
}
var array = _typeSerializersArray[id];
if (array == null)
@@ -459,12 +564,26 @@ public sealed partial class SerializationManager
}
if (regular)
{
array[interfaceIndex].Regular = serializer;
}
else
{
array[interfaceIndex].Generic = serializer;
}
private void RegisterIndexedNodeSerializer(Type interfaceIndex, Type elementType, Type nodeType, object serializer, bool regular)
{
lock (_lock)
{
var id = TypeSerializerType.GetId(interfaceIndex, elementType, nodeType);
if (id >= _typeNodeSerializersArray.Length)
Array.Resize(ref _typeNodeSerializersArray, (id + 1) * 2);
ref var tuple = ref _typeNodeSerializersArray[id];
if (regular)
tuple.Regular = serializer;
else
tuple.Generic = serializer;
tuple.Init = true;
}
}
@@ -474,7 +593,7 @@ public sealed partial class SerializationManager
private static class SerializedType
{
internal static int Id;
private static readonly object Lock = new();
private static readonly Lock Lock = new();
internal static int GetId(Type type)
{
@@ -515,4 +634,32 @@ public sealed partial class SerializationManager
SerializationGenerated = serializationGenerated;
}
}
internal static class TypeSerializerType
{
internal static int GetId(Type typeInterface, Type type, Type typeNode)
{
var interfaceIndex = SerializerInterfaces.IndexOf(typeInterface.GetGenericTypeDefinition());
if (interfaceIndex == -1)
throw new ArgumentException($"Invalid type interface: {typeInterface}");
var nodeIndex = Nodes.IndexOf(typeNode);
if (nodeIndex == -1)
throw new ArgumentException($"Invalid node type: {typeInterface}");
return SerializedType.GetId(type) *
(SerializerInterfaces.Length + Nodes.Length) +
interfaceIndex +
nodeIndex;
}
}
internal static class TypeSerializerType<TInterface, TType, TNode>
{
// ReSharper disable once StaticMemberInGenericType
internal static readonly int Index = SerializedType<TType>.Information.Id *
(SerializerInterfaces.Length + Nodes.Length) +
SerializerInterfaces.IndexOf(typeof(TInterface).GetGenericTypeDefinition()) +
Nodes.IndexOf(typeof(TNode));
}
}
@@ -371,17 +371,17 @@ namespace Robust.Shared.Serialization.Manager
}
#pragma warning disable CS0618
private static void RunAfterHook<TValue>(TValue instance, SerializationHookContext ctx)
{
if (instance is ISerializationHooks hooks)
RunAfterHookGenerated(hooks, ctx);
}
private static void RunAfterHookGenerated<TValue>(TValue instance, SerializationHookContext ctx) where TValue : ISerializationHooks
internal static void TryRunAfterHook<TValue>(TValue instance, SerializationHookContext ctx)
{
if (ctx.SkipHooks)
return;
if (instance is ISerializationHooks hooks)
ForceRunAfterHookGenerated(hooks, ctx);
}
private static void ForceRunAfterHookGenerated<TValue>(TValue instance, SerializationHookContext ctx) where TValue : ISerializationHooks
{
DebugTools.Assert(!typeof(TValue).IsValueType, "ISerializationHooks must only be used on reference types");
if (ctx.DeferQueue != null)