Skip to content

Commit 00fca38

Browse files
committed
Merge remote-tracking branch 'xmanning/develop-2.0.0' into feat/server-enforced-RpcInvokePermission
2 parents bcb73cd + edfd01b commit 00fca38

File tree

5 files changed

+193
-16
lines changed

5 files changed

+193
-16
lines changed

com.unity.netcode.gameobjects/Editor/CodeGen/NetworkBehaviourILPP.cs

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ private void CreateNetworkVariableTypeInitializers(AssemblyDefinition assembly,
609609
private const string k_NetworkVariableBase_Initialize = nameof(NetworkVariableBase.Initialize);
610610

611611
private const string k_RpcAttribute_Delivery = nameof(RpcAttribute.Delivery);
612+
private const string k_RpcAttribute_InvokePermission = nameof(RpcAttribute.InvokePermission);
612613
private const string k_ServerRpcAttribute_RequireOwnership = nameof(ServerRpcAttribute.RequireOwnership);
613614
private const string k_RpcParams_Server = nameof(__RpcParams.Server);
614615
private const string k_RpcParams_Client = nameof(__RpcParams.Client);
@@ -1311,7 +1312,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
13111312
return;
13121313
}
13131314
}
1314-
var rpcHandlers = new List<(uint RpcMethodId, MethodDefinition RpcHandler, string RpcMethodName)>();
1315+
var rpcHandlers = new List<(uint RpcMethodId, MethodDefinition RpcHandler, string RpcMethodName, CustomAttribute rpcAttribute)>();
13151316

13161317
bool isEditorOrDevelopment = assemblyDefines.Contains("UNITY_EDITOR") || assemblyDefines.Contains("DEVELOPMENT_BUILD");
13171318

@@ -1342,7 +1343,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
13421343

13431344
InjectWriteAndCallBlocks(methodDefinition, rpcAttribute, rpcMethodId);
13441345

1345-
rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute, rpcMethodId), methodDefinition.Name));
1346+
rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute, rpcMethodId), methodDefinition.Name, rpcAttribute));
13461347
}
13471348

13481349
GenerateVariableInitialization(typeDefinition);
@@ -1424,7 +1425,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
14241425
var instructions = new List<Instruction>();
14251426
var processor = initializeRpcsMethodDef.Body.GetILProcessor();
14261427

1427-
foreach (var (rpcMethodId, rpcHandler, rpcMethodName) in rpcHandlers)
1428+
foreach (var (rpcMethodId, rpcHandler, rpcMethodName, rpcAttribute) in rpcHandlers)
14281429
{
14291430
typeDefinition.Methods.Add(rpcHandler);
14301431

@@ -1435,12 +1436,35 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
14351436
callMethod = callMethod.MakeGeneric(genericTypes.ToArray());
14361437
}
14371438

1438-
// __registerRpc(RpcMethodId, HandleFunc, methodName);
1439+
var isClientRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ClientRpcAttribute_FullName;
1440+
1441+
var invokePermission = RpcInvokePermission.Anyone;
1442+
1443+
foreach (var attrField in rpcAttribute.Fields)
1444+
{
1445+
switch (attrField.Name)
1446+
{
1447+
case k_ServerRpcAttribute_RequireOwnership:
1448+
invokePermission = (attrField.Argument.Type == rpcHandler.Module.TypeSystem.Boolean && (bool)attrField.Argument.Value) ? RpcInvokePermission.Owner : RpcInvokePermission.Anyone;
1449+
break;
1450+
case k_RpcAttribute_InvokePermission:
1451+
invokePermission = (RpcInvokePermission)attrField.Argument.Value;
1452+
break;
1453+
}
1454+
}
1455+
1456+
if (isClientRpc)
1457+
{
1458+
invokePermission = RpcInvokePermission.Server;
1459+
}
1460+
1461+
// __registerRpc(RpcMethodId, HandleFunc, invokePermission, methodName);
14391462
instructions.Add(processor.Create(OpCodes.Ldarg_0));
14401463
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
14411464
instructions.Add(processor.Create(OpCodes.Ldnull));
14421465
instructions.Add(processor.Create(OpCodes.Ldftn, callMethod));
14431466
instructions.Add(processor.Create(OpCodes.Newobj, m_NetworkHandlerDelegateCtor_MethodRef));
1467+
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)invokePermission));
14441468
instructions.Add(processor.Create(OpCodes.Ldstr, rpcMethodName));
14451469
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour___registerRpc_MethodRef));
14461470
}
@@ -1517,6 +1541,7 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
15171541
private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinition)
15181542
{
15191543
CustomAttribute rpcAttribute = null;
1544+
15201545
foreach (var customAttribute in methodDefinition.CustomAttributes)
15211546
{
15221547
var customAttributeType_FullName = customAttribute.AttributeType.FullName;
@@ -1600,6 +1625,30 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio
16001625

16011626
return null;
16021627
}
1628+
1629+
bool hasRequireOwnership = false, hasInvokePermission = false;
1630+
1631+
foreach (var argument in rpcAttribute.Fields)
1632+
{
1633+
switch (argument.Name)
1634+
{
1635+
case k_ServerRpcAttribute_RequireOwnership:
1636+
hasRequireOwnership = true;
1637+
break;
1638+
case k_RpcAttribute_InvokePermission:
1639+
hasInvokePermission = true;
1640+
break;
1641+
default:
1642+
break;
1643+
}
1644+
}
1645+
1646+
if (hasRequireOwnership && hasInvokePermission)
1647+
{
1648+
m_Diagnostics.AddError("Rpc attribute cannot declare both RequireOwnership and InvokePermission!");
1649+
return null;
1650+
}
1651+
16031652
// Checks for IsSerializable are moved to later as the check is now done by dynamically seeing if any valid
16041653
// serializer OR extension method exists for it.
16051654
return rpcAttribute;
@@ -2346,6 +2395,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
23462395
m_Diagnostics.AddError($"{nameof(RpcAttribute)} contains field {field} which is not present in {nameof(RpcAttribute.RpcAttributeParams)}.");
23472396
}
23482397
}
2398+
23492399
instructions.Add(processor.Create(OpCodes.Ldloc, rpcAttributeParamsIdx));
23502400

23512401
// defaultTarget
@@ -2845,19 +2895,28 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
28452895
var processor = rpcHandler.Body.GetILProcessor();
28462896

28472897
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
2848-
var isCientRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ClientRpcAttribute_FullName;
2898+
var isClientRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ClientRpcAttribute_FullName;
28492899
var isGenericRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.RpcAttribute_FullName;
2850-
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
2900+
var invokePermission = RpcInvokePermission.Anyone;
28512901
foreach (var attrField in rpcAttribute.Fields)
28522902
{
28532903
switch (attrField.Name)
28542904
{
28552905
case k_ServerRpcAttribute_RequireOwnership:
2856-
requireOwnership = attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value;
2906+
invokePermission = (attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value) ? RpcInvokePermission.Owner : RpcInvokePermission.Anyone;
2907+
break;
2908+
case k_RpcAttribute_InvokePermission:
2909+
invokePermission = (RpcInvokePermission)attrField.Argument.Value;
28572910
break;
28582911
}
28592912
}
28602913

2914+
// legacy ClientRpc should always be RpcInvokePermission.Server
2915+
if (isClientRpc)
2916+
{
2917+
invokePermission = RpcInvokePermission.Server;
2918+
}
2919+
28612920
rpcHandler.Body.InitLocals = true;
28622921
// NetworkManager networkManager;
28632922
rpcHandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
@@ -2883,7 +2942,7 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
28832942
processor.Append(lastInstr);
28842943
}
28852944

2886-
if (isServerRpc && requireOwnership)
2945+
if (isServerRpc && invokePermission == RpcInvokePermission.Owner)
28872946
{
28882947
var roReturnInstr = processor.Create(OpCodes.Ret);
28892948
var roLastInstr = processor.Create(OpCodes.Nop);
@@ -2917,6 +2976,42 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
29172976

29182977
processor.Append(logNextInstr);
29192978

2979+
processor.Append(roReturnInstr);
2980+
processor.Append(roLastInstr);
2981+
} else if (invokePermission == RpcInvokePermission.Server)
2982+
{
2983+
var roReturnInstr = processor.Create(OpCodes.Ret);
2984+
var roLastInstr = processor.Create(OpCodes.Nop);
2985+
2986+
// if (rpcParams.Server.Receive.SenderClientId != NetworkManager.IsServer) { ... } return;
2987+
processor.Emit(OpCodes.Ldarg_2);
2988+
processor.Emit(OpCodes.Ldfld, m_RpcParams_Server_FieldRef);
2989+
processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_FieldRef);
2990+
processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_SenderClientId_FieldRef);
2991+
processor.Emit(OpCodes.Ldarg_0);
2992+
processor.Emit(OpCodes.Call, m_NetworkManager_getIsServer_MethodRef);
2993+
processor.Emit(OpCodes.Ceq);
2994+
processor.Emit(OpCodes.Ldc_I4, 0);
2995+
processor.Emit(OpCodes.Ceq);
2996+
processor.Emit(OpCodes.Brfalse, roLastInstr);
2997+
2998+
var logNextInstr = processor.Create(OpCodes.Nop);
2999+
3000+
// if (LogLevel.Normal > networkManager.LogLevel)
3001+
processor.Emit(OpCodes.Ldloc, netManLocIdx);
3002+
processor.Emit(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef);
3003+
processor.Emit(OpCodes.Ldc_I4, (int)LogLevel.Normal);
3004+
processor.Emit(OpCodes.Cgt);
3005+
processor.Emit(OpCodes.Ldc_I4, 0);
3006+
processor.Emit(OpCodes.Ceq);
3007+
processor.Emit(OpCodes.Brfalse, logNextInstr);
3008+
3009+
// Debug.LogError(...);
3010+
processor.Emit(OpCodes.Ldstr, "Only the server can invoke an Rpc with RpcInvokePermission.Server!");
3011+
processor.Emit(OpCodes.Call, m_Debug_LogError_MethodRef);
3012+
3013+
processor.Append(logNextInstr);
3014+
29203015
processor.Append(roReturnInstr);
29213016
processor.Append(roLastInstr);
29223017
}

com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public abstract class NetworkBehaviour : MonoBehaviour
3838

3939
// RuntimeAccessModifiersILPP will make this `public`
4040
internal static readonly Dictionary<Type, Dictionary<uint, RpcReceiveHandler>> __rpc_func_table = new Dictionary<Type, Dictionary<uint, RpcReceiveHandler>>();
41+
internal static readonly Dictionary<Type, Dictionary<uint, RpcInvokePermission>> __rpc_permission_table = new Dictionary<Type, Dictionary<uint, RpcInvokePermission>>();
4142

4243
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
4344
// RuntimeAccessModifiersILPP will make this `public`
@@ -325,10 +326,14 @@ internal FastBufferWriter __beginSendRpc(uint rpcMethodId, RpcParams rpcParams,
325326
#pragma warning restore IDE1006 // restore naming rule violation check
326327
{
327328
if (m_NetworkObject == null && !IsSpawned)
328-
{
329+
{
329330
throw new RpcException("The NetworkBehaviour must be spawned before calling this method.");
330331
}
331-
if (attributeParams.RequireOwnership && !IsOwner)
332+
else if (attributeParams.InvokePermission == RpcInvokePermission.Server && !IsServer)
333+
{
334+
throw new RpcException("This RPC can only be sent by the server.");
335+
}
336+
else if ((attributeParams.RequireOwnership || attributeParams.InvokePermission == RpcInvokePermission.Owner) && !IsOwner)
332337
{
333338
throw new RpcException("This RPC can only be sent by its owner.");
334339
}
@@ -935,10 +940,11 @@ internal virtual void __initializeRpcs()
935940

936941
#pragma warning disable IDE1006 // disable naming rule violation check
937942
// RuntimeAccessModifiersILPP will make this `protected`
938-
internal void __registerRpc(uint hash, RpcReceiveHandler handler, string rpcMethodName)
943+
internal void __registerRpc(uint hash, RpcReceiveHandler handler, RpcInvokePermission permission, string rpcMethodName)
939944
#pragma warning restore IDE1006 // restore naming rule violation check
940945
{
941946
__rpc_func_table[GetType()][hash] = handler;
947+
__rpc_permission_table[GetType()][hash] = permission;
942948
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
943949
__rpc_name_table[GetType()][hash] = rpcMethodName;
944950
#endif
@@ -985,6 +991,7 @@ internal void InitializeVariables()
985991
if (!__rpc_func_table.ContainsKey(GetType()))
986992
{
987993
__rpc_func_table[GetType()] = new Dictionary<uint, RpcReceiveHandler>();
994+
__rpc_permission_table[GetType()] = new Dictionary<uint, RpcInvokePermission>();
988995
#if UNITY_EDITOR || DEVELOPMENT_BUILD || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
989996
__rpc_name_table[GetType()] = new Dictionary<uint, string>();
990997
#endif

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/ProxyMessage.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,32 @@ public unsafe void Handle(ref NetworkContext context)
4141
}
4242
return;
4343
}
44-
4544
var observers = networkObject.Observers;
4645

46+
// Validate message if server
47+
if (networkManager.IsServer)
48+
{
49+
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(WrappedMessage.Metadata.NetworkBehaviourId);
50+
51+
RpcInvokePermission permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][WrappedMessage.Metadata.NetworkRpcMethodId];
52+
bool hasPermission = permission switch
53+
{
54+
RpcInvokePermission.Anyone => true,
55+
RpcInvokePermission.Server => context.SenderId == networkManager.LocalClientId,
56+
RpcInvokePermission.Owner => context.SenderId == networkBehaviour.OwnerClientId,
57+
_ => false,
58+
};
59+
60+
// Do not handle the message if the sender does not have permission to do so.
61+
if (!hasPermission)
62+
{
63+
return;
64+
}
65+
66+
WrappedMessage.SenderClientId = context.SenderId;
67+
}
68+
69+
4770
var nonServerIds = new NativeList<ulong>(Allocator.Temp);
4871
for (var i = 0; i < TargetClientIds.Length; ++i)
4972
{

com.unity.netcode.gameobjects/Runtime/Messaging/Messages/RpcMessages.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,29 @@ public static void Handle(ref NetworkContext context, ref RpcMetadata metadata,
6666
{
6767
NetworkLog.LogWarning($"[{metadata.NetworkObjectId}, {metadata.NetworkBehaviourId}, {metadata.NetworkRpcMethodId}] An RPC called on a {nameof(NetworkObject)} that is not in the spawned objects list. Please make sure the {nameof(NetworkObject)} is spawned before calling RPCs.");
6868
}
69-
return;
7069
}
7170
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
7271

7372
try
7473
{
74+
Type type = networkBehaviour.GetType();
75+
if (networkManager.IsServer)
76+
{
77+
RpcInvokePermission permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId];
78+
bool hasPermission = permission switch
79+
{
80+
RpcInvokePermission.Anyone => true,
81+
RpcInvokePermission.Server => context.SenderId == networkManager.LocalClientId,
82+
RpcInvokePermission.Owner => context.SenderId == networkBehaviour.OwnerClientId,
83+
_ => false,
84+
};
85+
86+
// Do not handle the message if the sender does not have permission to do so.
87+
if (!hasPermission)
88+
{
89+
return;
90+
}
91+
}
7592
NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams);
7693
}
7794
catch (Exception ex)
@@ -196,6 +213,12 @@ public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext conte
196213

197214
public void Handle(ref NetworkContext context)
198215
{
216+
var networkManager = (NetworkManager)context.SystemOwner;
217+
if (networkManager.IsServer)
218+
{
219+
SenderClientId = context.SenderId;
220+
}
221+
199222
var rpcParams = new __RpcParams
200223
{
201224
Ext = new RpcParams

0 commit comments

Comments
 (0)