Unstable 0.17.1.0
This commit is contained in:
@@ -5,6 +5,8 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
@@ -42,6 +44,13 @@ namespace Barotrauma
|
||||
public int NumberOfBits = 8;
|
||||
public bool IncludeColorAlpha = false;
|
||||
public int ArrayMaxSize = ushort.MaxValue;
|
||||
|
||||
public readonly int OrderKey;
|
||||
|
||||
public NetworkSerialize([CallerLineNumber] int lineNumber = 0)
|
||||
{
|
||||
OrderKey = lineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,6 +61,7 @@ namespace Barotrauma
|
||||
public readonly struct ReadWriteBehavior
|
||||
{
|
||||
public delegate dynamic? ReadDelegate(IReadMessage inc, Type type, NetworkSerialize attribute);
|
||||
|
||||
public delegate void WriteDelegate(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg);
|
||||
|
||||
public readonly ReadDelegate ReadAction;
|
||||
@@ -64,6 +74,58 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct CachedReflectedVariable
|
||||
{
|
||||
public delegate object? GetValueDelegate(object? obj);
|
||||
|
||||
public delegate void SetValueDelegate(object? obj, object? value);
|
||||
|
||||
public readonly Type Type;
|
||||
public readonly ReadWriteBehavior Behavior;
|
||||
public readonly NetworkSerialize Attribute;
|
||||
public readonly SetValueDelegate SetValue;
|
||||
public readonly GetValueDelegate GetValue;
|
||||
public readonly bool HasOwnAttribute;
|
||||
|
||||
public CachedReflectedVariable(MemberInfo info, ReadWriteBehavior behavior, Type baseClassType)
|
||||
{
|
||||
Behavior = behavior;
|
||||
|
||||
switch (info)
|
||||
{
|
||||
case PropertyInfo pi:
|
||||
Type = pi.PropertyType;
|
||||
GetValue = pi.GetValue;
|
||||
SetValue = pi.SetValue;
|
||||
break;
|
||||
case FieldInfo fi:
|
||||
Type = fi.FieldType;
|
||||
GetValue = fi.GetValue;
|
||||
SetValue = fi.SetValue;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Expected {nameof(FieldInfo)} or {nameof(PropertyInfo)} but found {info.GetType()}.", nameof(info));
|
||||
}
|
||||
|
||||
if (info.GetCustomAttribute<NetworkSerialize>() is { } ownAttriute)
|
||||
{
|
||||
HasOwnAttribute = true;
|
||||
Attribute = ownAttriute;
|
||||
}
|
||||
else if (baseClassType.GetCustomAttribute<NetworkSerialize>() is { } globalAttribute)
|
||||
{
|
||||
HasOwnAttribute = false;
|
||||
Attribute = globalAttribute;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to serialize \"{Type}\" in \"{baseClassType}\" because it has no {nameof(NetworkSerialize)} attribute.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, ImmutableArray<CachedReflectedVariable>> CachedVariables = new Dictionary<Type, ImmutableArray<CachedReflectedVariable>>();
|
||||
|
||||
private static readonly ImmutableDictionary<Type, ReadWriteBehavior> TypeBehaviors = new Dictionary<Type, ReadWriteBehavior>
|
||||
{
|
||||
{ typeof(Boolean), new ReadWriteBehavior(ReadBoolean, WriteDynamic) },
|
||||
@@ -82,8 +144,6 @@ namespace Barotrauma
|
||||
{ typeof(Vector2), new ReadWriteBehavior(ReadVector2, WriteVector2) }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private static readonly ReadWriteBehavior InvalidReadWriteBehavior = new ReadWriteBehavior(ReadInvalid, WriteInvalid);
|
||||
|
||||
private static readonly ImmutableDictionary<Predicate<Type>, ReadWriteBehavior> TypePredicates = new Dictionary<Predicate<Type>, ReadWriteBehavior>
|
||||
{
|
||||
// Arrays
|
||||
@@ -99,15 +159,18 @@ namespace Barotrauma
|
||||
{ type => Nullable.GetUnderlyingType(type) != null, new ReadWriteBehavior(ReadNullable, WriteNullable) },
|
||||
|
||||
// Option
|
||||
{ type => type.GetGenericTypeDefinition() == typeof(Option<>), new ReadWriteBehavior(ReadOption, WriteOption) }
|
||||
{ type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Option<>), new ReadWriteBehavior(ReadOption, WriteOption) }
|
||||
}.ToImmutableDictionary();
|
||||
|
||||
private static readonly ReadWriteBehavior InvalidReadWriteBehavior = new ReadWriteBehavior(ReadInvalid, WriteInvalid);
|
||||
|
||||
private static readonly Dictionary<Type, MethodInfo> cachedSomeCreateMethods = new Dictionary<Type, MethodInfo>();
|
||||
private static readonly Dictionary<Type, MethodInfo> cachedNoneCreateMethod = new Dictionary<Type, MethodInfo>();
|
||||
|
||||
private static void WriteInvalid(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg) => throw new InvalidOperationException($"Type {obj?.GetType()} cannot be serialized. Did you forget to implement INetSerializableStruct?");
|
||||
private static void WriteInvalid(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg) =>
|
||||
throw new SerializationException($"Type {obj?.GetType()} cannot be serialized. Did you forget to implement {nameof(INetSerializableStruct)}?");
|
||||
|
||||
private static dynamic ReadInvalid(IReadMessage inc, Type type, NetworkSerialize attribute) => throw new InvalidOperationException($"Type {type} cannot be deserialized. Did you forget to implement INetSerializableStruct?");
|
||||
private static dynamic ReadInvalid(IReadMessage inc, Type type, NetworkSerialize attribute) => throw new SerializationException($"Type {type} cannot be deserialized. Did you forget to implement {nameof(INetSerializableStruct)}?");
|
||||
|
||||
private static void WriteOption(dynamic? obj, NetworkSerialize attribute, IWriteMessage msg)
|
||||
{
|
||||
@@ -131,7 +194,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Option type was neither None<> or Some<>");
|
||||
throw new ArgumentOutOfRangeException(nameof(obj), "Option type was neither None or Some");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +210,7 @@ namespace Barotrauma
|
||||
if (TryFindBehavior(underlyingType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
dynamic? value = behavior.ReadAction(inc, underlyingType, attribute);
|
||||
return GetCreateMethod(typeof(Some<>), underlyingType, cachedSomeCreateMethods).Invoke(null, new []{ value });
|
||||
return GetCreateMethod(typeof(Some<>), underlyingType, cachedSomeCreateMethods).Invoke(null, new[] { value });
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Could not find suitable behavior for type {underlyingType} in {nameof(ReadOption)}");
|
||||
@@ -349,7 +412,7 @@ namespace Barotrauma
|
||||
private static dynamic ReadDouble(IReadMessage inc, Type type, NetworkSerialize attribute) => inc.ReadDouble();
|
||||
|
||||
private static dynamic ReadString(IReadMessage inc, Type type, NetworkSerialize attribute) => inc.ReadString();
|
||||
|
||||
|
||||
private static dynamic ReadIdentifier(IReadMessage inc, Type type, NetworkSerialize attribute) => inc.ReadIdentifier();
|
||||
|
||||
private static dynamic ReadColor(IReadMessage inc, Type type, NetworkSerialize attribute) => attribute.IncludeColorAlpha ? inc.ReadColorR8G8B8A8() : inc.ReadColorR8G8B8();
|
||||
@@ -411,7 +474,7 @@ namespace Barotrauma
|
||||
return new Range<int>(values.Min(), values.Max());
|
||||
}
|
||||
|
||||
public static bool TryFindBehavior(Type type, out ReadWriteBehavior behavior)
|
||||
private static bool TryFindBehavior(Type type, out ReadWriteBehavior behavior)
|
||||
{
|
||||
if (TypeBehaviors.TryGetValue(type, out behavior)) { return true; }
|
||||
|
||||
@@ -427,6 +490,46 @@ namespace Barotrauma
|
||||
behavior = InvalidReadWriteBehavior;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ImmutableArray<CachedReflectedVariable> GetPropertiesAndFields(Type type, Type baseClassType)
|
||||
{
|
||||
if (CachedVariables.TryGetValue(type, out var cached)) { return cached; }
|
||||
|
||||
List<CachedReflectedVariable> variables = new List<CachedReflectedVariable>();
|
||||
|
||||
IEnumerable<PropertyInfo> propertyInfos = type.GetProperties().Where(HasAttribute);
|
||||
IEnumerable<FieldInfo> fieldInfos = type.GetFields().Where(HasAttribute);
|
||||
|
||||
foreach (PropertyInfo info in propertyInfos)
|
||||
{
|
||||
if (TryFindBehavior(info.PropertyType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
variables.Add(new CachedReflectedVariable(info, behavior, baseClassType));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SerializationException($"Unable to serialize type \"{type}\".");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FieldInfo info in fieldInfos)
|
||||
{
|
||||
if (TryFindBehavior(info.FieldType, out ReadWriteBehavior behavior))
|
||||
{
|
||||
variables.Add(new CachedReflectedVariable(info, behavior, baseClassType));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SerializationException($"Unable to serialize type \"{type}\".");
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableArray<CachedReflectedVariable> array = variables.All(v => v.HasOwnAttribute) ? variables.OrderBy(v => v.Attribute.OrderKey).ToImmutableArray() : variables.ToImmutableArray();
|
||||
CachedVariables.Add(type, array);
|
||||
return array;
|
||||
|
||||
bool HasAttribute(MemberInfo info) => (info.GetCustomAttribute<NetworkSerialize>() ?? baseClassType.GetCustomAttribute<NetworkSerialize>()) != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -512,38 +615,11 @@ namespace Barotrauma
|
||||
object? newObject = Activator.CreateInstance(type);
|
||||
if (newObject is null) { return default!; }
|
||||
|
||||
PropertyInfo[] properties = type.GetProperties();
|
||||
foreach (PropertyInfo info in properties)
|
||||
var properties = NetSerializableProperties.GetPropertiesAndFields(type, type);
|
||||
foreach (NetSerializableProperties.CachedReflectedVariable property in properties)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, newObject);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.PropertyType, out var behavior))
|
||||
{
|
||||
object? value = behavior.ReadAction(inc, info.PropertyType, attribute);
|
||||
info.SetValue(newObject, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Unsupported property type \"{info.PropertyType}\" in {newObject}!");
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo[] fields = type.GetFields();
|
||||
foreach (FieldInfo info in fields)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, newObject);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.FieldType, out var behavior))
|
||||
{
|
||||
object? value = behavior.ReadAction(inc, info.FieldType, attribute);
|
||||
info.SetValue(newObject, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Unsupported field type \"{info.FieldType}\" in {newObject}!");
|
||||
}
|
||||
NetworkSerialize attribute = property.Attribute;
|
||||
property.SetValue(newObject, property.Behavior.ReadAction(inc, property.Type, attribute));
|
||||
}
|
||||
|
||||
return newObject;
|
||||
@@ -575,39 +651,34 @@ namespace Barotrauma
|
||||
/// <param name="msg">Outgoing network message</param>
|
||||
public void Write(IWriteMessage msg)
|
||||
{
|
||||
PropertyInfo[] properties = GetType().GetProperties();
|
||||
foreach (PropertyInfo info in properties)
|
||||
Type type = GetType();
|
||||
var properties = NetSerializableProperties.GetPropertiesAndFields(type, type);
|
||||
foreach (NetSerializableProperties.CachedReflectedVariable property in properties)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, this);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.PropertyType, out var behavior))
|
||||
{
|
||||
behavior.WriteAction(info.GetValue(this), attribute, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unsupported property type \"{info.PropertyType}\" in {this}");
|
||||
}
|
||||
}
|
||||
|
||||
FieldInfo[] fields = GetType().GetFields();
|
||||
foreach (FieldInfo info in fields)
|
||||
{
|
||||
NetworkSerialize? attribute = GetAttribute(info, this);
|
||||
if (attribute is null) { continue; }
|
||||
|
||||
if (NetSerializableProperties.TryFindBehavior(info.FieldType, out var behavior))
|
||||
{
|
||||
behavior.WriteAction(info.GetValue(this), attribute, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unsupported field type \"{info.FieldType}\" in {this}");
|
||||
}
|
||||
NetworkSerialize attribute = property.Attribute;
|
||||
property.Behavior.WriteAction(property.GetValue(this), attribute, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static NetworkSerialize? GetAttribute(MemberInfo info, object baseClass) => info.GetCustomAttribute<NetworkSerialize>() ?? baseClass.GetType().GetCustomAttribute<NetworkSerialize>();
|
||||
public static class WriteOnlyMessageExtensions
|
||||
{
|
||||
#if CLIENT
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ClientPacketHeader header)
|
||||
{
|
||||
msg.Write((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#elif SERVER
|
||||
public static IWriteMessage WithHeader(this IWriteMessage msg, ServerPacketHeader header)
|
||||
{
|
||||
msg.Write((byte)header);
|
||||
return msg;
|
||||
}
|
||||
#endif
|
||||
public static void Write(this IWriteMessage msg, INetSerializableStruct serializableStruct)
|
||||
{
|
||||
serializableStruct.Write(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user