forked from SubnauticaNitrox/Nitrox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPacket.cs
146 lines (127 loc) · 5.74 KB
/
Packet.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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)
.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
{
DEFAULT = 0,
PLAYER_MOVEMENT = 1,
VEHICLE_MOVEMENT = 2,
PLAYER_STATS = 3
}
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;
}
}
}
}