145 lines
5.7 KiB
C#
145 lines
5.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using BinaryPack;
|
|
using BinaryPack.Attributes;
|
|
using NitroxModel.Networking;
|
|
|
|
namespace NitroxModel.Packets
|
|
{
|
|
[Serializable]
|
|
public abstract class Packet
|
|
{
|
|
private static readonly Dictionary<Type, PropertyInfo[]> cachedPropertiesByType = new();
|
|
private static readonly StringBuilder toStringBuilder = new();
|
|
private static readonly object lockObject = new();
|
|
|
|
public static void InitSerializer()
|
|
{
|
|
static IEnumerable<Type> FindTypesInModelAssemblies()
|
|
{
|
|
return AppDomain.CurrentDomain.GetAssemblies()
|
|
.Where(assembly => new[] { nameof(NitroxModel), "NitroxModel-Subnautica" }
|
|
.Contains(assembly.GetName().Name))
|
|
.SelectMany(assembly =>
|
|
{
|
|
try
|
|
{
|
|
return assembly.GetTypes();
|
|
}
|
|
catch (ReflectionTypeLoadException e)
|
|
{
|
|
return e.Types.Where(t => t != null);
|
|
}
|
|
});
|
|
}
|
|
|
|
static IEnumerable<Type> FindUnionBaseTypes() => FindTypesInModelAssemblies()
|
|
.Where(t => t.IsAbstract && !t.IsSealed && (!t.BaseType?.IsAbstract ?? true) && !t.ContainsGenericParameters);
|
|
|
|
lock (lockObject)
|
|
{
|
|
foreach (Type type in FindUnionBaseTypes())
|
|
{
|
|
BinaryConverter.RegisterUnion(type, FindTypesInModelAssemblies()
|
|
.Where(t => type.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface)
|
|
.OrderByDescending(t =>
|
|
{
|
|
Type current = t;
|
|
int levels = 0;
|
|
|
|
while (current != type && current != null)
|
|
{
|
|
current = current.BaseType;
|
|
levels++;
|
|
}
|
|
|
|
return levels;
|
|
})
|
|
.ThenBy(t => t.FullName, StringComparer.Ordinal)
|
|
.ToArray());
|
|
}
|
|
|
|
// This will initialize the processor for Wrapper which will initialize all the others
|
|
_ = BinaryConverter.Serialize(new Wrapper());
|
|
}
|
|
}
|
|
|
|
[IgnoredMember]
|
|
public NitroxDeliveryMethod.DeliveryMethod DeliveryMethod { get; protected set; } = NitroxDeliveryMethod.DeliveryMethod.RELIABLE_ORDERED;
|
|
|
|
[IgnoredMember]
|
|
public UdpChannelId UdpChannel { get; protected set; } = UdpChannelId.DEFAULT;
|
|
|
|
public enum UdpChannelId : byte
|
|
{
|
|
DEFAULT = 0,
|
|
MOVEMENTS = 1,
|
|
}
|
|
|
|
public byte[] Serialize()
|
|
{
|
|
return BinaryConverter.Serialize(new Wrapper(this));
|
|
}
|
|
|
|
public static Packet Deserialize(byte[] data)
|
|
{
|
|
return BinaryConverter.Deserialize<Wrapper>(data).Packet;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
Type packetType = GetType();
|
|
|
|
if (!cachedPropertiesByType.TryGetValue(packetType, out PropertyInfo[] properties))
|
|
{
|
|
properties = packetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
|
.Where(x => x.Name is not nameof(DeliveryMethod) and not nameof(UdpChannel)).ToArray();
|
|
cachedPropertiesByType.Add(packetType, properties);
|
|
}
|
|
|
|
toStringBuilder.Clear();
|
|
toStringBuilder.Append($"[{packetType.Name}: ");
|
|
foreach (PropertyInfo property in properties)
|
|
{
|
|
object propertyValue = property.GetValue(this);
|
|
if (propertyValue is IList propertyList)
|
|
{
|
|
toStringBuilder.Append($"{property.Name}: {propertyList.Count}, ");
|
|
}
|
|
else
|
|
{
|
|
toStringBuilder.Append($"{property.Name}: {propertyValue}, ");
|
|
}
|
|
}
|
|
|
|
toStringBuilder.Remove(toStringBuilder.Length - 2, 2);
|
|
toStringBuilder.Append(']');
|
|
|
|
return toStringBuilder.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wrapper which is used to serialize packets in BinaryPack.
|
|
/// We cannot serialize Packets directly because
|
|
/// <p>
|
|
/// 1) We will not know what type to deserialize to and
|
|
/// 2) The root object must have a callable constructor so it can't be abstract
|
|
/// </p>
|
|
/// This type solves both problems and only adds a single byte to the data.
|
|
/// </summary>
|
|
public readonly struct Wrapper
|
|
{
|
|
public Packet Packet { get; init; } = null;
|
|
|
|
public Wrapper(Packet packet)
|
|
{
|
|
Packet = packet;
|
|
}
|
|
}
|
|
}
|
|
}
|