Skip to content

Commit 271e53f

Browse files
committed
Get logic working and add tests
1 parent e868ea0 commit 271e53f

File tree

8 files changed

+446
-100
lines changed

8 files changed

+446
-100
lines changed

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

Lines changed: 43 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,8 +1314,6 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
13141314
}
13151315
var rpcHandlers = new List<(uint RpcMethodId, MethodDefinition RpcHandler, string RpcMethodName, CustomAttribute rpcAttribute)>();
13161316

1317-
bool isEditorOrDevelopment = assemblyDefines.Contains("UNITY_EDITOR") || assemblyDefines.Contains("DEVELOPMENT_BUILD");
1318-
13191317
foreach (var methodDefinition in typeDefinition.Methods)
13201318
{
13211319
var rpcAttribute = CheckAndGetRpcAttribute(methodDefinition);
@@ -1436,16 +1434,18 @@ private void ProcessNetworkBehaviour(TypeDefinition typeDefinition, string[] ass
14361434
callMethod = callMethod.MakeGeneric(genericTypes.ToArray());
14371435
}
14381436

1437+
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
14391438
var isClientRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ClientRpcAttribute_FullName;
14401439

1441-
var invokePermission = RpcInvokePermission.Anyone;
1440+
var invokePermission = isServerRpc ? RpcInvokePermission.Owner : RpcInvokePermission.Everyone;
14421441

14431442
foreach (var attrField in rpcAttribute.Fields)
14441443
{
14451444
switch (attrField.Name)
14461445
{
14471446
case k_ServerRpcAttribute_RequireOwnership:
1448-
invokePermission = (attrField.Argument.Type == rpcHandler.Module.TypeSystem.Boolean && (bool)attrField.Argument.Value) ? RpcInvokePermission.Owner : RpcInvokePermission.Anyone;
1447+
var requireOwnership = attrField.Argument.Type == rpcHandler.Module.TypeSystem.Boolean && (bool)attrField.Argument.Value;
1448+
invokePermission = requireOwnership ? RpcInvokePermission.Owner : RpcInvokePermission.Everyone;
14491449
break;
14501450
case k_RpcAttribute_InvokePermission:
14511451
invokePermission = (RpcInvokePermission)attrField.Argument.Value;
@@ -1583,17 +1583,24 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio
15831583
isValid = false;
15841584
}
15851585

1586-
if (customAttributeType_FullName == CodeGenHelpers.RpcAttribute_FullName &&
1587-
!methodDefinition.Name.EndsWith("Rpc", StringComparison.OrdinalIgnoreCase))
1586+
if (customAttributeType_FullName == CodeGenHelpers.ClientRpcAttribute_FullName &&
1587+
!methodDefinition.Name.EndsWith("ClientRpc", StringComparison.OrdinalIgnoreCase))
15881588
{
1589-
m_Diagnostics.AddError(methodDefinition, "Rpc method must end with 'Rpc' suffix!");
1589+
m_Diagnostics.AddError(methodDefinition, "ClientRpc method must end with 'ClientRpc' suffix!");
15901590
isValid = false;
15911591
}
15921592

1593-
if (customAttributeType_FullName == CodeGenHelpers.ClientRpcAttribute_FullName &&
1594-
!methodDefinition.Name.EndsWith("ClientRpc", StringComparison.OrdinalIgnoreCase))
1593+
if (customAttributeType_FullName == CodeGenHelpers.RpcAttribute_FullName &&
1594+
!methodDefinition.Name.EndsWith("Rpc", StringComparison.OrdinalIgnoreCase))
15951595
{
1596-
m_Diagnostics.AddError(methodDefinition, "ClientRpc method must end with 'ClientRpc' suffix!");
1596+
m_Diagnostics.AddError(methodDefinition, "Rpc method must end with 'Rpc' suffix!");
1597+
1598+
// Extra compiler information if a method was defined as a local function
1599+
if (methodDefinition.Name.Contains("Rpc|", StringComparison.OrdinalIgnoreCase) && methodDefinition.Name.StartsWith("g__", StringComparison.OrdinalIgnoreCase))
1600+
{
1601+
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} appears to be a local function. Local functions cannot be RPCs.");
1602+
}
1603+
15971604
isValid = false;
15981605
}
15991606

@@ -1638,8 +1645,6 @@ private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinitio
16381645
case k_RpcAttribute_InvokePermission:
16391646
hasInvokePermission = true;
16401647
break;
1641-
default:
1642-
break;
16431648
}
16441649
}
16451650

@@ -2206,13 +2211,30 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
22062211
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_getNetworkManager_MethodRef));
22072212
instructions.Add(processor.Create(OpCodes.Stloc, netManLocIdx));
22082213

2209-
// if (networkManager == null || !networkManager.IsListening) return;
2214+
// if (networkManager == null || !networkManager.IsListening) { ... return };
22102215
instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx));
22112216
instructions.Add(processor.Create(OpCodes.Brfalse, returnInstr));
22122217
instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx));
22132218
instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkManager_getIsListening_MethodRef));
22142219
instructions.Add(processor.Create(OpCodes.Brtrue, lastInstr));
22152220

2221+
var logNextInstr = processor.Create(OpCodes.Nop);
2222+
2223+
// if (LogLevel.Normal > networkManager.LogLevel)
2224+
instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx));
2225+
instructions.Add(processor.Create(OpCodes.Ldfld, m_NetworkManager_LogLevel_FieldRef));
2226+
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)LogLevel.Normal));
2227+
instructions.Add(processor.Create(OpCodes.Cgt));
2228+
instructions.Add(processor.Create(OpCodes.Ldc_I4, 0));
2229+
instructions.Add(processor.Create(OpCodes.Ceq));
2230+
instructions.Add(processor.Create(OpCodes.Brfalse, logNextInstr));
2231+
2232+
// Debug.LogError(...);
2233+
instructions.Add(processor.Create(OpCodes.Ldstr, "Rpc methods can only be invoked after starting the NetworkManager!"));
2234+
instructions.Add(processor.Create(OpCodes.Call, m_Debug_LogError_MethodRef));
2235+
2236+
instructions.Add(logNextInstr);
2237+
22162238
instructions.Add(returnInstr);
22172239
instructions.Add(lastInstr);
22182240
}
@@ -2341,7 +2363,7 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
23412363
instructions.Add(processor.Create(OpCodes.Ldloca, rpcAttributeParamsIdx));
23422364
instructions.Add(processor.Create(OpCodes.Initobj, m_AttributeParamsType_TypeRef));
23432365

2344-
RpcAttribute.RpcAttributeParams dflt = default;
2366+
RpcAttribute.RpcAttributeParams defaultParameters = default;
23452367
foreach (var field in rpcAttribute.Fields)
23462368
{
23472369
var found = false;
@@ -2351,8 +2373,8 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
23512373
{
23522374
found = true;
23532375
var value = field.Argument.Value;
2354-
var paramField = dflt.GetType().GetField(attrField.Name);
2355-
if (value != paramField.GetValue(dflt))
2376+
var paramField = defaultParameters.GetType().GetField(attrField.Name);
2377+
if (value != paramField.GetValue(defaultParameters))
23562378
{
23572379
instructions.Add(processor.Create(OpCodes.Ldloca, rpcAttributeParamsIdx));
23582380
var type = value.GetType();
@@ -2458,11 +2480,11 @@ private void InjectWriteAndCallBlocks(MethodDefinition methodDefinition, CustomA
24582480
{
24592481
if (paramIndex != paramCount - 1)
24602482
{
2461-
m_Diagnostics.AddError(methodDefinition, $"{nameof(RpcParams)} must be the last parameter in a ClientRpc.");
2483+
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.Name} is invalid. {nameof(RpcParams)} must be the last parameter in a ClientRpc.");
24622484
}
24632485
if (!isGenericRpc)
24642486
{
2465-
m_Diagnostics.AddError($"Only Rpcs may accept {nameof(RpcParams)} as a parameter.");
2487+
m_Diagnostics.AddError($"{methodDefinition.Name} is invalid. Only Rpcs may accept {nameof(RpcParams)} as a parameter.");
24662488
}
24672489
continue;
24682490
}
@@ -2897,26 +2919,17 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
28972919
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
28982920
var isClientRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ClientRpcAttribute_FullName;
28992921
var isGenericRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.RpcAttribute_FullName;
2900-
var invokePermission = RpcInvokePermission.Anyone;
2922+
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
29012923
foreach (var attrField in rpcAttribute.Fields)
29022924
{
29032925
switch (attrField.Name)
29042926
{
29052927
case k_ServerRpcAttribute_RequireOwnership:
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;
2928+
requireOwnership = attrField.Argument.Type == typeSystem.Boolean && (bool)attrField.Argument.Value;
29102929
break;
29112930
}
29122931
}
29132932

2914-
// legacy ClientRpc should always be RpcInvokePermission.Server
2915-
if (isClientRpc)
2916-
{
2917-
invokePermission = RpcInvokePermission.Server;
2918-
}
2919-
29202933
rpcHandler.Body.InitLocals = true;
29212934
// NetworkManager networkManager;
29222935
rpcHandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
@@ -2942,7 +2955,7 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
29422955
processor.Append(lastInstr);
29432956
}
29442957

2945-
if (isServerRpc && invokePermission == RpcInvokePermission.Owner)
2958+
if (isServerRpc && requireOwnership)
29462959
{
29472960
var roReturnInstr = processor.Create(OpCodes.Ret);
29482961
var roLastInstr = processor.Create(OpCodes.Nop);
@@ -2976,42 +2989,6 @@ private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition
29762989

29772990
processor.Append(logNextInstr);
29782991

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-
30152992
processor.Append(roReturnInstr);
30162993
processor.Append(roLastInstr);
30172994
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -326,14 +326,16 @@ internal FastBufferWriter __beginSendRpc(uint rpcMethodId, RpcParams rpcParams,
326326
#pragma warning restore IDE1006 // restore naming rule violation check
327327
{
328328
if (m_NetworkObject == null && !IsSpawned)
329-
{
329+
{
330330
throw new RpcException("The NetworkBehaviour must be spawned before calling this method.");
331331
}
332-
else if (attributeParams.InvokePermission == RpcInvokePermission.Server && !IsServer)
332+
333+
if (attributeParams.InvokePermission == RpcInvokePermission.Server && !IsServer)
333334
{
334335
throw new RpcException("This RPC can only be sent by the server.");
335336
}
336-
else if ((attributeParams.RequireOwnership || attributeParams.InvokePermission == RpcInvokePermission.Owner) && !IsOwner)
337+
338+
if ((attributeParams.RequireOwnership || attributeParams.InvokePermission == RpcInvokePermission.Owner) && !IsOwner)
337339
{
338340
throw new RpcException("This RPC can only be sent by its owner.");
339341
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public unsafe void Handle(ref NetworkContext context)
5151
RpcInvokePermission permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][WrappedMessage.Metadata.NetworkRpcMethodId];
5252
bool hasPermission = permission switch
5353
{
54-
RpcInvokePermission.Anyone => true,
54+
RpcInvokePermission.Everyone => true,
5555
RpcInvokePermission.Server => context.SenderId == networkManager.LocalClientId,
5656
RpcInvokePermission.Owner => context.SenderId == networkBehaviour.OwnerClientId,
5757
_ => false,

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

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,40 +66,37 @@ 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+
70+
return;
6971
}
70-
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
7172

73+
var networkBehaviour = networkObject.GetNetworkBehaviourAtOrderIndex(metadata.NetworkBehaviourId);
7274
try
7375
{
74-
Type type = networkBehaviour.GetType();
75-
if (networkManager.IsServer)
76+
var permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId];
77+
78+
if ((permission == RpcInvokePermission.Server && rpcParams.SenderId != NetworkManager.ServerClientId) ||
79+
(permission == RpcInvokePermission.Owner && rpcParams.SenderId != networkObject.OwnerClientId))
7680
{
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)
81+
if (networkManager.LogLevel <= LogLevel.Developer)
8882
{
89-
return;
83+
Debug.LogError("Rpc message received from a client who does not have permission to perform this operation!");
9084
}
91-
}
85+
return;
86+
}
87+
9288
NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId](networkBehaviour, payload, rpcParams);
9389
}
9490
catch (Exception ex)
9591
{
96-
Debug.LogException(new Exception("Unhandled RPC exception!", ex));
97-
if (networkManager.LogLevel == LogLevel.Developer)
92+
Debug.LogException(new Exception($"Unhandled RPC exception!", ex));
93+
if (networkManager.LogLevel <= LogLevel.Developer)
9894
{
9995
Debug.Log($"RPC Table Contents");
10096
foreach (var entry in NetworkBehaviour.__rpc_func_table[networkBehaviour.GetType()])
10197
{
102-
Debug.Log($"{entry.Key} | {entry.Value.Method.Name}");
98+
var permission = NetworkBehaviour.__rpc_permission_table[networkBehaviour.GetType()][metadata.NetworkRpcMethodId];
99+
Debug.Log($"{entry.Key} | {entry.Value.Method.Name} | {permission}");
103100
}
104101
}
105102
}
@@ -138,6 +135,7 @@ public void Handle(ref NetworkContext context)
138135
{
139136
var rpcParams = new __RpcParams
140137
{
138+
SenderId = context.SenderId,
141139
Server = new ServerRpcParams
142140
{
143141
Receive = new ServerRpcReceiveParams
@@ -175,6 +173,7 @@ public void Handle(ref NetworkContext context)
175173
{
176174
var rpcParams = new __RpcParams
177175
{
176+
SenderId = NetworkManager.ServerClientId,
178177
Client = new ClientRpcParams
179178
{
180179
Receive = new ClientRpcReceiveParams
@@ -214,18 +213,19 @@ public unsafe bool Deserialize(FastBufferReader reader, ref NetworkContext conte
214213
public void Handle(ref NetworkContext context)
215214
{
216215
var networkManager = (NetworkManager)context.SystemOwner;
217-
if (networkManager.IsServer)
218-
{
219-
SenderClientId = context.SenderId;
220-
}
221-
216+
217+
// If the server is receiving, always trust the transportId for the SenderClientId
218+
// Otherwise, use the proxied id.
219+
var senderId = networkManager.IsServer ? context.SenderId : SenderClientId;
220+
222221
var rpcParams = new __RpcParams
223222
{
223+
SenderId = senderId,
224224
Ext = new RpcParams
225225
{
226226
Receive = new RpcReceiveParams
227227
{
228-
SenderClientId = SenderClientId
228+
SenderClientId = senderId
229229
}
230230
}
231231
};

0 commit comments

Comments
 (0)