Skip to content

Commit c22fb0c

Browse files
committed
Added a possible workaround for MirrorMethods OnInit method randomly crashing servers on startup - this should improve startup times as well.
1 parent 0630ecf commit c22fb0c

1 file changed

Lines changed: 123 additions & 40 deletions

File tree

LabExtended/API/MirrorMethods.cs

Lines changed: 123 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using LabExtended.Core;
2-
using LabExtended.Utilities;
3-
using LabExtended.Attributes;
42
using LabExtended.Extensions;
53

64
using Mirror;
75

6+
using System.Reflection;
87
using System.Reflection.Emit;
98

109
using UnityEngine;
@@ -22,8 +21,17 @@ namespace LabExtended.API;
2221
/// </summary>
2322
public static class MirrorMethods
2423
{
24+
private static bool hasInitialized;
25+
26+
private static Dictionary<string, string> rpcNames = new();
27+
private static Dictionary<string, int> rpcHashes = new();
28+
private static Dictionary<string, ulong> dirtyBits = new();
29+
private static Dictionary<Type, MethodInfo> writers = new();
30+
31+
private static Action<NetworkIdentity, NetworkConnection> sendSpawnMessage;
32+
2533
/// <summary>
26-
/// Gets the maximum length of a string.
34+
/// Gets the maximum length of a string (amount of UTF-8 bytes) that can be sent over the network.
2735
/// </summary>
2836
public const int MaxStringLength = 65534;
2937

@@ -32,31 +40,76 @@ public static class MirrorMethods
3240
/// <remarks>Keys are formatted as the declaring type of the property and then the name of the property
3341
/// (MyType.MyProperty)</remarks>
3442
/// </summary>
35-
public static Dictionary<string, string> RpcNames { get; } = new();
43+
public static Dictionary<string, string> RpcNames
44+
{
45+
get
46+
{
47+
if (!hasInitialized)
48+
Init();
49+
50+
return rpcNames;
51+
}
52+
}
3653

3754
/// <summary>
3855
/// Gets hashes of all RPCs.
3956
/// <remarks>Keys are formatted as the declaring type of the property and then the name of the property
4057
/// (MyType.MyProperty)</remarks>
4158
/// </summary>
42-
public static Dictionary<string, int> RpcHashes { get; } = new();
59+
public static Dictionary<string, int> RpcHashes
60+
{
61+
get
62+
{
63+
if (!hasInitialized)
64+
Init();
65+
66+
return rpcHashes;
67+
}
68+
}
4369

4470
/// <summary>
4571
/// Gets dirty bits of all network properties
4672
/// <remarks>Keys are formatted as the declaring type of the property and then the name of the property
4773
/// (MyType.MyProperty)</remarks>
4874
/// </summary>
49-
public static Dictionary<string, ulong> DirtyBits { get; } = new();
75+
public static Dictionary<string, ulong> DirtyBits
76+
{
77+
get
78+
{
79+
if (!hasInitialized)
80+
Init();
81+
82+
return dirtyBits;
83+
}
84+
}
5085

5186
/// <summary>
5287
/// Gets Mirror-generated network writer extensions.
5388
/// </summary>
54-
public static Dictionary<Type, Func<object, object[], object>> Writers { get; } = new();
89+
public static Dictionary<Type, MethodInfo> Writers
90+
{
91+
get
92+
{
93+
if (!hasInitialized)
94+
Init();
95+
96+
return writers;
97+
}
98+
}
5599

56100
/// <summary>
57101
/// Gets the <see cref="NetworkServer.SendSpawnMessage"/> delegate.
58102
/// </summary>
59-
public static Action<NetworkIdentity, NetworkConnection> SendSpawnMessage { get; private set; }
103+
public static Action<NetworkIdentity, NetworkConnection> SendSpawnMessage
104+
{
105+
get
106+
{
107+
if (!hasInitialized)
108+
Init();
109+
110+
return sendSpawnMessage;
111+
}
112+
}
60113

61114
/// <summary>
62115
/// Attempts to get a specific behaviour component of a network identity.
@@ -848,7 +901,7 @@ public static void WriteCustomSyncVar(this NetworkWriter writer, NetworkBehaviou
848901
if (!Writers.TryGetValue(customValue.GetType(), out var definedValueWriter))
849902
throw new Exception($"Type {customValue.GetType().FullName} does not have a defined writer");
850903

851-
valueWriter = (x, y) => definedValueWriter(null, [x, y]);
904+
valueWriter = (x, y) => definedValueWriter.Invoke(x, [y]);
852905
}
853906

854907
WriteCustomSyncVar(writer, behaviour.netId, dirtyBit, behaviour.ComponentIndex, customValue, valueWriter);
@@ -880,7 +933,7 @@ public static void WriteCustomSyncVar<T>(this NetworkWriter writer, NetworkBehav
880933
if (!Writers.TryGetValue(customValue.GetType(), out var definedValueWriter))
881934
throw new Exception($"Type {customValue.GetType().FullName} does not have a defined writer");
882935

883-
valueWriter = (x, y) => definedValueWriter(null, [x, y]);
936+
valueWriter = (x, y) => definedValueWriter.Invoke(x, [y]);
884937
}
885938

886939
WriteCustomSyncVar(writer, behaviour.netId, dirtyBit, behaviour.ComponentIndex, customValue,
@@ -1077,12 +1130,12 @@ private static void InitIdentityServerSide(NetworkIdentity identity, NetworkConn
10771130
}
10781131

10791132
// TODO: There's a weird bug that causes the Mono runtime to randomly crash at random points in this method.
1080-
[LoaderInitialize(1)]
1081-
private static void OnInit()
1133+
// Seems to be caused by the Mono runtimn as no exceptions are thrown (and it doesn't happen on Windows).
1134+
private static void Init()
10821135
{
10831136
try
10841137
{
1085-
SendSpawnMessage =
1138+
sendSpawnMessage =
10861139
typeof(NetworkServer).FindMethod(x => x.Name == "SendSpawnMessage")
10871140
.CreateDelegate(typeof(Action<NetworkIdentity, NetworkConnection>)) as
10881141
Action<NetworkIdentity, NetworkConnection>;
@@ -1108,18 +1161,21 @@ private static void OnInit()
11081161
if (isSerializer && method.ReturnType == typeof(void) && method.Name.StartsWith("Write"))
11091162
{
11101163
var parameters = method.GetAllParameters();
1111-
if (parameters.Length != 2) continue;
1164+
1165+
if (parameters.Length != 2)
1166+
continue;
11121167

11131168
var serializedType = parameters
11141169
.FirstOrDefault(y => y.ParameterType != typeof(NetworkWriter))
11151170
?.ParameterType;
11161171

1117-
if (serializedType is null) continue;
1118-
if (Writers.ContainsKey(serializedType)) continue;
1119-
1120-
var invoker = FastReflection.ForMethod(method);
1172+
if (serializedType is null)
1173+
continue;
1174+
1175+
if (Writers.ContainsKey(serializedType))
1176+
continue;
11211177

1122-
Writers.Add(serializedType, invoker);
1178+
Writers.Add(serializedType, method);
11231179
}
11241180
else if (method.HasAttribute<ClientRpcAttribute>() || method.HasAttribute<TargetRpcAttribute>())
11251181
{
@@ -1129,10 +1185,14 @@ private static void OnInit()
11291185
continue;
11301186

11311187
var body = method.GetMethodBody();
1132-
if (body is null) continue;
1188+
1189+
if (body is null)
1190+
continue;
11331191

11341192
var codes = body.GetILAsByteArray();
1135-
if (codes?.Length < 1) continue;
1193+
1194+
if (codes?.Length < 1)
1195+
continue;
11361196

11371197
var full = method.Module.ResolveString(BitConverter.ToInt32(codes,
11381198
codes.IndexOf((byte)OpCodes.Ldstr.Value) + 1));
@@ -1148,19 +1208,29 @@ private static void OnInit()
11481208
for (var y = 0; y < properties.Length; y++)
11491209
{
11501210
var prop = properties[y];
1151-
if (!prop.Name.StartsWith("Network")) continue;
1211+
1212+
if (!prop.Name.StartsWith("Network"))
1213+
continue;
11521214

11531215
var name = $"{prop.ReflectedType.Name}.{prop.Name}";
1154-
if (DirtyBits.ContainsKey(name)) continue;
1216+
1217+
if (DirtyBits.ContainsKey(name))
1218+
continue;
11551219

11561220
var setter = prop.GetSetMethod(true);
1157-
if (setter is null) continue;
1221+
1222+
if (setter is null)
1223+
continue;
11581224

11591225
var body = setter.GetMethodBody();
1160-
if (body is null) continue;
1226+
1227+
if (body is null)
1228+
continue;
11611229

11621230
var il = body.GetILAsByteArray();
1163-
if (il?.Length < 1) continue;
1231+
1232+
if (il?.Length < 1)
1233+
continue;
11641234

11651235
var bit = il[il.LastIndexOf((byte)OpCodes.Ldc_I8.Value) + 1];
11661236

@@ -1181,18 +1251,23 @@ private static void OnInit()
11811251
{
11821252
var method = writerExtensions[i];
11831253

1184-
if (method.IsGenericMethod) continue;
1185-
if (method.HasAttribute<ObsoleteAttribute>()) continue;
1254+
if (method.IsGenericMethod)
1255+
continue;
1256+
1257+
if (method.HasAttribute<ObsoleteAttribute>())
1258+
continue;
11861259

11871260
var parameters = method.GetAllParameters();
1188-
if (parameters.Length != 2) continue;
1261+
1262+
if (parameters.Length != 2)
1263+
continue;
11891264

11901265
var type = parameters.FirstOrDefault(x => x.ParameterType != typeof(NetworkWriter))?.ParameterType;
1191-
if (type is null) continue;
1192-
1193-
var invoker = FastReflection.ForMethod(method);
11941266

1195-
Writers.Add(type, invoker);
1267+
if (type is null)
1268+
continue;
1269+
1270+
Writers.Add(type, method);
11961271
}
11971272
catch (Exception ex)
11981273
{
@@ -1209,20 +1284,26 @@ private static void OnInit()
12091284
{
12101285
var method = generatedMethods[i];
12111286

1212-
if (method.IsGenericMethod) continue;
1213-
if (method.ReturnType != typeof(void)) continue;
1287+
if (method.IsGenericMethod)
1288+
continue;
1289+
1290+
if (method.ReturnType != typeof(void))
1291+
continue;
12141292

12151293
var parameters = method.GetAllParameters();
1216-
if (parameters.Length != 2) continue;
1294+
1295+
if (parameters.Length != 2)
1296+
continue;
12171297

12181298
var type = parameters.FirstOrDefault(x => x.ParameterType != typeof(NetworkWriter))?.ParameterType;
12191299

1220-
if (type is null) continue;
1221-
if (Writers.ContainsKey(type)) continue;
1300+
if (type is null)
1301+
continue;
12221302

1223-
var invoker = FastReflection.ForMethod(method);
1303+
if (Writers.ContainsKey(type))
1304+
continue;
12241305

1225-
Writers.Add(type, invoker);
1306+
Writers.Add(type, method);
12261307
}
12271308
catch (Exception ex)
12281309
{
@@ -1234,5 +1315,7 @@ private static void OnInit()
12341315
{
12351316
ApiLog.Error("Mirror Methods", ex);
12361317
}
1318+
1319+
hasInitialized = true;
12371320
}
12381321
}

0 commit comments

Comments
 (0)