Skip to content

Commit 72f0e82

Browse files
committed
Replaced RequireOwnership with a new RpcInvokePermission
that is respected from the server for both direct and proxy RPCs.
1 parent 0bc1f93 commit 72f0e82

File tree

5 files changed

+130
-16
lines changed

5 files changed

+130
-16
lines changed

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

Lines changed: 56 additions & 6 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

@@ -1439,12 +1440,25 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
14391440
callMethod = callMethod.MakeGeneric(genericTypes.ToArray());
14401441
}
14411442

1443+
RpcInvokePermission invokePermission = RpcInvokePermission.Anyone;
1444+
1445+
foreach (var attrField in rpcAttribute.Fields)
1446+
{
1447+
switch (attrField.Name)
1448+
{
1449+
case k_RpcAttribute_InvokePermission:
1450+
invokePermission = (RpcInvokePermission)attrField.Argument.Value;
1451+
break;
1452+
}
1453+
}
1454+
14421455
// __registerRpc(RpcMethodId, HandleFunc, methodName);
14431456
instructions.Add(processor.Create(OpCodes.Ldarg_0));
14441457
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
14451458
instructions.Add(processor.Create(OpCodes.Ldnull));
14461459
instructions.Add(processor.Create(OpCodes.Ldftn, callMethod));
14471460
instructions.Add(processor.Create(OpCodes.Newobj, m_NetworkHandlerDelegateCtor_MethodRef));
1461+
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)invokePermission));
14481462
instructions.Add(processor.Create(OpCodes.Ldstr, rpcMethodName));
14491463
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour___registerRpc_MethodRef));
14501464
}
@@ -2851,13 +2865,13 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
28512865
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
28522866
var isCientRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ClientRpcAttribute_FullName;
28532867
var isGenericRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.RpcAttribute_FullName;
2854-
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
2868+
var invokePermission = RpcInvokePermission.Anyone; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
28552869
foreach (var attrField in rpcAttribute.Fields)
28562870
{
28572871
switch (attrField.Name)
28582872
{
28592873
case k_ServerRpcAttribute_RequireOwnership:
2860-
requireOwnership = attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value;
2874+
invokePermission = (attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value) ? RpcInvokePermission.Owner : RpcInvokePermission.Anyone;
28612875
break;
28622876
}
28632877
}
@@ -2887,7 +2901,7 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
28872901
processor.Append(lastInstr);
28882902
}
28892903

2890-
if (isServerRpc && requireOwnership)
2904+
if (isServerRpc && invokePermission == RpcInvokePermission.Owner)
28912905
{
28922906
var roReturnInstr = processor.Create(OpCodes.Ret);
28932907
var roLastInstr = processor.Create(OpCodes.Nop);
@@ -2921,6 +2935,42 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
29212935

29222936
processor.Append(logNextInstr);
29232937

2938+
processor.Append(roReturnInstr);
2939+
processor.Append(roLastInstr);
2940+
} else if (invokePermission == RpcInvokePermission.Server)
2941+
{
2942+
var roReturnInstr = processor.Create(OpCodes.Ret);
2943+
var roLastInstr = processor.Create(OpCodes.Nop);
2944+
2945+
// if (rpcParams.Server.Receive.SenderClientId != NetworkManager.IsServer) { ... } return;
2946+
processor.Emit(OpCodes.Ldarg_2);
2947+
processor.Emit(OpCodes.Ldfld, m_RpcParams_Server_FieldRef);
2948+
processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_FieldRef);
2949+
processor.Emit(OpCodes.Ldfld, m_ServerRpcParams_Receive_SenderClientId_FieldRef);
2950+
processor.Emit(OpCodes.Ldarg_0);
2951+
processor.Emit(OpCodes.Call, m_NetworkManager_getIsServer_MethodRef);
2952+
processor.Emit(OpCodes.Ceq);
2953+
processor.Emit(OpCodes.Ldc_I4, 0);
2954+
processor.Emit(OpCodes.Ceq);
2955+
processor.Emit(OpCodes.Brfalse, roLastInstr);
2956+
2957+
var logNextInstr = processor.Create(OpCodes.Nop);
2958+
2959+
// if (LogLevel.Normal > networkManager.LogLevel)
2960+
processor.Emit(OpCodes.Ldloc, netManLocIdx);
2961+
processor.Emit(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef);
2962+
processor.Emit(OpCodes.Ldc_I4, (int)LogLevel.Normal);
2963+
processor.Emit(OpCodes.Cgt);
2964+
processor.Emit(OpCodes.Ldc_I4, 0);
2965+
processor.Emit(OpCodes.Ceq);
2966+
processor.Emit(OpCodes.Brfalse, logNextInstr);
2967+
2968+
// Debug.LogError(...);
2969+
processor.Emit(OpCodes.Ldstr, "Only the server can invoke an Rpc with RpcInvokePermission.Server!");
2970+
processor.Emit(OpCodes.Call, m_Debug_LogError_MethodRef);
2971+
2972+
processor.Append(logNextInstr);
2973+
29242974
processor.Append(roReturnInstr);
29252975
processor.Append(roLastInstr);
29262976
}

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public abstract class NetworkBehaviour : MonoBehaviour
3939

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

4344
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
4445
// RuntimeAccessModifiersILPP will make this `public`
@@ -326,12 +327,15 @@ internal FastBufferWriter __beginSendRpc(uint rpcMethodId, RpcParams rpcParams,
326327
#pragma warning restore IDE1006 // restore naming rule violation check
327328
{
328329
if (m_NetworkObject == null && !IsSpawned)
329-
{
330+
{
330331
throw new RpcException("The NetworkBehaviour must be spawned before calling this method.");
331332
}
332-
if (attributeParams.RequireOwnership && !IsOwner)
333+
if (attributeParams.InvokePermission == RpcInvokePermission.Owner && !IsOwner)
333334
{
334335
throw new RpcException("This RPC can only be sent by its owner.");
336+
} else if (attributeParams.InvokePermission == RpcInvokePermission.Server && !IsServer)
337+
{
338+
throw new RpcException("This RPC can only be sent by the server.");
335339
}
336340
return new FastBufferWriter(k_RpcMessageDefaultSize, Allocator.Temp, k_RpcMessageMaximumSize);
337341
}
@@ -950,10 +954,11 @@ internal virtual void __initializeRpcs()
950954

951955
#pragma warning disable IDE1006 // disable naming rule violation check
952956
// RuntimeAccessModifiersILPP will make this `protected`
953-
internal void __registerRpc(uint hash, RpcReceiveHandler handler, string rpcMethodName)
957+
internal void __registerRpc(uint hash, RpcReceiveHandler handler, RpcInvokePermission permission, string rpcMethodName)
954958
#pragma warning restore IDE1006 // restore naming rule violation check
955959
{
956960
__rpc_func_table[GetType()][hash] = handler;
961+
__rpc_permission_table[GetType()][hash] = permission;
957962
#if DEVELOPMENT_BUILD || UNITY_EDITOR || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
958963
__rpc_name_table[GetType()][hash] = rpcMethodName;
959964
#endif
@@ -1000,6 +1005,7 @@ internal void InitializeVariables()
10001005
if (!__rpc_func_table.ContainsKey(GetType()))
10011006
{
10021007
__rpc_func_table[GetType()] = new Dictionary<uint, RpcReceiveHandler>();
1008+
__rpc_permission_table[GetType()] = new Dictionary<uint, RpcInvokePermission>();
10031009
#if UNITY_EDITOR || DEVELOPMENT_BUILD || UNITY_MP_TOOLS_NET_STATS_MONITOR_ENABLED_IN_RELEASE
10041010
__rpc_name_table[GetType()] = new Dictionary<uint, string>();
10051011
#endif

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

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

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

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

Lines changed: 18 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)

com.unity.netcode.gameobjects/Runtime/Messaging/RpcAttributes.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,25 @@ public enum RpcDelivery
1818
Unreliable
1919
}
2020

21+
/// <summary>
22+
/// RPC invoke permissions
23+
/// </summary>
24+
public enum RpcInvokePermission
25+
{
26+
/// <summary>
27+
/// Anyone can invoke the Rpc.
28+
/// </summary>
29+
Anyone = 0,
30+
/// <summary>
31+
/// Rpc can only be invoked by the server.
32+
/// </summary>
33+
Server,
34+
/// <summary>
35+
/// Rpc can only be invoked by the owner of the NetworkBehaviour.
36+
/// </summary>
37+
Owner,
38+
}
39+
2140
/// <summary>
2241
/// <para>Represents the common base class for Rpc attributes.</para>
2342
/// </summary>
@@ -35,9 +54,9 @@ public struct RpcAttributeParams
3554
public RpcDelivery Delivery;
3655

3756
/// <summary>
38-
/// When true, only the owner of the object can execute this RPC
57+
/// Who has network permission to invoke this RPC
3958
/// </summary>
40-
public bool RequireOwnership;
59+
public RpcInvokePermission InvokePermission;
4160

4261
/// <summary>
4362
/// When true, local execution of the RPC is deferred until the next network tick
@@ -57,9 +76,9 @@ public struct RpcAttributeParams
5776
public RpcDelivery Delivery = RpcDelivery.Reliable;
5877

5978
/// <summary>
60-
/// When true, only the owner of the object can execute this RPC
79+
/// Who has network permission to invoke this RPC
6180
/// </summary>
62-
public bool RequireOwnership;
81+
public RpcInvokePermission InvokePermission;
6382

6483
/// <summary>
6584
/// When true, local execution of the RPC is deferred until the next network tick
@@ -97,7 +116,7 @@ public class ServerRpcAttribute : RpcAttribute
97116
/// When true, only the owner of the NetworkObject can invoke this ServerRpc.
98117
/// This property overrides the base RpcAttribute.RequireOwnership.
99118
/// </summary>
100-
public new bool RequireOwnership;
119+
public bool RequireOwnership;
101120

102121
/// <summary>
103122
/// Initializes a new instance of ServerRpcAttribute that targets the server

0 commit comments

Comments
 (0)