com.unity.netcode.gameobjects@1.0.0-pre.6

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).

## [1.0.0-pre.6] - 2022-03-02

### Added
- NetworkAnimator now properly synchrhonizes all animation layers as well as runtime-adjusted weighting between them (#1765)
- Added first set of tests for NetworkAnimator - parameter syncing, trigger set / reset, override network animator (#1735)

### Changed

### Fixed
- Fixed an issue where sometimes the first client to connect to the server could see messages from the server as coming from itself. (#1683)
- Fixed an issue where clients seemed to be able to send messages to ClientId 1, but these messages would actually still go to the server (id 0) instead of that client. (#1683)
- Improved clarity of error messaging when a client attempts to send a message to a destination other than the server, which isn't allowed. (#1683)
- Disallowed async keyword in RPCs (#1681)
- Fixed an issue where Alpha release versions of Unity (version 2022.2.0a5 and later) will not compile due to the UNet Transport no longer existing (#1678)
- Fixed messages larger than 64k being written with incorrectly truncated message size in header (#1686) (credit: @kaen)
- Fixed overloading RPC methods causing collisions and failing on IL2CPP targets. (#1694)
- Fixed spawn flow to propagate `IsSceneObject` down to children NetworkObjects, decouple implicit relationship between object spawning & `IsSceneObject` flag (#1685)
- Fixed error when serializing ConnectionApprovalMessage with scene management disabled when one or more objects is hidden via the CheckObjectVisibility delegate (#1720)
- Fixed CheckObjectVisibility delegate not being properly invoked for connecting clients when Scene Management is enabled. (#1680)
- Fixed NetworkList to properly call INetworkSerializable's NetworkSerialize() method (#1682)
- Fixed NetworkVariables containing more than 1300 bytes of data (such as large NetworkLists) no longer cause an OverflowException (the limit on data size is now whatever limit the chosen transport imposes on fragmented NetworkDelivery mechanisms) (#1725)
- Fixed ServerRpcParams and ClientRpcParams must be the last parameter of an RPC in order to function properly. Added a compile-time check to ensure this is the case and trigger an error if they're placed elsewhere (#1721)
- Fixed FastBufferReader being created with a length of 1 if provided an input of length 0 (#1724)
- Fixed The NetworkConfig's checksum hash includes the NetworkTick so that clients with a different tickrate than the server are identified and not allowed to connect (#1728)
- Fixed OwnedObjects not being properly modified when using ChangeOwnership (#1731)
- Improved performance in NetworkAnimator (#1735)
- Removed the "always sync" network animator (aka "autosend") parameters (#1746)
This commit is contained in:
Unity Technologies
2022-03-02 00:00:00 +00:00
parent 4818405514
commit 5b4aaa8b59
205 changed files with 6971 additions and 2722 deletions

View File

@@ -22,6 +22,10 @@ namespace Unity.Netcode.Editor.CodeGen
public static readonly string ClientRpcAttribute_FullName = typeof(ClientRpcAttribute).FullName;
public static readonly string ServerRpcParams_FullName = typeof(ServerRpcParams).FullName;
public static readonly string ClientRpcParams_FullName = typeof(ClientRpcParams).FullName;
public static readonly string ClientRpcSendParams_FullName = typeof(ClientRpcSendParams).FullName;
public static readonly string ClientRpcReceiveParams_FullName = typeof(ClientRpcReceiveParams).FullName;
public static readonly string ServerRpcSendParams_FullName = typeof(ServerRpcSendParams).FullName;
public static readonly string ServerRpcReceiveParams_FullName = typeof(ServerRpcReceiveParams).FullName;
public static readonly string INetworkSerializable_FullName = typeof(INetworkSerializable).FullName;
public static readonly string UnityColor_FullName = typeof(Color).FullName;
public static readonly string UnityColor32_FullName = typeof(Color32).FullName;

View File

@@ -13,7 +13,6 @@ using MethodAttributes = Mono.Cecil.MethodAttributes;
namespace Unity.Netcode.Editor.CodeGen
{
internal sealed class INetworkMessageILPP : ILPPInterface
{
public override ILPPInterface GetInstance() => this;
@@ -31,7 +30,6 @@ namespace Unity.Netcode.Editor.CodeGen
return null;
}
m_Diagnostics.Clear();
// read
@@ -95,27 +93,23 @@ namespace Unity.Netcode.Editor.CodeGen
}
private TypeReference m_FastBufferReader_TypeRef;
private TypeReference m_NetworkContext_TypeRef;
private MethodReference m_MessagingSystem_ReceiveMessage_MethodRef;
private TypeReference m_MessagingSystem_MessageWithHandler_TypeRef;
private MethodReference m_MessagingSystem_MessageHandler_Constructor_TypeRef;
private FieldReference m_ILPPMessageProvider___network_message_types_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_MessageType_FieldRef;
private FieldReference m_MessagingSystem_MessageWithHandler_Handler_FieldRef;
private MethodReference m_Type_GetTypeFromHandle_MethodRef;
private MethodReference m_List_Add_MethodRef;
private const string k_ReceiveMessageName = nameof(MessagingSystem.ReceiveMessage);
private bool ImportReferences(ModuleDefinition moduleDefinition)
{
m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(typeof(FastBufferReader));
m_NetworkContext_TypeRef = moduleDefinition.ImportReference(typeof(NetworkContext));
m_MessagingSystem_MessageHandler_Constructor_TypeRef =
moduleDefinition.ImportReference(typeof(MessagingSystem.MessageHandler).GetConstructors()[0]);
m_MessagingSystem_MessageHandler_Constructor_TypeRef = moduleDefinition.ImportReference(typeof(MessagingSystem.MessageHandler).GetConstructors()[0]);
var messageWithHandlerType = typeof(MessagingSystem.MessageWithHandler);
m_MessagingSystem_MessageWithHandler_TypeRef =
moduleDefinition.ImportReference(messageWithHandlerType);
m_MessagingSystem_MessageWithHandler_TypeRef = moduleDefinition.ImportReference(messageWithHandlerType);
foreach (var fieldInfo in messageWithHandlerType.GetFields())
{
switch (fieldInfo.Name)
@@ -162,38 +156,18 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
return true;
}
private MethodReference GetNetworkMessageRecieveHandler(TypeDefinition typeDefinition)
{
SequencePoint typeSequence = null;
foreach (var method in typeDefinition.Methods)
var messagingSystemType = typeof(MessagingSystem);
foreach (var methodInfo in messagingSystemType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
{
var resolved = method.Resolve();
var methodSequence = resolved.DebugInformation.SequencePoints.FirstOrDefault();
if (typeSequence == null || methodSequence.StartLine < typeSequence.StartLine)
switch (methodInfo.Name)
{
typeSequence = methodSequence;
}
if (resolved.IsStatic && resolved.IsPublic && resolved.Name == "Receive" && resolved.Parameters.Count == 2
&& !resolved.Parameters[0].IsIn
&& !resolved.Parameters[0].ParameterType.IsByReference
&& resolved.Parameters[0].ParameterType.Resolve() ==
m_FastBufferReader_TypeRef.Resolve()
&& resolved.Parameters[1].IsIn
&& resolved.Parameters[1].ParameterType.IsByReference
&& resolved.Parameters[1].ParameterType.GetElementType().Resolve() == m_NetworkContext_TypeRef.Resolve()
&& resolved.ReturnType == resolved.Module.TypeSystem.Void)
{
return method;
case k_ReceiveMessageName:
m_MessagingSystem_ReceiveMessage_MethodRef = moduleDefinition.ImportReference(methodInfo);
break;
}
}
m_Diagnostics.AddError(typeSequence, $"Class {typeDefinition.FullName} does not implement required method: `public static void Receive(FastBufferReader, in NetworkContext)`");
return null;
return true;
}
private MethodDefinition GetOrCreateStaticConstructor(TypeDefinition typeDefinition)
@@ -264,11 +238,8 @@ namespace Unity.Netcode.Editor.CodeGen
foreach (var type in networkMessageTypes)
{
var receiveMethod = GetNetworkMessageRecieveHandler(type);
if (receiveMethod == null)
{
continue;
}
var receiveMethod = new GenericInstanceMethod(m_MessagingSystem_ReceiveMessage_MethodRef);
receiveMethod.GenericArguments.Add(type);
CreateInstructionsToRegisterType(processor, instructions, type, receiveMethod);
}

View File

@@ -7,7 +7,6 @@ using System.Runtime.CompilerServices;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Unity.Collections;
using Unity.CompilationPipeline.Common.Diagnostics;
using Unity.CompilationPipeline.Common.ILPostProcessing;
using UnityEngine;
@@ -17,7 +16,6 @@ using ILPPInterface = Unity.CompilationPipeline.Common.ILPostProcessing.ILPostPr
namespace Unity.Netcode.Editor.CodeGen
{
internal sealed class NetworkBehaviourILPP : ILPPInterface
{
private const string k_ReadValueMethodName = nameof(FastBufferReader.ReadValueSafe);
@@ -25,7 +23,8 @@ namespace Unity.Netcode.Editor.CodeGen
public override ILPPInterface GetInstance() => this;
public override bool WillProcess(ICompiledAssembly compiledAssembly) => compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
public override bool WillProcess(ICompiledAssembly compiledAssembly) =>
compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == CodeGenHelpers.RuntimeAssemblyName);
private readonly List<DiagnosticMessage> m_Diagnostics = new List<DiagnosticMessage>();
@@ -109,8 +108,10 @@ namespace Unity.Netcode.Editor.CodeGen
private FieldReference m_NetworkManager_rpc_name_table_FieldRef;
private MethodReference m_NetworkManager_rpc_name_table_Add_MethodRef;
private TypeReference m_NetworkBehaviour_TypeRef;
private MethodReference m_NetworkBehaviour_SendServerRpc_MethodRef;
private MethodReference m_NetworkBehaviour_SendClientRpc_MethodRef;
private MethodReference m_NetworkBehaviour_beginSendServerRpc_MethodRef;
private MethodReference m_NetworkBehaviour_endSendServerRpc_MethodRef;
private MethodReference m_NetworkBehaviour_beginSendClientRpc_MethodRef;
private MethodReference m_NetworkBehaviour_endSendClientRpc_MethodRef;
private FieldReference m_NetworkBehaviour_rpc_exec_stage_FieldRef;
private MethodReference m_NetworkBehaviour_getNetworkManager_MethodRef;
private MethodReference m_NetworkBehaviour_getOwnerClientId_MethodRef;
@@ -124,8 +125,6 @@ namespace Unity.Netcode.Editor.CodeGen
private TypeReference m_ClientRpcParams_TypeRef;
private TypeReference m_FastBufferWriter_TypeRef;
private MethodReference m_FastBufferWriter_Constructor;
private MethodReference m_FastBufferWriter_Dispose;
private Dictionary<string, MethodReference> m_FastBufferWriter_WriteValue_MethodRefs = new Dictionary<string, MethodReference>();
private List<MethodReference> m_FastBufferWriter_ExtensionMethodRefs = new List<MethodReference>();
@@ -144,8 +143,10 @@ namespace Unity.Netcode.Editor.CodeGen
private const string k_NetworkManager_rpc_name_table = nameof(NetworkManager.__rpc_name_table);
private const string k_NetworkBehaviour_rpc_exec_stage = nameof(NetworkBehaviour.__rpc_exec_stage);
private const string k_NetworkBehaviour_SendServerRpc = nameof(NetworkBehaviour.__sendServerRpc);
private const string k_NetworkBehaviour_SendClientRpc = nameof(NetworkBehaviour.__sendClientRpc);
private const string k_NetworkBehaviour_beginSendServerRpc = nameof(NetworkBehaviour.__beginSendServerRpc);
private const string k_NetworkBehaviour_endSendServerRpc = nameof(NetworkBehaviour.__endSendServerRpc);
private const string k_NetworkBehaviour_beginSendClientRpc = nameof(NetworkBehaviour.__beginSendClientRpc);
private const string k_NetworkBehaviour_endSendClientRpc = nameof(NetworkBehaviour.__endSendClientRpc);
private const string k_NetworkBehaviour_NetworkManager = nameof(NetworkBehaviour.NetworkManager);
private const string k_NetworkBehaviour_OwnerClientId = nameof(NetworkBehaviour.OwnerClientId);
@@ -234,11 +235,17 @@ namespace Unity.Netcode.Editor.CodeGen
{
switch (methodInfo.Name)
{
case k_NetworkBehaviour_SendServerRpc:
m_NetworkBehaviour_SendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
case k_NetworkBehaviour_beginSendServerRpc:
m_NetworkBehaviour_beginSendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
break;
case k_NetworkBehaviour_SendClientRpc:
m_NetworkBehaviour_SendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
case k_NetworkBehaviour_endSendServerRpc:
m_NetworkBehaviour_endSendServerRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
break;
case k_NetworkBehaviour_beginSendClientRpc:
m_NetworkBehaviour_beginSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
break;
case k_NetworkBehaviour_endSendClientRpc:
m_NetworkBehaviour_endSendClientRpc_MethodRef = moduleDefinition.ImportReference(methodInfo);
break;
}
}
@@ -299,17 +306,12 @@ namespace Unity.Netcode.Editor.CodeGen
var fastBufferWriterType = typeof(FastBufferWriter);
m_FastBufferWriter_TypeRef = moduleDefinition.ImportReference(fastBufferWriterType);
m_FastBufferWriter_Constructor = moduleDefinition.ImportReference(
fastBufferWriterType.GetConstructor(new[] { typeof(int), typeof(Allocator), typeof(int) }));
m_FastBufferWriter_Dispose = moduleDefinition.ImportReference(fastBufferWriterType.GetMethod("Dispose"));
var fastBufferReaderType = typeof(FastBufferReader);
m_FastBufferReader_TypeRef = moduleDefinition.ImportReference(fastBufferReaderType);
// Find all extension methods for FastBufferReader and FastBufferWriter to enable user-implemented
// methods to be called.
var assemblies = new List<AssemblyDefinition>();
assemblies.Add(m_MainModule.Assembly);
var assemblies = new List<AssemblyDefinition> { m_MainModule.Assembly };
foreach (var reference in m_MainModule.AssemblyReferences)
{
var assembly = m_AssemblyResolver.Resolve(reference);
@@ -319,8 +321,7 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
var extensionConstructor =
moduleDefinition.ImportReference(typeof(ExtensionAttribute).GetConstructor(new Type[] { }));
var extensionConstructor = moduleDefinition.ImportReference(typeof(ExtensionAttribute).GetConstructor(new Type[] { }));
foreach (var assembly in assemblies)
{
foreach (var module in assembly.Modules)
@@ -332,6 +333,7 @@ namespace Unity.Netcode.Editor.CodeGen
{
continue;
}
foreach (var method in type.Methods)
{
if (!method.IsStatic)
@@ -356,13 +358,11 @@ namespace Unity.Netcode.Editor.CodeGen
var parameters = method.Parameters;
if (parameters.Count == 2
&& parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve())
if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferWriter_TypeRef.MakeByReferenceType().Resolve())
{
m_FastBufferWriter_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method));
}
else if (parameters.Count == 2
&& parameters[0].ParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve())
else if (parameters.Count == 2 && parameters[0].ParameterType.Resolve() == m_FastBufferReader_TypeRef.MakeByReferenceType().Resolve())
{
m_FastBufferReader_ExtensionMethodRefs.Add(m_MainModule.ImportReference(method));
}
@@ -395,9 +395,20 @@ namespace Unity.Netcode.Editor.CodeGen
continue;
}
if (methodDefinition.HasCustomAttributes)
{
foreach (var attribute in methodDefinition.CustomAttributes)
{
if (attribute.AttributeType.Name == nameof(AsyncStateMachineAttribute))
{
m_Diagnostics.AddError(methodDefinition, $"{methodDefinition.FullName}: RPCs cannot be 'async'");
}
}
}
InjectWriteAndCallBlocks(methodDefinition, rpcAttribute, rpcMethodId);
rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute)));
rpcHandlers.Add((rpcMethodId, GenerateStaticHandler(methodDefinition, rpcAttribute, rpcMethodId)));
if (isEditorOrDevelopment)
{
@@ -477,7 +488,6 @@ namespace Unity.Netcode.Editor.CodeGen
private CustomAttribute CheckAndGetRpcAttribute(MethodDefinition methodDefinition)
{
CustomAttribute rpcAttribute = null;
bool isServerRpc = false;
foreach (var customAttribute in methodDefinition.CustomAttributes)
{
var customAttributeType_FullName = customAttribute.AttributeType.FullName;
@@ -521,7 +531,6 @@ namespace Unity.Netcode.Editor.CodeGen
if (isValid)
{
isServerRpc = customAttributeType_FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
rpcAttribute = customAttribute;
}
}
@@ -569,12 +578,12 @@ namespace Unity.Netcode.Editor.CodeGen
checkType = paramType.GetElementType().Resolve();
}
if (
(parameters[0].ParameterType.Resolve() == checkType
|| (parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn)))
if ((parameters[0].ParameterType.Resolve() == checkType ||
(parameters[0].ParameterType.Resolve() == checkType.MakeByReferenceType().Resolve() && parameters[0].IsIn)))
{
return method;
}
if (method.HasGenericParameters && method.GenericParameters.Count == 1)
{
if (method.GenericParameters[0].HasConstraints)
@@ -584,17 +593,15 @@ namespace Unity.Netcode.Editor.CodeGen
{
var resolvedConstraint = constraint.Resolve();
if (
(resolvedConstraint.IsInterface &&
!checkType.HasInterface(resolvedConstraint.FullName))
|| (resolvedConstraint.IsClass &&
!checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName))
|| (resolvedConstraint.Name == "ValueType" && !checkType.IsValueType))
if ((resolvedConstraint.IsInterface && !checkType.HasInterface(resolvedConstraint.FullName)) ||
(resolvedConstraint.IsClass && !checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)) ||
(resolvedConstraint.Name == "ValueType" && !checkType.IsValueType))
{
meetsConstraints = false;
break;
}
}
if (meetsConstraints)
{
var instanceMethod = new GenericInstanceMethod(method);
@@ -624,8 +631,8 @@ namespace Unity.Netcode.Editor.CodeGen
{
if (parameters[1].IsIn)
{
if (parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve()
&& ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
if (parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() &&
((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
{
methodRef = method;
m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef;
@@ -635,8 +642,8 @@ namespace Unity.Netcode.Editor.CodeGen
else
{
if (parameters[1].ParameterType.Resolve() == paramType.Resolve()
&& parameters[1].ParameterType.IsArray == paramType.IsArray)
if (parameters[1].ParameterType.Resolve() == paramType.Resolve() &&
parameters[1].ParameterType.IsArray == paramType.IsArray)
{
methodRef = method;
m_FastBufferWriter_WriteValue_MethodRefs[assemblyQualifiedName] = methodRef;
@@ -707,11 +714,8 @@ namespace Unity.Netcode.Editor.CodeGen
{
var resolvedConstraint = constraint.Resolve();
if (
(resolvedConstraint.IsInterface &&
checkType.HasInterface(resolvedConstraint.FullName))
|| (resolvedConstraint.IsClass &&
checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)))
if ((resolvedConstraint.IsInterface && checkType.HasInterface(resolvedConstraint.FullName)) ||
(resolvedConstraint.IsClass && checkType.Resolve().IsSubclassOf(resolvedConstraint.FullName)))
{
var instanceMethod = new GenericInstanceMethod(method);
instanceMethod.GenericArguments.Add(checkType);
@@ -736,11 +740,10 @@ namespace Unity.Netcode.Editor.CodeGen
foreach (var method in m_FastBufferReader_ExtensionMethodRefs)
{
var parameters = method.Resolve().Parameters;
if (
method.Name == k_ReadValueMethodName
&& parameters[1].IsOut
&& parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve()
&& ((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
if (method.Name == k_ReadValueMethodName &&
parameters[1].IsOut &&
parameters[1].ParameterType.Resolve() == paramType.MakeByReferenceType().Resolve() &&
((ByReferenceType)parameters[1].ParameterType).ElementType.IsArray == paramType.IsArray)
{
methodRef = method;
m_FastBufferReader_ReadValue_MethodRefs[assemblyQualifiedName] = methodRef;
@@ -772,8 +775,8 @@ namespace Unity.Netcode.Editor.CodeGen
var instructions = new List<Instruction>();
var processor = methodDefinition.Body.GetILProcessor();
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
var requireOwnership = true; // default value MUST be = `ServerRpcAttribute.RequireOwnership`
var rpcDelivery = RpcDelivery.Reliable; // default value MUST be = `RpcAttribute.Delivery`
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
var rpcDelivery = RpcDelivery.Reliable; // default value MUST be == `RpcAttribute.Delivery`
foreach (var attrField in rpcAttribute.Fields)
{
switch (attrField.Name)
@@ -797,9 +800,9 @@ namespace Unity.Netcode.Editor.CodeGen
// NetworkManager networkManager;
methodDefinition.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
int netManLocIdx = methodDefinition.Body.Variables.Count - 1;
// NetworkSerializer serializer;
// FastBufferWriter bufferWriter;
methodDefinition.Body.Variables.Add(new VariableDefinition(m_FastBufferWriter_TypeRef));
int serializerLocIdx = methodDefinition.Body.Variables.Count - 1;
int bufWriterLocIdx = methodDefinition.Body.Variables.Count - 1;
// XXXRpcParams
if (!hasRpcParams)
@@ -854,6 +857,8 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(beginInstr);
// var bufferWriter = __beginSendServerRpc(rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc
// var bufferWriter = __beginSendClientRpc(rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc
if (isServerRpc)
{
// ServerRpc
@@ -867,8 +872,7 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(processor.Create(OpCodes.Ldarg_0));
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_getOwnerClientId_MethodRef));
instructions.Add(processor.Create(OpCodes.Ldloc, netManLocIdx));
instructions.Add(
processor.Create(OpCodes.Callvirt, m_NetworkManager_getLocalClientId_MethodRef));
instructions.Add(processor.Create(OpCodes.Callvirt, m_NetworkManager_getLocalClientId_MethodRef));
instructions.Add(processor.Create(OpCodes.Ceq));
instructions.Add(processor.Create(OpCodes.Ldc_I4, 0));
instructions.Add(processor.Create(OpCodes.Ceq));
@@ -886,8 +890,7 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(processor.Create(OpCodes.Brfalse, logNextInstr));
// Debug.LogError(...);
instructions.Add(processor.Create(OpCodes.Ldstr,
"Only the owner can invoke a ServerRpc that requires ownership!"));
instructions.Add(processor.Create(OpCodes.Ldstr, "Only the owner can invoke a ServerRpc that requires ownership!"));
instructions.Add(processor.Create(OpCodes.Call, m_Debug_LogError_MethodRef));
instructions.Add(logNextInstr);
@@ -895,31 +898,86 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(roReturnInstr);
instructions.Add(roLastInstr);
}
// var bufferWriter = __beginSendServerRpc(rpcMethodId, serverRpcParams, rpcDelivery);
instructions.Add(processor.Create(OpCodes.Ldarg_0));
// rpcMethodId
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
// rpcParams
instructions.Add(hasRpcParams ? processor.Create(OpCodes.Ldarg, paramCount) : processor.Create(OpCodes.Ldloc, rpcParamsIdx));
// rpcDelivery
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
// __beginSendServerRpc
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_beginSendServerRpc_MethodRef));
instructions.Add(processor.Create(OpCodes.Stloc, bufWriterLocIdx));
}
else
{
// ClientRpc
// var writer = new FastBufferWriter(1285, Allocator.Temp, 63985);
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldc_I4, 1300 - sizeof(byte) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)));
instructions.Add(processor.Create(OpCodes.Ldc_I4_2));
instructions.Add(processor.Create(OpCodes.Ldc_I4, 64000 - sizeof(byte) - sizeof(ulong) - sizeof(uint) - sizeof(ushort)));
instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Constructor));
// var bufferWriter = __beginSendClientRpc(rpcMethodId, clientRpcParams, rpcDelivery);
instructions.Add(processor.Create(OpCodes.Ldarg_0));
var firstInstruction = processor.Create(OpCodes.Nop);
instructions.Add(firstInstruction);
// rpcMethodId
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
// rpcParams
instructions.Add(hasRpcParams ? processor.Create(OpCodes.Ldarg, paramCount) : processor.Create(OpCodes.Ldloc, rpcParamsIdx));
// rpcDelivery
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
// __beginSendClientRpc
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_beginSendClientRpc_MethodRef));
instructions.Add(processor.Create(OpCodes.Stloc, bufWriterLocIdx));
}
// write method parameters into stream
for (int paramIndex = 0; paramIndex < paramCount; ++paramIndex)
{
var paramDef = methodDefinition.Parameters[paramIndex];
var paramType = paramDef.ParameterType;
// ServerRpcParams
if (paramType.FullName == CodeGenHelpers.ServerRpcParams_FullName && isServerRpc && paramIndex == paramCount - 1)
if (paramType.FullName == CodeGenHelpers.ClientRpcSendParams_FullName ||
paramType.FullName == CodeGenHelpers.ClientRpcReceiveParams_FullName)
{
m_Diagnostics.AddError($"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ClientRpcParams)} instead.");
continue;
}
if (paramType.FullName == CodeGenHelpers.ServerRpcSendParams_FullName ||
paramType.FullName == CodeGenHelpers.ServerRpcReceiveParams_FullName)
{
m_Diagnostics.AddError($"Rpcs may not accept {paramType.FullName} as a parameter. Use {nameof(ServerRpcParams)} instead.");
continue;
}
// ServerRpcParams
if (paramType.FullName == CodeGenHelpers.ServerRpcParams_FullName)
{
if (paramIndex != paramCount - 1)
{
m_Diagnostics.AddError(methodDefinition, $"{nameof(ServerRpcParams)} must be the last parameter in a ServerRpc.");
}
if (!isServerRpc)
{
m_Diagnostics.AddError($"ClientRpcs may not accept {nameof(ServerRpcParams)} as a parameter.");
}
continue;
}
// ClientRpcParams
if (paramType.FullName == CodeGenHelpers.ClientRpcParams_FullName && !isServerRpc && paramIndex == paramCount - 1)
if (paramType.FullName == CodeGenHelpers.ClientRpcParams_FullName)
{
if (paramIndex != paramCount - 1)
{
m_Diagnostics.AddError(methodDefinition, $"{nameof(ClientRpcParams)} must be the last parameter in a ClientRpc.");
}
if (isServerRpc)
{
m_Diagnostics.AddError($"ServerRpcs may not accept {nameof(ClientRpcParams)} as a parameter.");
}
continue;
}
@@ -942,8 +1000,8 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(processor.Create(OpCodes.Cgt_Un));
instructions.Add(processor.Create(OpCodes.Stloc, isSetLocalIndex));
// writer.WriteValueSafe(isSet);
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
// bufferWriter.WriteValueSafe(isSet);
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
instructions.Add(processor.Create(OpCodes.Ldloca, isSetLocalIndex));
instructions.Add(processor.Create(OpCodes.Call, boolMethodRef));
@@ -956,11 +1014,11 @@ namespace Unity.Netcode.Editor.CodeGen
var foundMethodRef = GetWriteMethodForParameter(paramType, out var methodRef);
if (foundMethodRef)
{
// writer.WriteNetworkSerializable(param) for INetworkSerializable, OR
// writer.WriteNetworkSerializable(param, -1, 0) for INetworkSerializable arrays, OR
// writer.WriteValueSafe(param) for value types, OR
// writer.WriteValueSafe(param, -1, 0) for arrays of value types, OR
// writer.WriteValueSafe(param, false) for strings
// bufferWriter.WriteNetworkSerializable(param) for INetworkSerializable, OR
// bufferWriter.WriteNetworkSerializable(param, -1, 0) for INetworkSerializable arrays, OR
// bufferWriter.WriteValueSafe(param) for value types, OR
// bufferWriter.WriteValueSafe(param, -1, 0) for arrays of value types, OR
// bufferWriter.WriteValueSafe(param, false) for strings
var method = methodRef.Resolve();
var checkParameter = method.Parameters[0];
var isExtensionMethod = false;
@@ -971,11 +1029,11 @@ namespace Unity.Netcode.Editor.CodeGen
}
if (!isExtensionMethod || method.Parameters[0].ParameterType.IsByReference)
{
instructions.Add(processor.Create(OpCodes.Ldloca, serializerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
}
else
{
instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx));
instructions.Add(processor.Create(OpCodes.Ldloc, bufWriterLocIdx));
}
if (checkParameter.IsIn || checkParameter.IsOut || checkParameter.ParameterType.IsByReference)
{
@@ -986,16 +1044,14 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(processor.Create(OpCodes.Ldarg, paramIndex + 1));
}
// Special handling for WriteValue() on arrays and strings since they have additional arguments.
if (paramType.IsArray
&& ((!isExtensionMethod && methodRef.Parameters.Count == 3)
|| (isExtensionMethod && methodRef.Parameters.Count == 4)))
if (paramType.IsArray && ((!isExtensionMethod && methodRef.Parameters.Count == 3) ||
(isExtensionMethod && methodRef.Parameters.Count == 4)))
{
instructions.Add(processor.Create(OpCodes.Ldc_I4_M1));
instructions.Add(processor.Create(OpCodes.Ldc_I4_0));
}
else if (paramType == typeSystem.String
&& ((!isExtensionMethod && methodRef.Parameters.Count == 2)
|| (isExtensionMethod && methodRef.Parameters.Count == 3)))
else if (paramType == typeSystem.String && ((!isExtensionMethod && methodRef.Parameters.Count == 2) ||
(isExtensionMethod && methodRef.Parameters.Count == 3)))
{
instructions.Add(processor.Create(OpCodes.Ldc_I4_0));
}
@@ -1015,20 +1071,20 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.Add(endInstr);
// __sendServerRpc(ref serializer, rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc
// __sendClientRpc(ref serializer, rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc
// __endSendServerRpc(ref bufferWriter, rpcMethodId, serverRpcParams, rpcDelivery) -> ServerRpc
// __endSendClientRpc(ref bufferWriter, rpcMethodId, clientRpcParams, rpcDelivery) -> ClientRpc
if (isServerRpc)
{
// ServerRpc
// __sendServerRpc(ref serializer, rpcMethodId, serverRpcParams, rpcDelivery);
// __endSendServerRpc(ref bufferWriter, rpcMethodId, serverRpcParams, rpcDelivery);
instructions.Add(processor.Create(OpCodes.Ldarg_0));
// serializer
instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx));
// bufferWriter
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
// rpcMethodId
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
if (hasRpcParams)
{
// rpcParams
@@ -1039,25 +1095,24 @@ namespace Unity.Netcode.Editor.CodeGen
// default
instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx));
}
// rpcDelivery
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
// EndSendServerRpc
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_SendServerRpc_MethodRef));
// __endSendServerRpc
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_endSendServerRpc_MethodRef));
}
else
{
// ClientRpc
// __sendClientRpc(ref serializer, rpcMethodId, clientRpcParams, rpcDelivery);
// __endSendClientRpc(ref bufferWriter, rpcMethodId, clientRpcParams, rpcDelivery);
instructions.Add(processor.Create(OpCodes.Ldarg_0));
// serializer
instructions.Add(processor.Create(OpCodes.Ldloc, serializerLocIdx));
// bufferWriter
instructions.Add(processor.Create(OpCodes.Ldloca, bufWriterLocIdx));
// rpcMethodId
instructions.Add(processor.Create(OpCodes.Ldc_I4, unchecked((int)rpcMethodId)));
if (hasRpcParams)
{
// rpcParams
@@ -1068,36 +1123,11 @@ namespace Unity.Netcode.Editor.CodeGen
// default
instructions.Add(processor.Create(OpCodes.Ldloc, rpcParamsIdx));
}
// rpcDelivery
instructions.Add(processor.Create(OpCodes.Ldc_I4, (int)rpcDelivery));
// EndSendClientRpc
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_SendClientRpc_MethodRef));
}
{
// TODO: Figure out why try/catch here cause the try block not to execute at all.
// End try block
//instructions.Add(processor.Create(OpCodes.Leave, lastInstr));
// writer.Dispose();
var handlerFirst = processor.Create(OpCodes.Ldloca, serializerLocIdx);
instructions.Add(handlerFirst);
instructions.Add(processor.Create(OpCodes.Call, m_FastBufferWriter_Dispose));
// End finally block
//instructions.Add(processor.Create(OpCodes.Endfinally));
// try { ... serialization code ... } finally { writer.Dispose(); }
/*var handler = new ExceptionHandler(ExceptionHandlerType.Finally)
{
TryStart = firstInstruction,
TryEnd = handlerFirst,
HandlerStart = handlerFirst,
HandlerEnd = lastInstr
};
processor.Body.ExceptionHandlers.Add(handler);*/
// __endSendClientRpc
instructions.Add(processor.Create(OpCodes.Call, m_NetworkBehaviour_endSendClientRpc_MethodRef));
}
instructions.Add(lastInstr);
@@ -1132,25 +1162,21 @@ namespace Unity.Netcode.Editor.CodeGen
instructions.ForEach(instruction => processor.Body.Instructions.Insert(0, instruction));
}
private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute)
private MethodDefinition GenerateStaticHandler(MethodDefinition methodDefinition, CustomAttribute rpcAttribute, uint rpcMethodId)
{
var typeSystem = methodDefinition.Module.TypeSystem;
var nhandler = new MethodDefinition(
$"{methodDefinition.Name}__nhandler",
var rpcHandler = new MethodDefinition(
$"__rpc_handler_{rpcMethodId}",
MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig,
methodDefinition.Module.TypeSystem.Void);
nhandler.Parameters.Add(new ParameterDefinition("target", ParameterAttributes.None, m_NetworkBehaviour_TypeRef));
nhandler.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, m_FastBufferReader_TypeRef));
nhandler.Parameters.Add(new ParameterDefinition("rpcParams", ParameterAttributes.None, m_RpcParams_TypeRef));
rpcHandler.Parameters.Add(new ParameterDefinition("target", ParameterAttributes.None, m_NetworkBehaviour_TypeRef));
rpcHandler.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, m_FastBufferReader_TypeRef));
rpcHandler.Parameters.Add(new ParameterDefinition("rpcParams", ParameterAttributes.None, m_RpcParams_TypeRef));
var processor = nhandler.Body.GetILProcessor();
// begin Try/Catch
var tryStart = processor.Create(OpCodes.Nop);
processor.Append(tryStart);
var processor = rpcHandler.Body.GetILProcessor();
var isServerRpc = rpcAttribute.AttributeType.FullName == CodeGenHelpers.ServerRpcAttribute_FullName;
var requireOwnership = true; // default value MUST be = `ServerRpcAttribute.RequireOwnership`
var requireOwnership = true; // default value MUST be == `ServerRpcAttribute.RequireOwnership`
foreach (var attrField in rpcAttribute.Fields)
{
switch (attrField.Name)
@@ -1161,10 +1187,10 @@ namespace Unity.Netcode.Editor.CodeGen
}
}
nhandler.Body.InitLocals = true;
rpcHandler.Body.InitLocals = true;
// NetworkManager networkManager;
nhandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
int netManLocIdx = nhandler.Body.Variables.Count - 1;
rpcHandler.Body.Variables.Add(new VariableDefinition(m_NetworkManager_TypeRef));
int netManLocIdx = rpcHandler.Body.Variables.Count - 1;
{
var returnInstr = processor.Create(OpCodes.Ret);
@@ -1233,8 +1259,8 @@ namespace Unity.Netcode.Editor.CodeGen
var paramType = paramDef.ParameterType;
// local variable
nhandler.Body.Variables.Add(new VariableDefinition(paramType));
int localIndex = nhandler.Body.Variables.Count - 1;
rpcHandler.Body.Variables.Add(new VariableDefinition(paramType));
int localIndex = rpcHandler.Body.Variables.Count - 1;
paramLocalMap[paramIndex] = localIndex;
// ServerRpcParams, ClientRpcParams
@@ -1268,8 +1294,8 @@ namespace Unity.Netcode.Editor.CodeGen
}
// reader.ReadValueSafe(out bool isSet)
nhandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean));
int isSetLocalIndex = nhandler.Body.Variables.Count - 1;
rpcHandler.Body.Variables.Add(new VariableDefinition(typeSystem.Boolean));
int isSetLocalIndex = rpcHandler.Body.Variables.Count - 1;
processor.Emit(OpCodes.Ldarga, 1);
processor.Emit(OpCodes.Ldloca, isSetLocalIndex);
processor.Emit(OpCodes.Call, boolMethodRef);
@@ -1336,55 +1362,8 @@ namespace Unity.Netcode.Editor.CodeGen
processor.Emit(OpCodes.Ldc_I4, (int)NetworkBehaviour.__RpcExecStage.None);
processor.Emit(OpCodes.Stfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef);
// pull in the Exception Module
var exception = m_MainModule.ImportReference(typeof(Exception));
// Get Exception.ToString()
var exp = m_MainModule.ImportReference(typeof(Exception).GetMethod("ToString", new Type[] { }));
// Get String.Format (This is equivalent to an interpolated string)
var stringFormat = m_MainModule.ImportReference(typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object) }));
nhandler.Body.Variables.Add(new VariableDefinition(exception));
int exceptionVariableIndex = nhandler.Body.Variables.Count - 1;
//try ends/catch begins
var catchEnds = processor.Create(OpCodes.Nop);
processor.Emit(OpCodes.Leave, catchEnds);
// Load the Exception onto the stack
var catchStarts = processor.Create(OpCodes.Stloc, exceptionVariableIndex);
processor.Append(catchStarts);
// Load string for the error log that will be shown
processor.Emit(OpCodes.Ldstr, $"Unhandled RPC Exception:\n {{0}}");
processor.Emit(OpCodes.Ldloc, exceptionVariableIndex);
processor.Emit(OpCodes.Callvirt, exp);
processor.Emit(OpCodes.Call, stringFormat);
// Call Debug.LogError
processor.Emit(OpCodes.Call, m_Debug_LogError_MethodRef);
// reset NetworkBehaviour.__rpc_exec_stage = __RpcExecStage.None;
processor.Emit(OpCodes.Ldarg_0);
processor.Emit(OpCodes.Ldc_I4, (int)NetworkBehaviour.__RpcExecStage.None);
processor.Emit(OpCodes.Stfld, m_NetworkBehaviour_rpc_exec_stage_FieldRef);
// catch ends
processor.Append(catchEnds);
processor.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Catch)
{
CatchType = exception,
TryStart = tryStart,
TryEnd = catchStarts,
HandlerStart = catchStarts,
HandlerEnd = catchEnds
});
processor.Emit(OpCodes.Ret);
return nhandler;
return rpcHandler;
}
}
}

View File

@@ -134,8 +134,10 @@ namespace Unity.Netcode.Editor.CodeGen
foreach (var methodDefinition in typeDefinition.Methods)
{
if (methodDefinition.Name == nameof(NetworkBehaviour.__sendServerRpc)
|| methodDefinition.Name == nameof(NetworkBehaviour.__sendClientRpc))
if (methodDefinition.Name == nameof(NetworkBehaviour.__beginSendServerRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__endSendServerRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__beginSendClientRpc) ||
methodDefinition.Name == nameof(NetworkBehaviour.__endSendClientRpc))
{
methodDefinition.IsFamily = true;
}