Skip to content

Commit 911696d

Browse files
pkaminskiTwoTenPvP
authored andcommitted
fix(core): fix RPCs to work correctly on IL2CPP backend. (#235)
* fix(core): fix RPCs to work correctly on IL2CPP backend. * style: Fixed code style
1 parent 9de5c1d commit 911696d

File tree

7 files changed

+266
-296
lines changed

7 files changed

+266
-296
lines changed

MLAPI/Core/NetworkedBehaviour.cs

Lines changed: 27 additions & 266 deletions
Large diffs are not rendered by default.

MLAPI/Core/NetworkedObject.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
using MLAPI.Configuration;
66
using MLAPI.Exceptions;
77
using MLAPI.Hashing;
8-
using MLAPI.Internal;
98
using MLAPI.Logging;
109
using MLAPI.Messaging;
1110
using MLAPI.Security;
12-
using MLAPI.Serialization;
1311
using MLAPI.Serialization.Pooled;
1412
using MLAPI.Spawning;
1513
using UnityEngine;
16-
using BitStream = MLAPI.Serialization.BitStream;
1714

1815
namespace MLAPI
1916
{
@@ -365,12 +362,6 @@ private void OnDestroy()
365362
if (NetworkingManager.Singleton != null)
366363
{
367364
SpawnManager.OnDestroyObject(NetworkId, false);
368-
369-
for (int i = 0; i < childNetworkedBehaviours.Count; i++)
370-
{
371-
childNetworkedBehaviours[i].CachedServerRpcs.Remove(childNetworkedBehaviours[i]);
372-
childNetworkedBehaviours[i].CachedClientRpcs.Remove(childNetworkedBehaviours[i]);
373-
}
374365
}
375366
}
376367

MLAPI/Messaging/ClientRPCAttribute.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ namespace MLAPI.Messaging
88
/// Remember that a host is a server and a client
99
/// </summary>
1010
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
11-
public class ClientRPCAttribute : Attribute
11+
public class ClientRPCAttribute : RPCAttribute
1212
{
13-
internal ReflectionMethod reflectionMethod;
14-
internal RpcDelegate rpcDelegate;
1513
}
16-
}
14+
}

MLAPI/Messaging/RPCAttribute.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace MLAPI.Messaging
4+
{
5+
/// <summary>
6+
/// Generic supertype of Client and Server RPC Attributes. Do not use directly.
7+
/// </summary>
8+
public abstract class RPCAttribute : Attribute
9+
{
10+
}
11+
}

MLAPI/Messaging/ReflectionMethod.cs

Lines changed: 110 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,120 @@
11
using System;
22
using System.IO;
33
using System.Reflection;
4+
using MLAPI.Logging;
5+
using MLAPI.Serialization;
46
using MLAPI.Serialization.Pooled;
57

68
namespace MLAPI.Messaging
79
{
810
internal class ReflectionMethod
911
{
10-
private MethodInfo method;
11-
private Type[] parameterTypes;
12-
private object[] parameterRefs;
13-
14-
public ReflectionMethod(MethodInfo methodInfo)
12+
internal readonly MethodInfo method;
13+
internal readonly bool useDelegate;
14+
internal readonly bool serverTarget;
15+
private readonly bool requireOwnership;
16+
private readonly int index;
17+
private readonly Type[] parameterTypes;
18+
private readonly object[] parameterRefs;
19+
20+
internal static ReflectionMethod Create(MethodInfo method, ParameterInfo[] parameters, int index)
21+
{
22+
RPCAttribute[] attributes = (RPCAttribute[])method.GetCustomAttributes(typeof(RPCAttribute), true);
23+
24+
if (attributes.Length == 0)
25+
return null;
26+
27+
if (attributes.Length > 1)
28+
{
29+
if (LogHelper.CurrentLogLevel <= LogLevel.Normal) LogHelper.LogWarning("Having more than one ServerRPC or ClientRPC attribute per method is not supported.");
30+
}
31+
32+
if (method.ReturnType != typeof(void) && !SerializationManager.IsTypeSupported(method.ReturnType))
33+
{
34+
if (LogHelper.CurrentLogLevel <= LogLevel.Error) LogHelper.LogWarning("Invalid return type of RPC. Has to be either void or RpcResponse<T> with a serializable type");
35+
}
36+
37+
return new ReflectionMethod(method, parameters, attributes[0], index);
38+
}
39+
40+
internal ReflectionMethod(MethodInfo method, ParameterInfo[] parameters, RPCAttribute attribute, int index)
1541
{
16-
method = methodInfo;
17-
ParameterInfo[] parameters = methodInfo.GetParameters();
42+
this.method = method;
43+
this.index = index;
44+
45+
if (attribute is ServerRPCAttribute serverRpcAttribute)
46+
{
47+
requireOwnership = serverRpcAttribute.RequireOwnership;
48+
serverTarget = true;
49+
}
50+
else
51+
{
52+
requireOwnership = false;
53+
serverTarget = false;
54+
}
55+
1856
parameterTypes = new Type[parameters.Length];
19-
parameterRefs = new object[parameters.Length];
20-
21-
for (int i = 0; i < parameters.Length; i++)
57+
58+
if (parameters.Length == 2 && method.ReturnType == typeof(void) && parameters[0].ParameterType == typeof(ulong) && parameters[1].ParameterType == typeof(Stream))
2259
{
23-
parameterTypes[i] = parameters[i].ParameterType;
60+
useDelegate = true;
61+
}
62+
else
63+
{
64+
useDelegate = false;
65+
66+
parameterRefs = new object[parameters.Length];
67+
68+
for (int i = 0; i < parameters.Length; i++)
69+
{
70+
parameterTypes[i] = parameters[i].ParameterType;
71+
}
2472
}
2573
}
2674

27-
internal object Invoke(object instance, Stream stream)
75+
internal object Invoke(NetworkedBehaviour target, ulong senderClientId, Stream stream)
76+
{
77+
if (requireOwnership == true && senderClientId != target.OwnerClientId)
78+
{
79+
if (LogHelper.CurrentLogLevel <= LogLevel.Normal) LogHelper.LogWarning("Only owner can invoke ServerRPC that is marked to require ownership");
80+
81+
return null;
82+
}
83+
84+
target.executingRpcSender = senderClientId;
85+
86+
if (stream.Position == 0)
87+
{
88+
if (useDelegate)
89+
{
90+
return InvokeDelegate(target, senderClientId, stream);
91+
}
92+
else
93+
{
94+
return InvokeReflected(target, stream);
95+
}
96+
}
97+
else
98+
{
99+
// Create a new stream so that the stream they get ONLY contains user data and not MLAPI headers
100+
using (PooledBitStream userStream = PooledBitStream.Get())
101+
{
102+
userStream.CopyUnreadFrom(stream);
103+
userStream.Position = 0;
104+
105+
if (useDelegate)
106+
{
107+
return InvokeDelegate(target, senderClientId, stream);
108+
}
109+
else
110+
{
111+
return InvokeReflected(target, stream);
112+
}
113+
}
114+
}
115+
}
116+
117+
private object InvokeReflected(NetworkedBehaviour instance, Stream stream)
28118
{
29119
using (PooledBitReader reader = PooledBitReader.Get(stream))
30120
{
@@ -36,5 +126,12 @@ internal object Invoke(object instance, Stream stream)
36126
return method.Invoke(instance, parameterRefs);
37127
}
38128
}
129+
130+
private object InvokeDelegate(NetworkedBehaviour target, ulong senderClientId, Stream stream)
131+
{
132+
target.rpcDelegates[index](senderClientId, stream);
133+
134+
return null;
135+
}
39136
}
40-
}
137+
}

MLAPI/Messaging/RpcTypeDefinition.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using MLAPI.Logging;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Reflection;
5+
6+
namespace MLAPI.Messaging
7+
{
8+
internal class RpcTypeDefinition
9+
{
10+
private static readonly Dictionary<Type, RpcTypeDefinition> typeLookup = new Dictionary<Type, RpcTypeDefinition>();
11+
private static readonly Dictionary<ulong, string> hashResults = new Dictionary<ulong, string>();
12+
13+
public static RpcTypeDefinition Get(Type type)
14+
{
15+
if (typeLookup.ContainsKey(type))
16+
{
17+
return typeLookup[type];
18+
}
19+
else
20+
{
21+
RpcTypeDefinition info = new RpcTypeDefinition(type);
22+
typeLookup.Add(type, info);
23+
24+
return info;
25+
}
26+
}
27+
28+
private static ulong HashMethodNameAndValidate(string name)
29+
{
30+
ulong hash = NetworkedBehaviour.HashMethodName(name);
31+
32+
if (hashResults.ContainsKey(hash))
33+
{
34+
string hashResult = hashResults[hash];
35+
36+
if (hashResult != name)
37+
{
38+
if (LogHelper.CurrentLogLevel <= LogLevel.Error) LogHelper.LogError("Hash collision detected for RPC method. The method \"" + name + "\" collides with the method \"" + hashResult + "\". This can be solved by increasing the amount of bytes to use for hashing in the NetworkConfig or changing the name of one of the conflicting methods.");
39+
}
40+
}
41+
else
42+
{
43+
hashResults.Add(hash, name);
44+
}
45+
46+
return hash;
47+
}
48+
49+
private static List<MethodInfo> GetAllMethods(Type type, Type limitType)
50+
{
51+
List<MethodInfo> list = new List<MethodInfo>();
52+
53+
while (type != null && type != limitType)
54+
{
55+
list.AddRange(type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));
56+
57+
type = type.BaseType;
58+
}
59+
60+
return list;
61+
}
62+
63+
public readonly Dictionary<ulong, ReflectionMethod> serverMethods = new Dictionary<ulong, ReflectionMethod>();
64+
public readonly Dictionary<ulong, ReflectionMethod> clientMethods = new Dictionary<ulong, ReflectionMethod>();
65+
private readonly ReflectionMethod[] delegateMethods;
66+
67+
private RpcTypeDefinition(Type type)
68+
{
69+
List<ReflectionMethod> delegateMethodsList = new List<ReflectionMethod>();
70+
List<MethodInfo> methods = GetAllMethods(type, typeof(NetworkedBehaviour));
71+
72+
for (int i = 0; i < methods.Count; i++)
73+
{
74+
MethodInfo method = methods[i];
75+
ParameterInfo[] parameters = method.GetParameters();
76+
ReflectionMethod rpcMethod = ReflectionMethod.Create(method, parameters, delegateMethodsList.Count);
77+
78+
if (rpcMethod == null)
79+
continue;
80+
81+
Dictionary<ulong, ReflectionMethod> lookupTarget = rpcMethod.serverTarget ? serverMethods : clientMethods;
82+
83+
lookupTarget.Add(HashMethodNameAndValidate(method.Name), rpcMethod);
84+
85+
if (parameters.Length > 0)
86+
{
87+
lookupTarget.Add(HashMethodNameAndValidate(NetworkedBehaviour.GetHashableMethodSignature(method)), rpcMethod);
88+
}
89+
90+
if (rpcMethod.useDelegate)
91+
{
92+
delegateMethodsList.Add(rpcMethod);
93+
}
94+
}
95+
96+
delegateMethods = delegateMethodsList.ToArray();
97+
}
98+
99+
internal RpcDelegate[] CreateTargetedDelegates(NetworkedBehaviour target)
100+
{
101+
if (delegateMethods.Length == 0)
102+
return null;
103+
104+
RpcDelegate[] rpcDelegates = new RpcDelegate[delegateMethods.Length];
105+
106+
for (int i = 0; i < delegateMethods.Length; i++)
107+
{
108+
rpcDelegates[i] = (RpcDelegate) Delegate.CreateDelegate(typeof(RpcDelegate), target, delegateMethods[i].method.Name);
109+
}
110+
111+
return rpcDelegates;
112+
}
113+
}
114+
}

MLAPI/Messaging/ServerRPCAttribute.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ namespace MLAPI.Messaging
88
/// Remember that a host is a server and a client
99
/// </summary>
1010
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
11-
public class ServerRPCAttribute : Attribute
11+
public class ServerRPCAttribute : RPCAttribute
1212
{
1313
/// <summary>
1414
/// Whether or not the ServerRPC should only be run if executed by the owner of the object
1515
/// </summary>
1616
public bool RequireOwnership = true;
17-
internal ReflectionMethod reflectionMethod;
18-
internal RpcDelegate rpcDelegate;
1917
}
20-
}
18+
}

0 commit comments

Comments
 (0)