Format code

This commit is contained in:
Wojtek Figat
2023-03-13 20:20:10 +01:00
parent d2a0438b71
commit 6f304040b0

View File

@@ -53,6 +53,7 @@ namespace Flax.Build.Plugins
internal const string NetworkRpc = "NetworkRpc";
private const string Thunk1 = "INetworkSerializable_Serialize";
private const string Thunk2 = "INetworkSerializable_Deserialize";
private static readonly Dictionary<string, InBuildSerializer> _inBuildSerializers = new Dictionary<string, InBuildSerializer>()
{
{ "System.Boolean", new InBuildSerializer("WriteBoolean", "ReadBoolean") },
@@ -127,6 +128,7 @@ namespace Flax.Build.Plugins
{
fields = structInfo.Fields;
}
bool useReplication = false, useRpc = false;
if (fields != null)
{
@@ -139,6 +141,7 @@ namespace Flax.Build.Plugins
}
}
}
if (properties != null)
{
foreach (var propertyInfo in properties)
@@ -150,6 +153,7 @@ namespace Flax.Build.Plugins
}
}
}
if (functions != null)
{
foreach (var functionInfo in functions)
@@ -161,20 +165,22 @@ namespace Flax.Build.Plugins
}
}
}
if (useReplication)
{
typeInfo.SetTag(NetworkReplicated, string.Empty);
// Generate C++ wrapper functions to serialize/deserialize type
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkReplicator.h");
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkStream.h");
OnGenerateCppTypeSerialize(buildData, typeInfo, contents, fields, properties, true);
OnGenerateCppTypeSerialize(buildData, typeInfo, contents, fields, properties, false);
}
if (useRpc)
{
typeInfo.SetTag(NetworkRpc, string.Empty);
// Generate C++ wrapper functions to invoke/execute RPC
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkStream.h");
BindingsGenerator.CppIncludeFiles.Add("Engine/Networking/NetworkReplicator.h");
@@ -282,7 +288,7 @@ namespace Flax.Build.Plugins
// Replicate base type
OnGenerateCppWriteSerializer(contents, classStructInfo.BaseType.NativeName, "obj", serialize);
}
// Replicate all marked fields and properties
if (fields != null)
{
@@ -293,6 +299,7 @@ namespace Flax.Build.Plugins
OnGenerateCppTypeSerializeData(buildData, typeInfo, contents, fieldInfo.Type, $"obj.{fieldInfo.Name}", serialize);
}
}
if (properties != null)
{
foreach (var propertyInfo in properties)
@@ -308,6 +315,7 @@ namespace Flax.Build.Plugins
}
}
}
contents.AppendLine(" }");
contents.AppendLine();
}
@@ -326,9 +334,10 @@ namespace Flax.Build.Plugins
if (apiType != null)
return IsRawPOD(buildData, apiType);
}
return false;
}
private void OnGenerateCppTypeSerializeData(Builder.BuildData buildData, ApiTypeInfo caller, StringBuilder contents, TypeInfo type, string name, bool serialize)
{
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, type, caller);
@@ -365,7 +374,7 @@ namespace Flax.Build.Plugins
throw new Exception($"Invalid pointer type '{type}' that cannot be serialized for replication of {caller.Name}.");
if (type.IsRef)
throw new Exception($"Invalid reference type '{type}' that cannot be serialized for replication of {caller.Name}.");
// Structure serializer
OnGenerateCppWriteSerializer(contents, apiType.NativeName, name, serialize);
}
@@ -375,13 +384,13 @@ namespace Flax.Build.Plugins
OnGenerateCppWriteRaw(contents, name, serialize);
}
}
private void OnGenerateCppWriteRaw(StringBuilder contents, string data, bool serialize)
{
var method = serialize ? "Write" : "Read";
contents.AppendLine($" stream->{method}({data});");
}
private void OnGenerateCppWriteSerializer(StringBuilder contents, string type, string data, bool serialize)
{
if (type == "ScriptingObject" || type == "Script" || type == "Actor")
@@ -405,6 +414,7 @@ namespace Flax.Build.Plugins
// Register generated serializer functions
contents.AppendLine($" NetworkReplicator::AddSerializer(ScriptingTypeHandle({typeNameNative}::TypeInitializer), {typeNameInternal}Internal::INetworkSerializable_Serialize, {typeNameInternal}Internal::INetworkSerializable_Deserialize);");
}
if (rpcTag != null)
{
// Register generated RPCs
@@ -413,6 +423,7 @@ namespace Flax.Build.Plugins
{
functions = classInfo.Functions;
}
if (functions != null)
{
foreach (var functionInfo in functions)
@@ -430,16 +441,15 @@ namespace Flax.Build.Plugins
// Skip types that don't use networking
if (typeInfo.GetTag(NetworkReplicated) == null)
return;
if (typeInfo is ClassInfo)
return;
// Generate C# wrapper functions to serialize/deserialize type directly from managed code
OnGenerateCSharpTypeSerialize(buildData, typeInfo, contents, indent, true);
OnGenerateCSharpTypeSerialize(buildData, typeInfo, contents, indent, false);
}
private void OnGenerateCSharpTypeSerialize(Builder.BuildData buildData, ApiTypeInfo typeInfo, StringBuilder contents, string indent, bool serialize)
{
var mode = serialize ? "true" : "false";
@@ -463,6 +473,7 @@ namespace Flax.Build.Plugins
contents.Append(indent).AppendLine($" throw new NotImplementedException(\"Not supported native structure with references used in managed code for replication.\");");
}
}
contents.Append(indent).AppendLine("}");
}
@@ -493,7 +504,7 @@ namespace Flax.Build.Plugins
private void OnPatchDotNetAssembly(Builder.BuildData buildData, NativeCpp.BuildOptions buildOptions, Task buildTask, string assemblyPath)
{
using (DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver())
using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters{ ReadWrite = true, ReadSymbols = true, AssemblyResolver = assemblyResolver }))
using (AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath, new ReaderParameters { ReadWrite = true, ReadSymbols = true, AssemblyResolver = assemblyResolver }))
{
// Setup module search locations
var searchDirectories = new HashSet<string>();
@@ -503,6 +514,7 @@ namespace Flax.Build.Plugins
if (file.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
searchDirectories.Add(Path.GetDirectoryName(file));
}
foreach (var e in searchDirectories)
assemblyResolver.AddSearchDirectory(e);
@@ -554,6 +566,7 @@ namespace Flax.Build.Plugins
}
}
}
var isNetworkReplicated = false;
foreach (FieldDefinition f in type.Fields)
{
@@ -562,6 +575,7 @@ namespace Flax.Build.Plugins
isNetworkReplicated = true;
break;
}
foreach (PropertyDefinition p in type.Properties)
{
if (!p.HasAttribute(NetworkReplicatedAttribute))
@@ -569,6 +583,7 @@ namespace Flax.Build.Plugins
isNetworkReplicated = true;
break;
}
if (type.IsValueType)
{
if (isINetworkSerializable)
@@ -597,6 +612,7 @@ namespace Flax.Build.Plugins
modified = true;
}
}
if (failed)
throw new Exception($"Failed to generate network replication for assembly {assemblyPath}");
if (!modified)
@@ -650,6 +666,7 @@ namespace Flax.Build.Plugins
il.Emit(OpCodes.Newobj, module.ImportReference(serializeFuncCtor));
il.Emit(OpCodes.Call, module.ImportReference(addSerializer));
}
foreach (var e in methodRPCs)
{
// NetworkReplicator.AddRPC(typeof(<type>), "<name>", <name>_Execute, <isServer>, <isClient>, <channel>);
@@ -664,13 +681,14 @@ namespace Flax.Build.Plugins
il.Emit(OpCodes.Ldc_I4, e.Channel);
il.Emit(OpCodes.Call, module.ImportReference(addRPC));
}
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
c.Methods.Add(m);
}
// Serialize assembly back to the file
assembly.Write(new WriterParameters { WriteSymbols = true } );
assembly.Write(new WriterParameters { WriteSymbols = true });
}
}
@@ -696,7 +714,7 @@ namespace Flax.Build.Plugins
TypeDefinition networkStream = networkStreamType.Resolve();
ILProcessor il = m.Body.GetILProcessor();
il.Emit(OpCodes.Nop);
// Serialize base type
if (type.BaseType != null && type.BaseType.FullName != "System.ValueType" && type.BaseType.FullName != "FlaxEngine.Object" && type.BaseType.CanBeResolved())
{
@@ -747,14 +765,14 @@ namespace Flax.Build.Plugins
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.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, module.ImportReference(fromUnmanagedPtr));
il.Emit(OpCodes.Castclass, module.ImportReference(networkStream));
il.Emit(OpCodes.Stloc_1);
// Generate normal serializer
var serializer = GenerateSerializer(type, serialize, ref failed, name, voidType, networkStreamType);
@@ -810,17 +828,20 @@ namespace Flax.Build.Plugins
failed = true;
return;
}
if (property.SetMethod == null)
{
MonoCecil.CompilationError($"Missing setter method for property '{property.Name}' of type {valueType.FullName} in {type.FullName} for automatic replication.", property);
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();
if (_inBuildSerializers.TryGetValue(valueType.FullName, out var serializer))
@@ -843,6 +864,7 @@ namespace Flax.Build.Plugins
il.Emit(OpCodes.Ldarg_1);
m = networkStreamType.GetMethod(serializer.ReadMethod);
}
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
if (!serialize)
{
@@ -966,7 +988,7 @@ namespace Flax.Build.Plugins
else
il.Emit(propertyGetOpCode, property.GetMethod);
// int num2 = ((array2 != null) ? array2.Length : 0);
// int num2 = ((array2 != null) ? array2.Length : 0);
il.Emit(OpCodes.Dup);
Instruction jmp1 = il.Create(OpCodes.Nop);
il.Emit(OpCodes.Brtrue_S, jmp1);
@@ -979,8 +1001,8 @@ namespace Flax.Build.Plugins
il.Emit(OpCodes.Conv_I4);
il.Append(jmp2);
il.Emit(OpCodes.Stloc, varStart + 0);
// stream.WriteInt32(num2);
// stream.WriteInt32(num2);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldloc, varStart + 0);
var m = networkStreamType.GetMethod("WriteInt32");
@@ -1039,16 +1061,16 @@ namespace Flax.Build.Plugins
var m = networkStreamType.GetMethod("ReadInt32");
il.Emit(OpCodes.Callvirt, module.ImportReference(m));
il.Emit(OpCodes.Stloc, varStart + 0);
// System.Array.Resize(ref Array1, num);
// System.Array.Resize(ref Array1, num);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldflda, field);
il.Emit(OpCodes.Ldloc, varStart + 0);
module.TryGetTypeReference("System.Array", out var arrayType);
m = arrayType.Resolve().GetMethod("Resize", 2);
il.Emit(OpCodes.Call, module.ImportReference(m.InflateGeneric(elementType)));
// fixed (int* buffer = Array1)
// fixed (int* buffer = Array1)
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, field);
@@ -1067,8 +1089,8 @@ namespace Flax.Build.Plugins
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>));
// stream.ReadBytes((byte*)buffer, num * sizeof(<elementType>));
il.Append(jmp2);
il.Emit(OpCodes.Ldloc, varStart + 2);
il.Emit(OpCodes.Ldc_I4_0);
@@ -1127,7 +1149,7 @@ namespace Flax.Build.Plugins
module.GetType("System.Guid", out var guidType);
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));
@@ -1222,12 +1244,14 @@ namespace Flax.Build.Plugins
failed = true;
return;
}
if (method.IsVirtual)
{
MonoCecil.CompilationError($"Not supported virtual RPC method '{method.FullName}'.", method);
failed = true;
return;
}
ModuleDefinition module = type.Module;
var voidType = module.TypeSystem.Void;
if (method.ReturnType != voidType)
@@ -1236,12 +1260,14 @@ namespace Flax.Build.Plugins
failed = true;
return;
}
if (method.IsStatic)
{
MonoCecil.CompilationError($"Not supported static RPC method '{method.FullName}'.", method);
failed = true;
return;
}
var methodRPC = new MethodRPC();
methodRPC.Type = type;
methodRPC.Method = method;
@@ -1252,6 +1278,7 @@ namespace Flax.Build.Plugins
methodRPC.IsClient = (bool)attribute.ConstructorArguments[1].Value;
methodRPC.Channel = (int)attribute.ConstructorArguments[2].Value;
}
methodRPC.IsServer = (bool)attribute.GetFieldValue("Server", methodRPC.IsServer);
methodRPC.IsClient = (bool)attribute.GetFieldValue("Client", methodRPC.IsClient);
methodRPC.Channel = (int)attribute.GetFieldValue("Channel", methodRPC.Channel);
@@ -1261,12 +1288,14 @@ namespace Flax.Build.Plugins
failed = true;
return;
}
if (!methodRPC.IsServer && !methodRPC.IsClient)
{
MonoCecil.CompilationError($"Network RPC {method.Name} in {type.FullName} needs to have Server or Client specifier.", method);
failed = true;
return;
}
module.GetType("System.IntPtr", out var intPtrType);
module.GetType("FlaxEngine.Object", out var scriptingObjectType);
var fromUnmanagedPtr = scriptingObjectType.Resolve().GetMethod("FromUnmanagedPtr");
@@ -1294,7 +1323,7 @@ namespace Flax.Build.Plugins
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;
for (int i = 0; i < method.Parameters.Count; i++)
@@ -1306,6 +1335,7 @@ namespace Flax.Build.Plugins
failed = true;
return;
}
var parameterType = parameter.ParameterType;
il.Body.Variables.Add(new VariableDefinition(parameterType));
}
@@ -1324,8 +1354,9 @@ namespace Flax.Build.Plugins
{
il.Emit(OpCodes.Ldloc, argsStart + i);
}
il.Emit(OpCodes.Callvirt, method);
il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ret);
type.Methods.Add(m);
@@ -1418,7 +1449,6 @@ namespace Flax.Build.Plugins
il.InsertBefore(ilStart, tmp);
//il.InsertBefore(ilStart, il.Create(OpCodes.Ret));
il.InsertBefore(ilStart, il.Create(OpCodes.Br, jumpBodyEnd));
}
// if (client && networkMode == NetworkManagerMode.Server) return;