From b07528d9cefbb26c0f1e9b6a85b27df04915b022 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 18 Jun 2023 17:11:15 +0200 Subject: [PATCH] Refactor Network RPC C# codegen to share code with data serializer --- Flax.sln.DotSettings | 1 + .../Build/Plugins/NetworkingPlugin.cs | 696 +++++++++++------- 2 files changed, 422 insertions(+), 275 deletions(-) diff --git a/Flax.sln.DotSettings b/Flax.sln.DotSettings index e71c2da2d..fe1d1463e 100644 --- a/Flax.sln.DotSettings +++ b/Flax.sln.DotSettings @@ -306,6 +306,7 @@ True True True + True True True True diff --git a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs index e047f5116..ed92fd5c1 100644 --- a/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs +++ b/Source/Tools/Flax.Build/Build/Plugins/NetworkingPlugin.cs @@ -760,8 +760,8 @@ namespace Flax.Build.Plugins else if (isNetworkReplicated) { // Generate serialization methods - GenerateSerializer(ref context, type, true, Thunk1); - GenerateSerializer(ref context, type, false, Thunk2); + GenerateDotnetSerializer(ref context, type, true, Thunk1); + GenerateDotnetSerializer(ref context, type, false, Thunk2); context.Modified = true; } } @@ -791,7 +791,7 @@ namespace Flax.Build.Plugins type.Methods.Add(m); } - private static MethodDefinition GenerateSerializer(ref DotnetContext context, TypeDefinition type, bool serialize, string name) + private static MethodDefinition GenerateDotnetSerializer(ref DotnetContext context, TypeDefinition type, bool serialize, string name) { ModuleDefinition module = type.Module; var m = new MethodDefinition(name, MethodAttributes.Public | MethodAttributes.HideBySig, context.VoidType); @@ -805,12 +805,14 @@ namespace Flax.Build.Plugins GenerateSerializeCallback(module, il, type.BaseType.Resolve(), serialize); } + var ildContext = new DotnetIlContext(il); + // Serialize all type fields marked with NetworkReplicated attribute foreach (FieldDefinition f in type.Fields) { if (!f.HasAttribute(NetworkReplicatedAttribute)) continue; - GenerateSerialization(ref context, type, serialize, f.FieldType, il, new DotnetValueContext(f)); + GenerateDotnetSerialization(ref context, serialize, ref ildContext, new DotnetValueContext(type, f)); } // Serialize all type properties marked with NetworkReplicated attribute @@ -818,7 +820,7 @@ namespace Flax.Build.Plugins { if (!p.HasAttribute(NetworkReplicatedAttribute)) continue; - GenerateSerialization(ref context, type, serialize, p.PropertyType, il, new DotnetValueContext(p)); + GenerateDotnetSerialization(ref context, serialize, ref ildContext, new DotnetValueContext(type, p)); } if (serialize) @@ -858,7 +860,7 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Stloc_1); // Generate normal serializer - var serializer = GenerateSerializer(ref context, type, serialize, name); + var serializer = GenerateDotnetSerializer(ref context, type, serialize, name); // Call serializer il.Emit(OpCodes.Ldloc_0); @@ -898,11 +900,132 @@ namespace Flax.Build.Plugins } } + private struct DotnetIlContext + { + public ILProcessor Il; + public MethodDefinition RPC; + public Instruction IlStart; + public int StreamLocalIndex; + + public bool IsRPC => RPC != null; + public Mono.Collections.Generic.Collection Variables => Il.Body.Variables; + + public bool InitLocals + { + get => Il.Body.InitLocals; + set => Il.Body.InitLocals = value; + } + + public DotnetIlContext(ILProcessor il, MethodDefinition rpc = null, Instruction ilStart = null) + { + Il = il; + RPC = rpc; + IlStart = ilStart; + StreamLocalIndex = -1; + } + + public Instruction Create(OpCode opcode) + { + return Il.Create(opcode); + } + + public void Emit(Instruction instruction) + { + if (IlStart != null) + Il.InsertBefore(IlStart, instruction); + else + Il.Append(instruction); + } + + public void Emit(OpCode opcode) + { + Emit(Il.Create(opcode)); + } + + public void Emit(OpCode opcode, TypeReference type) + { + Emit(Il.Create(opcode, type)); + } + + public void Emit(OpCode opcode, MethodReference method) + { + Emit(Il.Create(opcode, method)); + } + + public void Emit(OpCode opcode, CallSite site) + { + Emit(Il.Create(opcode, site)); + } + + public void Emit(OpCode opcode, FieldReference field) + { + Emit(Il.Create(opcode, field)); + } + + public void Emit(OpCode opcode, string value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, byte value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, sbyte value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, int value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, long value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, float value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, double value) + { + Emit(Il.Create(opcode, value)); + } + + public void Emit(OpCode opcode, Instruction target) + { + Emit(Il.Create(opcode, target)); + } + + public void Emit(OpCode opcode, Instruction[] targets) + { + Emit(Il.Create(opcode, targets)); + } + + public void Emit(OpCode opcode, VariableDefinition variable) + { + Emit(Il.Create(opcode, variable)); + } + + public void Emit(OpCode opcode, ParameterDefinition parameter) + { + Emit(Il.Create(opcode, parameter)); + } + } + private struct DotnetValueContext { + public TypeReference Type; + public TypeReference ValueType; public FieldReference Field; public PropertyDefinition Property; public int LocalVarIndex; + public int ArgIndex; public OpCode PropertyGetOpCode { @@ -926,28 +1049,55 @@ namespace Flax.Build.Plugins } } - public DotnetValueContext(FieldDefinition field) + public DotnetValueContext(TypeReference type, FieldDefinition field) { + Type = type; + ValueType = field.FieldType; Field = field; Property = null; LocalVarIndex = -1; + ArgIndex = -1; } - public DotnetValueContext(PropertyDefinition property) + public DotnetValueContext(TypeReference type, PropertyDefinition property) { + Type = type; + ValueType = property.PropertyType; Field = null; Property = property; LocalVarIndex = -1; + ArgIndex = -1; } - public DotnetValueContext(int localVarIndex) + public DotnetValueContext(TypeReference type, int localVarIndex, TypeReference valueType) { + Type = type; + ValueType = valueType; Field = null; Property = null; LocalVarIndex = localVarIndex; + ArgIndex = -1; } - public void GetProperty(ILProcessor il, int propertyVar) + public bool Validate() + { + if (Property != null) + { + if (Property.GetMethod == null) + { + MonoCecil.CompilationError($"Missing getter method for property '{Property.Name}' of type {ValueType.FullName} in {Type.FullName} for automatic replication.", Property); + return true; + } + if (Property.SetMethod == null) + { + MonoCecil.CompilationError($"Missing setter method for property '{Property.Name}' of type {ValueType.FullName} in {Type.FullName} for automatic replication.", Property); + return true; + } + } + return false; + } + + public void GetProperty(ref DotnetIlContext il, int propertyVar) { if (Property != null) { @@ -959,7 +1109,7 @@ namespace Flax.Build.Plugins } } - public void SetProperty(ILProcessor il) + public void SetProperty(ref DotnetIlContext il) { if (Property != null) { @@ -970,9 +1120,13 @@ namespace Flax.Build.Plugins } } - public void Load(ILProcessor il) + public void Load(ref DotnetIlContext il) { - if (Field != null) + if (ArgIndex != -1) + { + il.Emit(OpCodes.Ldarg, ArgIndex); + } + else if (Field != null) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, Field); @@ -988,9 +1142,13 @@ namespace Flax.Build.Plugins } } - public void LoadAddress(ILProcessor il) + public void LoadAddress(ref DotnetIlContext il) { - if (Field != null) + if (ArgIndex != -1) + { + il.Emit(OpCodes.Ldarga, ArgIndex); + } + else if (Field != null) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, Field); @@ -1006,9 +1164,13 @@ namespace Flax.Build.Plugins } } - public void Store(ILProcessor il) + public void Store(ref DotnetIlContext il) { - if (Field != null) + if (ArgIndex != -1) + { + il.Emit(OpCodes.Starg, ArgIndex); + } + else if (Field != null) { il.Emit(OpCodes.Stfld, Field); } @@ -1023,59 +1185,47 @@ namespace Flax.Build.Plugins } } - private static void GenerateSerialization(ref DotnetContext context, TypeDefinition type, bool serialize, TypeReference valueType, ILProcessor il, DotnetValueContext valueContext) + private static void GenerateDotnetSerialization(ref DotnetContext context, bool serialize, ref DotnetIlContext il, DotnetValueContext valueContext) { - if (valueContext.Property != null) + if (valueContext.Validate()) { - if (valueContext.Property.GetMethod == null) - { - MonoCecil.CompilationError($"Missing getter method for property '{valueContext.Property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", valueContext.Property); - context.Failed = true; - return; - } - - if (valueContext.Property.SetMethod == null) - { - MonoCecil.CompilationError($"Missing setter method for property '{valueContext.Property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", valueContext.Property); - context.Failed = true; - return; - } + context.Failed = true; + return; } - - ModuleDefinition module = type.Module; - TypeDefinition valueTypeDef = valueType.Resolve(); + ModuleDefinition module = valueContext.Type.Module; + TypeDefinition valueTypeDef = valueContext.ValueType.Resolve(); TypeDefinition networkStreamType = context.NetworkStreamType.Resolve(); // Ensure to have valid serialization already generated for that value type (eg. when using custom structure field serialization) GenerateTypeSerialization(ref context, valueTypeDef); - if (valueType.IsArray) + if (valueContext.ValueType.IsArray) { - var elementType = valueType.GetElementType(); + var elementType = valueContext.ValueType.GetElementType(); var isRawPod = IsRawPOD(elementType); // Whether to use raw memory copy (eg. int, enum, Vector2) - var varStart = il.Body.Variables.Count; + var varStart = il.Variables.Count; module.GetType("System.Int32", out var intType); - il.Body.Variables.Add(new VariableDefinition(intType)); // [0] int length + il.Variables.Add(new VariableDefinition(intType)); // [0] int length if (isRawPod) { - il.Body.Variables.Add(new VariableDefinition(new PointerType(elementType))); // [1] * - il.Body.Variables.Add(new VariableDefinition(new PinnedType(valueType))); // [2] [] pinned + il.Variables.Add(new VariableDefinition(new PointerType(elementType))); // [1] * + il.Variables.Add(new VariableDefinition(new PinnedType(valueContext.ValueType))); // [2] [] pinned } else { - il.Body.Variables.Add(new VariableDefinition(intType)); // [1] int idx - il.Body.Variables.Add(new VariableDefinition(elementType)); // [2] + il.Variables.Add(new VariableDefinition(intType)); // [1] int idx + il.Variables.Add(new VariableDefinition(elementType)); // [2] } if (valueContext.Property != null) - il.Body.Variables.Add(new VariableDefinition(valueType)); // [3] [] - il.Body.InitLocals = true; - valueContext.GetProperty(il, varStart + 3); + il.Variables.Add(new VariableDefinition(valueContext.ValueType)); // [3] [] + il.InitLocals = true; + valueContext.GetProperty(ref il, varStart + 3); if (serialize) { // [] array = Array; il.Emit(OpCodes.Nop); - valueContext.Load(il); + valueContext.Load(ref il); // int length = ((array != null) ? array.Length : 0); il.Emit(OpCodes.Dup); @@ -1085,33 +1235,35 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Ldc_I4_0); Instruction jmp2 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Br_S, jmp2); - il.Append(jmp1); + il.Emit(jmp1); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); - il.Append(jmp2); + il.Emit(jmp2); il.Emit(OpCodes.Stloc, varStart + 0); // stream.WriteInt32(length); - il.Emit(OpCodes.Ldarg_1); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc, il.StreamLocalIndex); + else + il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloc, varStart + 0); - var m = networkStreamType.GetMethod("WriteInt32"); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteInt32"))); il.Emit(OpCodes.Nop); if (isRawPod) { // fixed (* bytes2 = Array) - valueContext.Load(il); + valueContext.Load(ref il); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, varStart + 2); Instruction jmp3 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brfalse_S, jmp3); - il.Emit(OpCodes.Ldloc_2); + il.Emit(OpCodes.Ldloc, varStart + 2); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); Instruction jmp4 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brtrue_S, jmp4); - il.Append(jmp3); + il.Emit(jmp3); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_U); il.Emit(OpCodes.Stloc, varStart + 1); // * @@ -1119,20 +1271,22 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Br_S, jmp5); // stream.WriteBytes((byte*)bytes, length * sizeof())); - il.Append(jmp4); + il.Emit(jmp4); il.Emit(OpCodes.Ldloc, varStart + 2); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ldelema, elementType); il.Emit(OpCodes.Conv_U); il.Emit(OpCodes.Stloc, varStart + 1); // * - il.Append(jmp5); - il.Emit(OpCodes.Ldarg_1); + il.Emit(jmp5); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc, il.StreamLocalIndex); + else + il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloc, varStart + 1); // * il.Emit(OpCodes.Ldloc, varStart + 0); il.Emit(OpCodes.Sizeof, elementType); il.Emit(OpCodes.Mul); - m = networkStreamType.GetMethod("WriteBytes", 2); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteBytes", 2))); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Stloc, varStart + 2); @@ -1147,15 +1301,15 @@ namespace Flax.Build.Plugins // element = array[idx] Instruction jmp4 = il.Create(OpCodes.Nop); - il.Append(jmp4); - valueContext.Load(il); + il.Emit(jmp4); + valueContext.Load(ref il); il.Emit(OpCodes.Ldloc, varStart + 1); // idx il.Emit(OpCodes.Ldelem_Ref); il.Emit(OpCodes.Stloc, varStart + 2); // // Serialize item value il.Emit(OpCodes.Nop); - GenerateSerialization(ref context, type, serialize, elementType, il, new DotnetValueContext(varStart + 2)); + GenerateDotnetSerialization(ref context, serialize, ref il, new DotnetValueContext(valueContext.Type, varStart + 2, elementType)); // idx++ il.Emit(OpCodes.Nop); @@ -1165,7 +1319,7 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Stloc, varStart + 1); // idx // idx < length - il.Append(jmp3); + il.Emit(jmp3); il.Emit(OpCodes.Ldloc, varStart + 1); // idx il.Emit(OpCodes.Ldloc, varStart + 0); // length il.Emit(OpCodes.Clt); @@ -1176,25 +1330,26 @@ namespace Flax.Build.Plugins { // int length = stream.ReadInt32(); il.Emit(OpCodes.Nop); - il.Emit(OpCodes.Ldarg_1); - var m = networkStreamType.GetMethod("ReadInt32"); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc_1); + else + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("ReadInt32"))); il.Emit(OpCodes.Stloc, varStart + 0); // length // System.Array.Resize(ref Array, length); - valueContext.LoadAddress(il); + valueContext.LoadAddress(ref il); il.Emit(OpCodes.Ldloc, varStart + 0); // length module.TryGetTypeReference("System.Array", out var arrayType); if (arrayType == null) module.GetType("System.Array", out arrayType); - m = arrayType.Resolve().GetMethod("Resize", 2); - il.Emit(OpCodes.Call, module.ImportReference(m.InflateGeneric(elementType))); + il.Emit(OpCodes.Call, module.ImportReference(arrayType.Resolve().GetMethod("Resize", 2).InflateGeneric(elementType))); il.Emit(OpCodes.Nop); if (isRawPod) { // fixed (* buffer = Array) - valueContext.Load(il); + valueContext.Load(ref il); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc, varStart + 2); Instruction jmp1 = il.Create(OpCodes.Nop); @@ -1204,7 +1359,7 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Conv_I4); Instruction jmp2 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brtrue_S, jmp2); - il.Append(jmp1); + il.Emit(jmp1); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Conv_U); il.Emit(OpCodes.Stloc, varStart + 1); // * buffer @@ -1212,20 +1367,22 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Br_S, jmp3); // stream.ReadBytes((byte*)buffer, length * sizeof()); - il.Append(jmp2); + il.Emit(jmp2); il.Emit(OpCodes.Ldloc, varStart + 2); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ldelema, elementType); il.Emit(OpCodes.Conv_U); il.Emit(OpCodes.Stloc, varStart + 1); // * buffer - il.Append(jmp3); - il.Emit(OpCodes.Ldarg_1); + il.Emit(jmp3); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc_1); + else + il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloc, varStart + 1); // * buffer il.Emit(OpCodes.Ldloc, varStart + 0); // length il.Emit(OpCodes.Sizeof, elementType); il.Emit(OpCodes.Mul); - m = networkStreamType.GetMethod("ReadBytes", 2); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("ReadBytes", 2))); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Stloc, varStart + 2); @@ -1240,12 +1397,12 @@ namespace Flax.Build.Plugins // Deserialize item value Instruction jmp4 = il.Create(OpCodes.Nop); - il.Append(jmp4); - GenerateSerialization(ref context, type, serialize, elementType, il, new DotnetValueContext(varStart + 2)); + il.Emit(jmp4); + GenerateDotnetSerialization(ref context, serialize, ref il, new DotnetValueContext(valueContext.Type, varStart + 2, elementType)); // array[idx] = element il.Emit(OpCodes.Nop); - valueContext.Load(il); + valueContext.Load(ref il); il.Emit(OpCodes.Ldloc, varStart + 1); // idx il.Emit(OpCodes.Ldloc, varStart + 2); // il.Emit(OpCodes.Stelem_Ref); @@ -1258,82 +1415,87 @@ namespace Flax.Build.Plugins il.Emit(OpCodes.Stloc, varStart + 1); // idx // idx < length - il.Append(jmp3); + il.Emit(jmp3); il.Emit(OpCodes.Ldloc, varStart + 1); // idx il.Emit(OpCodes.Ldloc, varStart + 0); // length il.Emit(OpCodes.Clt); il.Emit(OpCodes.Brtrue_S, jmp4); } - valueContext.SetProperty(il); + valueContext.SetProperty(ref il); } } - else if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer)) + else if (_inBuildSerializers.TryGetValue(valueContext.ValueType.FullName, out var serializer)) { // Call NetworkStream method to write/read data - MethodDefinition m; if (serialize) { - il.Emit(OpCodes.Ldarg_1); - valueContext.Load(il); - m = networkStreamType.GetMethod(serializer.WriteMethod); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc, il.StreamLocalIndex); + else + il.Emit(OpCodes.Ldarg_1); + valueContext.Load(ref il); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod(serializer.WriteMethod))); } else { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - m = networkStreamType.GetMethod(serializer.ReadMethod); - } - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); - if (!serialize) - { - valueContext.Store(il); + if (il.IsRPC) + { + il.Emit(OpCodes.Ldloc_1); + } + else + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + } + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod(serializer.ReadMethod))); + valueContext.Store(ref il); } } - else if (valueType.IsScriptingObject()) + else if (valueContext.ValueType.IsScriptingObject()) { // Replicate ScriptingObject as Guid ID module.GetType("System.Guid", out var guidType); module.GetType("FlaxEngine.Object", out var scriptingObjectType); if (serialize) { - il.Emit(OpCodes.Ldarg_1); - valueContext.Load(il); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc, il.StreamLocalIndex); + else + il.Emit(OpCodes.Ldarg_1); + valueContext.Load(ref il); il.Emit(OpCodes.Dup); Instruction jmp1 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Brtrue_S, jmp1); il.Emit(OpCodes.Pop); - var guidEmpty = guidType.Resolve().GetField("Empty"); - il.Emit(OpCodes.Ldsfld, module.ImportReference(guidEmpty)); + il.Emit(OpCodes.Ldsfld, module.ImportReference(guidType.Resolve().GetField("Empty"))); Instruction jmp2 = il.Create(OpCodes.Nop); il.Emit(OpCodes.Br_S, jmp2); - il.Append(jmp1); - var getID = scriptingObjectType.Resolve().GetMethod("get_ID"); - il.Emit(OpCodes.Call, module.ImportReference(getID)); - il.Append(jmp2); - var writeGuid = networkStreamType.GetMethod("WriteGuid"); - il.Emit(OpCodes.Callvirt, module.ImportReference(writeGuid)); + il.Emit(jmp1); + il.Emit(OpCodes.Call, module.ImportReference(scriptingObjectType.Resolve().GetMethod("get_ID"))); + il.Emit(jmp2); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteGuid"))); } else { - var varStart = il.Body.Variables.Count; + var varStart = il.Variables.Count; var reference = module.ImportReference(guidType); reference.IsValueType = true; // Fix locals init to have valuetype for Guid instead of class - il.Body.Variables.Add(new VariableDefinition(reference)); - il.Body.InitLocals = true; - var m = networkStreamType.GetMethod("ReadGuid"); + il.Variables.Add(new VariableDefinition(reference)); + il.InitLocals = true; module.GetType("System.Type", out var typeType); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc_1); + else + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("ReadGuid"))); il.Emit(OpCodes.Stloc_S, (byte)varStart); il.Emit(OpCodes.Ldloca_S, (byte)varStart); - il.Emit(OpCodes.Ldtoken, valueType); - var getTypeFromHandle = typeType.Resolve().GetMethod("GetTypeFromHandle"); - il.Emit(OpCodes.Call, module.ImportReference(getTypeFromHandle)); - var tryFind = scriptingObjectType.Resolve().GetMethod("TryFind", 2); - il.Emit(OpCodes.Call, module.ImportReference(tryFind)); - il.Emit(OpCodes.Castclass, valueType); - valueContext.Store(il); + il.Emit(OpCodes.Ldtoken, valueContext.ValueType); + il.Emit(OpCodes.Call, module.ImportReference(typeType.Resolve().GetMethod("GetTypeFromHandle"))); + il.Emit(OpCodes.Call, module.ImportReference(scriptingObjectType.Resolve().GetMethod("TryFind", 2))); + il.Emit(OpCodes.Castclass, valueContext.ValueType); + valueContext.Store(ref il); } } else if (valueTypeDef.IsEnum) @@ -1342,45 +1504,71 @@ namespace Flax.Build.Plugins // TODO: use smaller uint depending on enum values range if (serialize) { - il.Emit(OpCodes.Ldarg_1); - valueContext.Load(il); - var m = networkStreamType.GetMethod("WriteUInt32"); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); + if (il.IsRPC) + il.Emit(OpCodes.Ldloc, il.StreamLocalIndex); + else + il.Emit(OpCodes.Ldarg_1); + valueContext.Load(ref il); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteUInt32"))); } else { - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - var m = networkStreamType.GetMethod("ReadUInt32"); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); - valueContext.Store(il); + if (il.IsRPC) + { + il.Emit(OpCodes.Ldloc_1); + } + else + { + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + } + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("ReadUInt32"))); + valueContext.Store(ref il); } } - else if (valueType.IsValueType) + else if (valueContext.ValueType.IsValueType) { // Invoke structure generated serializer - valueContext.LoadAddress(il); - il.Emit(OpCodes.Ldarg_1); - var m = valueTypeDef.GetMethod(serialize ? Thunk1 : Thunk2); - il.Emit(OpCodes.Call, module.ImportReference(m)); + valueContext.LoadAddress(ref il); + if (il.IsRPC) + { + if (serialize) + { + il.Emit(OpCodes.Ldloc, il.StreamLocalIndex); + } + else + { + il.Emit(OpCodes.Initobj, valueContext.ValueType); + valueContext.LoadAddress(ref il); + il.Emit(OpCodes.Ldloc_1); + } + } + else + { + il.Emit(OpCodes.Ldarg_1); + } + il.Emit(OpCodes.Call, module.ImportReference(valueTypeDef.GetMethod(serialize ? Thunk1 : Thunk2))); } else { // Unknown type if (valueContext.Property != null) - MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {valueContext.Property.Name} in {type.FullName} for automatic replication.", valueContext.Property); + MonoCecil.CompilationError($"Not supported type '{valueContext.ValueType.FullName}' on {valueContext.Property.Name} in {valueContext.Type.FullName} for automatic replication.", valueContext.Property); else if (valueContext.Field != null) - MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {valueContext.Field.Name} in {type.FullName} for automatic replication.", valueContext.Field.Resolve()); + MonoCecil.CompilationError($"Not supported type '{valueContext.ValueType.FullName}' on {valueContext.Field.Name} in {valueContext.Type.FullName} for automatic replication.", valueContext.Field.Resolve()); + else if (il.IsRPC) + MonoCecil.CompilationError($"Not supported parameter type '{valueContext.ValueType.FullName}' on RPC method {il.RPC.Name} in {valueContext.Type.FullName} for automatic replication.", il.RPC); else - MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' for automatic replication."); + MonoCecil.CompilationError($"Not supported type '{valueContext.ValueType.FullName}' for automatic replication."); context.Failed = true; } } - private static void GenerateDotNetRPCSerializerType(ref DotnetContext context, TypeDefinition type, bool serialize, int localIndex, TypeReference valueType, ILProcessor il, TypeDefinition networkStreamType, int streamLocalIndex, Instruction ilStart) + private static void GenerateDotNetRPCSerializerType(ref DotnetContext context, TypeDefinition type, int localIndex, TypeReference valueType, ref DotnetIlContext il, TypeDefinition networkStreamType, int streamLocalIndex) { ModuleDefinition module = type.Module; TypeDefinition valueTypeDef = valueType.Resolve(); + bool serialize = true; // Ensure to have valid serialization already generated for that value type GenerateTypeSerialization(ref context, valueTypeDef); @@ -1388,23 +1576,17 @@ namespace Flax.Build.Plugins if (valueType.IsArray) { // TODO: refactor network stream read/write to share code between replication and rpcs - Log.Error($"Not supported type '{valueType.FullName}' for RPC parameter in {type.FullName}."); - context.Failed = true; + //Log.Error($"Not supported type '{valueType.FullName}' for RPC parameter in {type.FullName}."); + //context.Failed = true; } else if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer)) { // Call NetworkStream method to write/read data if (serialize) { - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, localIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod(serializer.WriteMethod)))); - } - else - { - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod(serializer.ReadMethod))); - il.Emit(OpCodes.Stloc, localIndex); + il.Emit(OpCodes.Ldloc, streamLocalIndex); + il.Emit(OpCodes.Ldarg, localIndex); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod(serializer.WriteMethod))); } } else if (valueType.IsScriptingObject()) @@ -1414,43 +1596,19 @@ namespace Flax.Build.Plugins module.GetType("FlaxEngine.Object", out var scriptingObjectType); if (serialize) { - il.InsertBefore(ilStart, il.Create(OpCodes.Nop)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, localIndex)); + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldloc, streamLocalIndex); + il.Emit(OpCodes.Ldarg, localIndex); Instruction jmp1 = il.Create(OpCodes.Nop); - il.InsertBefore(ilStart, il.Create(OpCodes.Brtrue_S, jmp1)); - var guidEmpty = guidType.Resolve().GetField("Empty"); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldsfld, module.ImportReference(guidEmpty))); + il.Emit(OpCodes.Brtrue_S, jmp1); + il.Emit(OpCodes.Ldsfld, module.ImportReference(guidType.Resolve().GetField("Empty"))); Instruction jmp2 = il.Create(OpCodes.Nop); - il.InsertBefore(ilStart, il.Create(OpCodes.Br_S, jmp2)); - il.InsertBefore(ilStart, jmp1); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, localIndex)); - var getID = scriptingObjectType.Resolve().GetMethod("get_ID"); - il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(getID))); - il.InsertBefore(ilStart, jmp2); - var writeGuid = networkStreamType.GetMethod("WriteGuid"); - il.InsertBefore(ilStart, il.Create(OpCodes.Callvirt, module.ImportReference(writeGuid))); - } - else - { - var varStart = il.Body.Variables.Count; - var reference = module.ImportReference(guidType); - reference.IsValueType = true; // Fix locals init to have valuetype for Guid instead of class - il.Body.Variables.Add(new VariableDefinition(reference)); - il.Body.InitLocals = true; - var m = networkStreamType.GetMethod("ReadGuid"); - module.GetType("System.Type", out var typeType); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); - il.Emit(OpCodes.Stloc_S, (byte)varStart); - il.Emit(OpCodes.Ldloca_S, (byte)varStart); - il.Emit(OpCodes.Ldtoken, valueType); - var getTypeFromHandle = typeType.Resolve().GetMethod("GetTypeFromHandle"); - il.Emit(OpCodes.Call, module.ImportReference(getTypeFromHandle)); - var tryFind = scriptingObjectType.Resolve().GetMethod("TryFind", 2); - il.Emit(OpCodes.Call, module.ImportReference(tryFind)); - il.Emit(OpCodes.Castclass, valueType); - il.Emit(OpCodes.Stloc, localIndex); + il.Emit(OpCodes.Br_S, jmp2); + il.Emit( jmp1); + il.Emit(OpCodes.Ldarg, localIndex); + il.Emit(OpCodes.Call, module.ImportReference(scriptingObjectType.Resolve().GetMethod("get_ID"))); + il.Emit( jmp2); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteGuid"))); } } else if (valueTypeDef.IsEnum) @@ -1459,16 +1617,9 @@ namespace Flax.Build.Plugins // TODO: use smaller uint depending on enum values range if (serialize) { - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, localIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteUInt32")))); - } - else - { - il.Emit(OpCodes.Ldloc_1); - var m = networkStreamType.GetMethod("ReadUInt32"); - il.Emit(OpCodes.Callvirt, module.ImportReference(m)); - il.Emit(OpCodes.Stloc, localIndex); + il.Emit(OpCodes.Ldloc, streamLocalIndex); + il.Emit(OpCodes.Ldarg, localIndex); + il.Emit(OpCodes.Callvirt, module.ImportReference(networkStreamType.GetMethod("WriteUInt32"))); } } else if (valueType.IsValueType) @@ -1476,18 +1627,10 @@ namespace Flax.Build.Plugins // Invoke structure generated serializer if (serialize) { - il.InsertBefore(ilStart, il.Create(OpCodes.Nop)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarga, localIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(valueTypeDef.GetMethod(Thunk1)))); - } - else - { - il.Emit(OpCodes.Ldloca_S, (byte)localIndex); - il.Emit(OpCodes.Initobj, valueType); - il.Emit(OpCodes.Ldloca_S, (byte)localIndex); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Call, module.ImportReference(valueTypeDef.GetMethod(Thunk2))); + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldarga, localIndex); + il.Emit(OpCodes.Ldloc, streamLocalIndex); + il.Emit( OpCodes.Call, module.ImportReference(valueTypeDef.GetMethod(Thunk1))); } } else @@ -1569,26 +1712,27 @@ namespace Flax.Build.Plugins var m = new MethodDefinition(method.Name + "_Execute", MethodAttributes.Static | MethodAttributes.Assembly | MethodAttributes.HideBySig, voidType); m.Parameters.Add(new ParameterDefinition("instancePtr", ParameterAttributes.None, intPtrType)); m.Parameters.Add(new ParameterDefinition("streamPtr", ParameterAttributes.None, module.ImportReference(intPtrType))); - ILProcessor il = m.Body.GetILProcessor(); + ILProcessor ilp = m.Body.GetILProcessor(); + var il = new DotnetIlContext(ilp, method); il.Emit(OpCodes.Nop); - il.Body.InitLocals = true; + il.InitLocals = true; // instance = ()FlaxEngine.Object.FromUnmanagedPtr(instancePtr) - il.Body.Variables.Add(new VariableDefinition(type)); + il.Variables.Add(new VariableDefinition(type)); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr)); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Stloc_0); // NetworkStream stream = (NetworkStream)FlaxEngine.Object.FromUnmanagedPtr(streamPtr) - il.Body.Variables.Add(new VariableDefinition(networkStream)); + il.Variables.Add(new VariableDefinition(networkStream)); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr)); il.Emit(OpCodes.Castclass, networkStream); il.Emit(OpCodes.Stloc_1); // Add locals for each RPC parameter - var argsStart = il.Body.Variables.Count; + var argsStart = il.Variables.Count; for (int i = 0; i < method.Parameters.Count; i++) { var parameter = method.Parameters[i]; @@ -1600,7 +1744,7 @@ namespace Flax.Build.Plugins } var parameterType = parameter.ParameterType; - il.Body.Variables.Add(new VariableDefinition(parameterType)); + il.Variables.Add(new VariableDefinition(parameterType)); } // Deserialize parameters from the stream @@ -1624,7 +1768,7 @@ namespace Flax.Build.Plugins continue; } - GenerateDotNetRPCSerializerType(ref context, type, false, argsStart + i, parameterType, il, networkStream.Resolve(), 1, null); + GenerateDotnetSerialization(ref context, false, ref il, new DotnetValueContext(type, argsStart + i, parameterType)); } // Call RPC method body @@ -1644,59 +1788,61 @@ namespace Flax.Build.Plugins // Inject custom code before RPC method body to invoke it { - ILProcessor il = method.Body.GetILProcessor(); - Instruction ilStart = il.Body.Instructions[0]; + ILProcessor ilp = method.Body.GetILProcessor(); + Instruction ilStart = ilp.Body.Instructions[0]; + var il = new DotnetIlContext(ilp, method, ilStart); module.GetType("System.Boolean", out var boolType); module.GetType("FlaxEngine.Networking.NetworkManagerMode", out var networkManagerModeType); module.GetType("FlaxEngine.Networking.NetworkManager", out var networkManagerType); var networkManagerGetMode = networkManagerType.Resolve().GetMethod("get_Mode", 0); - il.Body.InitLocals = true; - var varsStart = il.Body.Variables.Count; + il.InitLocals = true; + var varsStart = il.Variables.Count; - il.InsertBefore(ilStart, il.Create(OpCodes.Nop)); + il.Emit(OpCodes.Nop); // Is Server/Is Client boolean constants - il.Body.Variables.Add(new VariableDefinition(module.ImportReference(boolType))); // [0] - il.Body.Variables.Add(new VariableDefinition(module.ImportReference(boolType))); // [1] - il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4, methodRPC.IsServer ? 1 : 0)); - il.InsertBefore(ilStart, il.Create(OpCodes.Stloc, varsStart + 0)); // isServer loc=0 - il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4, methodRPC.IsClient ? 1 : 0)); - il.InsertBefore(ilStart, il.Create(OpCodes.Stloc, varsStart + 1)); // isClient loc=1 + il.Variables.Add(new VariableDefinition(module.ImportReference(boolType))); // [0] + il.Variables.Add(new VariableDefinition(module.ImportReference(boolType))); // [1] + il.Emit(OpCodes.Ldc_I4, methodRPC.IsServer ? 1 : 0); + il.Emit(OpCodes.Stloc, varsStart + 0); // isServer loc=0 + il.Emit(OpCodes.Ldc_I4, methodRPC.IsClient ? 1 : 0); + il.Emit(OpCodes.Stloc, varsStart + 1); // isClient loc=1 // NetworkManagerMode mode = NetworkManager.Mode; - il.Body.Variables.Add(new VariableDefinition(module.ImportReference(networkManagerModeType))); // [2] - il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(networkManagerGetMode))); - il.InsertBefore(ilStart, il.Create(OpCodes.Stloc, varsStart + 2)); // mode loc=2 + il.Variables.Add(new VariableDefinition(module.ImportReference(networkManagerModeType))); // [2] + il.Emit(OpCodes.Call, module.ImportReference(networkManagerGetMode)); + il.Emit(OpCodes.Stloc, varsStart + 2); // mode loc=2 // if ((server && networkMode == NetworkManagerMode.Client) || (client && networkMode != NetworkManagerMode.Client)) - var jumpIfBodyStart = il.Create(OpCodes.Nop); // if block body - var jumpIf2Start = il.Create(OpCodes.Nop); // 2nd part of the if - var jumpBodyStart = il.Create(OpCodes.Nop); // original method body start - var jumpBodyEnd = il.Body.Instructions.Last(x => x.OpCode == OpCodes.Ret && x.Previous != null); + var jumpIfBodyStart = ilp.Create(OpCodes.Nop); // if block body + var jumpIf2Start = ilp.Create(OpCodes.Nop); // 2nd part of the if + var jumpBodyStart = ilp.Create(OpCodes.Nop); // original method body start + var jumpBodyEnd = ilp.Body.Instructions.Last(x => x.OpCode == OpCodes.Ret && x.Previous != null); if (jumpBodyEnd == null) throw new Exception("Missing IL Return op code in method " + method.Name); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 0)); - il.InsertBefore(ilStart, il.Create(OpCodes.Brfalse_S, jumpIf2Start)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_2)); - il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, jumpIfBodyStart)); + il.Emit(OpCodes.Ldloc, varsStart + 0); + il.Emit(OpCodes.Brfalse_S, jumpIf2Start); + il.Emit(OpCodes.Ldloc, varsStart + 2); + il.Emit(OpCodes.Ldc_I4_2); + il.Emit(OpCodes.Beq_S, jumpIfBodyStart); // || - il.InsertBefore(ilStart, jumpIf2Start); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 1)); - il.InsertBefore(ilStart, il.Create(OpCodes.Brfalse, jumpBodyStart)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_2)); - il.InsertBefore(ilStart, il.Create(OpCodes.Beq, jumpBodyStart)); + il.Emit(jumpIf2Start); + il.Emit(OpCodes.Ldloc, varsStart + 1); + il.Emit(OpCodes.Brfalse, jumpBodyStart); + il.Emit(OpCodes.Ldloc, varsStart + 2); + il.Emit(OpCodes.Ldc_I4_2); + il.Emit(OpCodes.Beq, jumpBodyStart); // { - il.InsertBefore(ilStart, jumpIfBodyStart); + il.Emit(jumpIfBodyStart); // NetworkStream stream = NetworkReplicator.BeginInvokeRPC(); - il.Body.Variables.Add(new VariableDefinition(module.ImportReference(networkStream))); // [3] + il.Variables.Add(new VariableDefinition(module.ImportReference(networkStream))); // [3] var streamLocalIndex = varsStart + 3; + il.StreamLocalIndex = streamLocalIndex; module.GetType("FlaxEngine.Networking.NetworkReplicator", out var networkReplicatorType); var beginInvokeRPC = networkReplicatorType.Resolve().GetMethod("BeginInvokeRPC", 0); - il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(beginInvokeRPC))); - il.InsertBefore(ilStart, il.Create(OpCodes.Stloc, streamLocalIndex)); // stream loc=3 + il.Emit(OpCodes.Call, module.ImportReference(beginInvokeRPC)); + il.Emit(OpCodes.Stloc, streamLocalIndex); // stream loc=3 // Serialize all RPC parameters var targetIdsArgIndex = -1; @@ -1714,58 +1860,58 @@ namespace Flax.Build.Plugins continue; } - GenerateDotNetRPCSerializerType(ref context, type, true, i + 1, parameterType, il, networkStream.Resolve(), streamLocalIndex, ilStart); + GenerateDotnetSerialization(ref context, true, ref il, new DotnetValueContext(type, -1, parameterType) { ArgIndex = i + 1 }); } // NetworkReplicator.EndInvokeRPC(this, typeof(), "", stream, targetIds); - il.InsertBefore(ilStart, il.Create(OpCodes.Nop)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg_0)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldtoken, type)); + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldtoken, type); module.GetType("System.Type", out var typeType); var getTypeFromHandle = typeType.Resolve().GetMethod("GetTypeFromHandle"); - il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(getTypeFromHandle))); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldstr, method.Name)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, streamLocalIndex)); + il.Emit(OpCodes.Call, module.ImportReference(getTypeFromHandle)); + il.Emit(OpCodes.Ldstr, method.Name); + il.Emit(OpCodes.Ldloc, streamLocalIndex); if (targetIdsArgIndex != -1) { - il.InsertBefore(ilStart, il.Create(OpCodes.Ldarg, targetIdsArgIndex)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldfld, module.ImportReference(targetIdsField))); + il.Emit(OpCodes.Ldarg, targetIdsArgIndex); + il.Emit(OpCodes.Ldfld, module.ImportReference(targetIdsField)); } else - il.InsertBefore(ilStart, il.Create(OpCodes.Ldnull)); + il.Emit(OpCodes.Ldnull); var endInvokeRPC = networkReplicatorType.Resolve().GetMethod("EndInvokeRPC", 5); - il.InsertBefore(ilStart, il.Create(OpCodes.Call, module.ImportReference(endInvokeRPC))); + il.Emit(OpCodes.Call, module.ImportReference(endInvokeRPC)); // if (server && networkMode == NetworkManagerMode.Client) return; if (methodRPC.IsServer) { - il.InsertBefore(ilStart, il.Create(OpCodes.Nop)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_2)); - var tmp = il.Create(OpCodes.Nop); - il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, tmp)); - il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyStart)); - il.InsertBefore(ilStart, tmp); - //il.InsertBefore(ilStart, il.Create(OpCodes.Ret)); - il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd)); + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldloc, varsStart + 2); + il.Emit(OpCodes.Ldc_I4_2); + var tmp = ilp.Create(OpCodes.Nop); + il.Emit(OpCodes.Beq_S, tmp); + il.Emit(OpCodes.Br, jumpBodyStart); + il.Emit(tmp); + //il.Emit(OpCodes.Ret); + il.Emit(OpCodes.Br, jumpBodyEnd); } // if (client && networkMode == NetworkManagerMode.Server) return; if (methodRPC.IsClient) { - il.InsertBefore(ilStart, il.Create(OpCodes.Nop)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldloc, varsStart + 2)); - il.InsertBefore(ilStart, il.Create(OpCodes.Ldc_I4_1)); - var tmp = il.Create(OpCodes.Nop); - il.InsertBefore(ilStart, il.Create(OpCodes.Beq_S, tmp)); - il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyStart)); - il.InsertBefore(ilStart, tmp); - //il.InsertBefore(ilStart, il.Create(OpCodes.Ret)); - il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd)); + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldloc, varsStart + 2); + il.Emit(OpCodes.Ldc_I4_1); + var tmp = ilp.Create(OpCodes.Nop); + il.Emit(OpCodes.Beq_S, tmp); + il.Emit(OpCodes.Br, jumpBodyStart); + il.Emit(tmp); + //il.Emit(OpCodes.Ret); + il.Emit(OpCodes.Br, jumpBodyEnd); } // Continue to original method body - il.InsertBefore(ilStart, jumpBodyStart); + il.Emit(jumpBodyStart); } context.MethodRPCs.Add(methodRPC);