first commit
This commit is contained in:
144
NitroxModel/Packets/Packet.cs
Normal file
144
NitroxModel/Packets/Packet.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user