Refactor C# network replication codegen to be more extendable based on value context source
This commit is contained in:
@@ -347,14 +347,14 @@ namespace Flax.Build.Plugins
|
||||
contents.AppendLine();
|
||||
}
|
||||
|
||||
private bool IsRawPOD(Builder.BuildData buildData, ApiTypeInfo type)
|
||||
private static bool IsRawPOD(Builder.BuildData buildData, ApiTypeInfo type)
|
||||
{
|
||||
// TODO: what if type fields have custom replication settings (eg. compression)?
|
||||
type.EnsureInited(buildData);
|
||||
return type.IsPod;
|
||||
}
|
||||
|
||||
private bool IsRawPOD(Builder.BuildData buildData, ApiTypeInfo caller, ApiTypeInfo apiType, TypeInfo type)
|
||||
private static bool IsRawPOD(Builder.BuildData buildData, ApiTypeInfo caller, ApiTypeInfo apiType, TypeInfo type)
|
||||
{
|
||||
if (type.IsPod(buildData, caller))
|
||||
{
|
||||
@@ -365,6 +365,12 @@ namespace Flax.Build.Plugins
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsRawPOD(TypeReference type)
|
||||
{
|
||||
// TODO:
|
||||
return type.IsValueType;
|
||||
}
|
||||
|
||||
private void OnGenerateCppTypeSerializeData(Builder.BuildData buildData, ApiTypeInfo caller, StringBuilder contents, TypeInfo type, string name, bool serialize)
|
||||
{
|
||||
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, type, caller);
|
||||
@@ -789,7 +795,7 @@ namespace Flax.Build.Plugins
|
||||
{
|
||||
if (!f.HasAttribute(NetworkReplicatedAttribute))
|
||||
continue;
|
||||
GenerateSerializerType(ref context, type, serialize, f, null, f.FieldType, il);
|
||||
GenerateSerializerType(ref context, type, serialize, f.FieldType, il, new DotnetValueContext(f));
|
||||
}
|
||||
|
||||
// Serialize all type properties marked with NetworkReplicated attribute
|
||||
@@ -797,7 +803,7 @@ namespace Flax.Build.Plugins
|
||||
{
|
||||
if (!p.HasAttribute(NetworkReplicatedAttribute))
|
||||
continue;
|
||||
GenerateSerializerType(ref context, type, serialize, null, p, p.PropertyType, il);
|
||||
GenerateSerializerType(ref context, type, serialize, p.PropertyType, il, new DotnetValueContext(p));
|
||||
}
|
||||
|
||||
if (serialize)
|
||||
@@ -877,53 +883,346 @@ namespace Flax.Build.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateSerializerType(ref DotnetContext context, TypeDefinition type, bool serialize, FieldReference field, PropertyDefinition property, TypeReference valueType, ILProcessor il)
|
||||
private struct DotnetValueContext
|
||||
{
|
||||
if (field == null && property == null)
|
||||
throw new ArgumentException();
|
||||
TypeDefinition networkStreamType = context.NetworkStreamType.Resolve();
|
||||
var propertyGetOpCode = OpCodes.Call;
|
||||
var propertySetOpCode = OpCodes.Call;
|
||||
if (property != null)
|
||||
public FieldReference Field;
|
||||
public PropertyDefinition Property;
|
||||
public int LocalVarIndex;
|
||||
|
||||
public OpCode PropertyGetOpCode
|
||||
{
|
||||
if (property.GetMethod == null)
|
||||
get
|
||||
{
|
||||
MonoCecil.CompilationError($"Missing getter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
||||
var propertyGetOpCode = OpCodes.Call;
|
||||
if (Property != null && Property.GetMethod.IsVirtual)
|
||||
propertyGetOpCode = OpCodes.Callvirt;
|
||||
return propertyGetOpCode;
|
||||
}
|
||||
}
|
||||
|
||||
public OpCode PropertySetOpCode
|
||||
{
|
||||
get
|
||||
{
|
||||
var propertyGetOpCode = OpCodes.Call;
|
||||
if (Property != null && Property.GetMethod.IsVirtual)
|
||||
propertyGetOpCode = OpCodes.Callvirt;
|
||||
return propertyGetOpCode;
|
||||
}
|
||||
}
|
||||
|
||||
public DotnetValueContext(FieldDefinition field)
|
||||
{
|
||||
Field = field;
|
||||
Property = null;
|
||||
LocalVarIndex = -1;
|
||||
}
|
||||
|
||||
public DotnetValueContext(PropertyDefinition property)
|
||||
{
|
||||
Field = null;
|
||||
Property = property;
|
||||
LocalVarIndex = -1;
|
||||
}
|
||||
|
||||
public void GetProperty(ILProcessor il, int propertyVar)
|
||||
{
|
||||
if (Property != null)
|
||||
{
|
||||
// <elementType>[] array = ArrayProperty;
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(PropertyGetOpCode, Property.GetMethod);
|
||||
il.Emit(OpCodes.Stloc, propertyVar);
|
||||
LocalVarIndex = propertyVar;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetProperty(ILProcessor il)
|
||||
{
|
||||
if (Property != null)
|
||||
{
|
||||
// ArrayProperty = array
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldloc, LocalVarIndex);
|
||||
il.Emit(PropertySetOpCode, Property.SetMethod);
|
||||
}
|
||||
}
|
||||
|
||||
public void Load(ILProcessor il)
|
||||
{
|
||||
if (Field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, Field);
|
||||
}
|
||||
else if (Property != null && LocalVarIndex == -1)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(PropertyGetOpCode, Property.GetMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloc, LocalVarIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadAddress(ILProcessor il)
|
||||
{
|
||||
if (Field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldflda, Field);
|
||||
}
|
||||
else if (Property != null && LocalVarIndex == -1)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(PropertyGetOpCode, Property.GetMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloca_S, (byte)LocalVarIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Store(ILProcessor il)
|
||||
{
|
||||
if (Field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Stfld, Field);
|
||||
}
|
||||
else if (Property != null)
|
||||
{
|
||||
il.Emit(PropertySetOpCode, Property.SetMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException("TODO: storing local variable value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateSerializerType(ref DotnetContext context, TypeDefinition type, bool serialize, TypeReference valueType, ILProcessor il, DotnetValueContext valueContext)
|
||||
{
|
||||
if (valueContext.Property != null)
|
||||
{
|
||||
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 (property.SetMethod == null)
|
||||
if (valueContext.Property.SetMethod == null)
|
||||
{
|
||||
MonoCecil.CompilationError($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
|
||||
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;
|
||||
}
|
||||
|
||||
if (property.GetMethod.IsVirtual)
|
||||
propertyGetOpCode = OpCodes.Callvirt;
|
||||
if (property.SetMethod.IsVirtual)
|
||||
propertySetOpCode = OpCodes.Callvirt;
|
||||
}
|
||||
|
||||
ModuleDefinition module = type.Module;
|
||||
TypeDefinition valueTypeDef = 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 (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
||||
if (valueType.IsArray)
|
||||
{
|
||||
var elementType = valueType.GetElementType();
|
||||
var isRawPod = IsRawPOD(elementType); // Whether to use raw memory copy (eg. int, enum, Vector2)
|
||||
var varStart = il.Body.Variables.Count;
|
||||
module.GetType("System.Int32", out var intType);
|
||||
il.Body.Variables.Add(new VariableDefinition(intType)); // [0] int length
|
||||
if (isRawPod)
|
||||
{
|
||||
il.Body.Variables.Add(new VariableDefinition(new PointerType(elementType))); // [1] <elementType>*
|
||||
il.Body.Variables.Add(new VariableDefinition(new PinnedType(valueType))); // [2] <elementType>[] pinned
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Body.Variables.Add(new VariableDefinition(intType)); // [1] int idx
|
||||
il.Body.Variables.Add(new VariableDefinition(elementType)); // [2] <elementType>
|
||||
}
|
||||
if (valueContext.Property != null)
|
||||
il.Body.Variables.Add(new VariableDefinition(valueType)); // [3] <elementType>[]
|
||||
il.Body.InitLocals = true;
|
||||
valueContext.GetProperty(il, varStart + 3);
|
||||
if (serialize)
|
||||
{
|
||||
// <elementType>[] array = Array;
|
||||
il.Emit(OpCodes.Nop);
|
||||
valueContext.Load(il);
|
||||
|
||||
// int length = ((array != null) ? array.Length : 0);
|
||||
il.Emit(OpCodes.Dup);
|
||||
Instruction jmp1 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp1);
|
||||
il.Emit(OpCodes.Pop);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
Instruction jmp2 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp2);
|
||||
il.Append(jmp1);
|
||||
il.Emit(OpCodes.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
il.Append(jmp2);
|
||||
il.Emit(OpCodes.Stloc, varStart + 0);
|
||||
|
||||
// stream.WriteInt32(length);
|
||||
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.Nop);
|
||||
if (isRawPod)
|
||||
{
|
||||
// fixed (<elementType>* bytes2 = Array)
|
||||
valueContext.Load(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.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
Instruction jmp4 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp4);
|
||||
il.Append(jmp3);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Conv_U);
|
||||
il.Emit(OpCodes.Stloc, varStart + 1);
|
||||
Instruction jmp5 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp5);
|
||||
|
||||
// stream.WriteBytes((byte*)bytes, length * sizeof(<elementType>)));
|
||||
il.Append(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(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.Nop);
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// int idx = 0
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Stloc, varStart + 1);
|
||||
Instruction jmp3 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp3);
|
||||
|
||||
// <elementType> element = array[idx];
|
||||
Instruction jmp4 = il.Create(OpCodes.Nop);
|
||||
il.Append(jmp4);
|
||||
valueContext.Load(il);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 1); // idx
|
||||
il.Emit(OpCodes.Ldelem_Ref);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2); // <elementType>
|
||||
|
||||
// TODO: serialize element type from [varStart + 2]
|
||||
|
||||
il.Emit(OpCodes.Nop);
|
||||
|
||||
// idx++
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 1); // idx
|
||||
il.Emit(OpCodes.Ldc_I4_1);
|
||||
il.Emit(OpCodes.Add);
|
||||
il.Emit(OpCodes.Stloc, varStart + 1); // idx
|
||||
|
||||
// idx < num
|
||||
il.Append(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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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));
|
||||
il.Emit(OpCodes.Stloc, varStart + 0);
|
||||
|
||||
// System.Array.Resize(ref Array, length);
|
||||
valueContext.LoadAddress(il);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 0);
|
||||
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.Nop);
|
||||
if (isRawPod)
|
||||
{
|
||||
// fixed (<elementType>* buffer = Array)
|
||||
valueContext.Load(il);
|
||||
il.Emit(OpCodes.Dup);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2);
|
||||
Instruction jmp1 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brfalse_S, jmp1);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 2);
|
||||
il.Emit(OpCodes.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
Instruction jmp2 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp2);
|
||||
il.Append(jmp1);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Conv_U);
|
||||
il.Emit(OpCodes.Stloc, varStart + 1);
|
||||
Instruction jmp3 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp3);
|
||||
|
||||
// stream.ReadBytes((byte*)buffer, num * sizeof(<elementType>));
|
||||
il.Append(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);
|
||||
il.Append(jmp3);
|
||||
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("ReadBytes", 2);
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: deserialize item-by-item
|
||||
}
|
||||
|
||||
valueContext.SetProperty(il);
|
||||
}
|
||||
}
|
||||
else if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
||||
{
|
||||
// Call NetworkStream method to write/read data
|
||||
MethodDefinition m;
|
||||
if (serialize)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
else
|
||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||
valueContext.Load(il);
|
||||
m = networkStreamType.GetMethod(serializer.WriteMethod);
|
||||
}
|
||||
else
|
||||
@@ -936,10 +1235,7 @@ namespace Flax.Build.Plugins
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
if (!serialize)
|
||||
{
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Stfld, field);
|
||||
else
|
||||
il.Emit(propertySetOpCode, property.SetMethod);
|
||||
valueContext.Store(il);
|
||||
}
|
||||
}
|
||||
else if (valueType.IsScriptingObject())
|
||||
@@ -950,11 +1246,7 @@ namespace Flax.Build.Plugins
|
||||
if (serialize)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
else
|
||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||
valueContext.Load(il);
|
||||
il.Emit(OpCodes.Dup);
|
||||
Instruction jmp1 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp1);
|
||||
@@ -990,10 +1282,7 @@ namespace Flax.Build.Plugins
|
||||
var tryFind = scriptingObjectType.Resolve().GetMethod("TryFind", 2);
|
||||
il.Emit(OpCodes.Call, module.ImportReference(tryFind));
|
||||
il.Emit(OpCodes.Castclass, valueType);
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Stfld, field);
|
||||
else
|
||||
il.Emit(propertySetOpCode, property.SetMethod);
|
||||
valueContext.Store(il);
|
||||
}
|
||||
}
|
||||
else if (valueTypeDef.IsEnum)
|
||||
@@ -1003,11 +1292,7 @@ namespace Flax.Build.Plugins
|
||||
if (serialize)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
else
|
||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||
valueContext.Load(il);
|
||||
var m = networkStreamType.GetMethod("WriteUInt32");
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
}
|
||||
@@ -1017,209 +1302,24 @@ namespace Flax.Build.Plugins
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
var m = networkStreamType.GetMethod("ReadUInt32");
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Stfld, field);
|
||||
else
|
||||
il.Emit(propertySetOpCode, property.SetMethod);
|
||||
valueContext.Store(il);
|
||||
}
|
||||
}
|
||||
else if (valueType.IsValueType)
|
||||
{
|
||||
// Invoke structure generated serializer
|
||||
// TODO: check if this type has generated serialization code
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (field != null)
|
||||
il.Emit(OpCodes.Ldflda, field);
|
||||
else
|
||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||
valueContext.LoadAddress(il);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
var m = valueTypeDef.GetMethod(serialize ? Thunk1 : Thunk2);
|
||||
il.Emit(OpCodes.Call, module.ImportReference(m));
|
||||
}
|
||||
else if (valueType.IsArray && valueType.GetElementType().IsValueType)
|
||||
{
|
||||
// TODO: support any array type by iterating over elements (separate serialize for each one)
|
||||
var elementType = valueType.GetElementType();
|
||||
var varStart = il.Body.Variables.Count;
|
||||
module.GetType("System.Int32", out var intType);
|
||||
il.Body.Variables.Add(new VariableDefinition(intType));
|
||||
il.Body.Variables.Add(new VariableDefinition(new PointerType(elementType)));
|
||||
il.Body.Variables.Add(new VariableDefinition(new PinnedType(valueType)));
|
||||
if (property != null)
|
||||
il.Body.Variables.Add(new VariableDefinition(valueType));
|
||||
il.Body.InitLocals = true;
|
||||
if (serialize)
|
||||
{
|
||||
// <elementType>[] array = Array;
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
// <elementType>[] array = ArrayProperty;
|
||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||
il.Emit(OpCodes.Stloc, varStart + 3);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 3);
|
||||
}
|
||||
|
||||
// int num2 = ((array != null) ? array.Length : 0);
|
||||
il.Emit(OpCodes.Dup);
|
||||
Instruction jmp1 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp1);
|
||||
il.Emit(OpCodes.Pop);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
Instruction jmp2 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp2);
|
||||
il.Append(jmp1);
|
||||
il.Emit(OpCodes.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
il.Append(jmp2);
|
||||
il.Emit(OpCodes.Stloc, varStart + 0);
|
||||
|
||||
// stream.WriteInt32(num2);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 0);
|
||||
var m = networkStreamType.GetMethod("WriteInt32");
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
|
||||
// fixed (<elementType>* bytes2 = Array)
|
||||
il.Emit(OpCodes.Nop);
|
||||
if (field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloc, varStart + 3);
|
||||
}
|
||||
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.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
Instruction jmp4 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp4);
|
||||
il.Append(jmp3);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Conv_U);
|
||||
il.Emit(OpCodes.Stloc, varStart + 1);
|
||||
Instruction jmp5 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp5);
|
||||
|
||||
// stream.WriteBytes((byte*)bytes, num * sizeof(<elementType>)));
|
||||
il.Append(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(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.Nop);
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// int num = stream.ReadInt32();
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ldarg_1);
|
||||
var m = networkStreamType.GetMethod("ReadInt32");
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
il.Emit(OpCodes.Stloc, varStart + 0);
|
||||
|
||||
// System.Array.Resize(ref ArrayField, num);
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
if (field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldflda, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
// <elementType>[] array = ArrayProperty;
|
||||
il.Emit(propertyGetOpCode, property.GetMethod);
|
||||
il.Emit(OpCodes.Stloc, varStart + 3);
|
||||
il.Emit(OpCodes.Ldloca_S, (byte)(varStart + 3));
|
||||
}
|
||||
il.Emit(OpCodes.Ldloc, varStart + 0);
|
||||
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)));
|
||||
|
||||
// fixed (<elementType>* buffer = Array)
|
||||
il.Emit(OpCodes.Nop);
|
||||
if (field != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldfld, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloc, varStart + 3);
|
||||
}
|
||||
il.Emit(OpCodes.Dup);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2);
|
||||
Instruction jmp1 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brfalse_S, jmp1);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 2);
|
||||
il.Emit(OpCodes.Ldlen);
|
||||
il.Emit(OpCodes.Conv_I4);
|
||||
Instruction jmp2 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Brtrue_S, jmp2);
|
||||
il.Append(jmp1);
|
||||
il.Emit(OpCodes.Ldc_I4_0);
|
||||
il.Emit(OpCodes.Conv_U);
|
||||
il.Emit(OpCodes.Stloc, varStart + 1);
|
||||
Instruction jmp3 = il.Create(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Br_S, jmp3);
|
||||
|
||||
// stream.ReadBytes((byte*)buffer, num * sizeof(<elementType>));
|
||||
il.Append(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);
|
||||
il.Append(jmp3);
|
||||
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("ReadBytes", 2);
|
||||
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
|
||||
il.Emit(OpCodes.Nop);
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
il.Emit(OpCodes.Stloc, varStart + 2);
|
||||
if (property != null)
|
||||
{
|
||||
il.Emit(OpCodes.Ldarg_0);
|
||||
il.Emit(OpCodes.Ldloc, varStart + 3);
|
||||
il.Emit(propertySetOpCode, property.SetMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown type
|
||||
if (property != null)
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {property.Name} in {type.FullName} for automatic replication.", property);
|
||||
else if (field != null)
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {field.Name} in {type.FullName} for automatic replication.", field.Resolve());
|
||||
if (valueContext.Property != null)
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' on {valueContext.Property.Name} in {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());
|
||||
else
|
||||
MonoCecil.CompilationError($"Not supported type '{valueType.FullName}' for automatic replication.");
|
||||
context.Failed = true;
|
||||
@@ -1234,7 +1334,13 @@ namespace Flax.Build.Plugins
|
||||
// Ensure to have valid serialization already generated for that value type
|
||||
GenerateTypeSerialization(ref context, valueTypeDef);
|
||||
|
||||
if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
||||
if (type.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;
|
||||
}
|
||||
else if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
|
||||
{
|
||||
// Call NetworkStream method to write/read data
|
||||
if (serialize)
|
||||
|
||||
Reference in New Issue
Block a user