quantum-space-buddies/QNetWeaver/SyncListStructProcessor.cs
2021-06-18 22:40:38 +01:00

310 lines
12 KiB
C#

using Mono.Cecil;
using Mono.Cecil.Cil;
namespace QNetWeaver
{
internal class SyncListStructProcessor
{
public SyncListStructProcessor(TypeDefinition typeDef)
{
Weaver.DLog(typeDef, "SyncListStructProcessor for " + typeDef.Name, new object[0]);
m_TypeDef = typeDef;
}
public void Process()
{
var genericInstanceType = (GenericInstanceType)m_TypeDef.BaseType;
if (genericInstanceType.GenericArguments.Count == 0)
{
Weaver.fail = true;
Log.Error("SyncListStructProcessor no generic args");
}
else
{
m_ItemType = Weaver.scriptDef.MainModule.ImportReference(genericInstanceType.GenericArguments[0]);
Weaver.DLog(m_TypeDef, "SyncListStructProcessor Start item:" + m_ItemType.FullName, new object[0]);
Weaver.ResetRecursionCount();
var methodReference = GenerateSerialization();
if (!Weaver.fail)
{
var methodReference2 = GenerateDeserialization();
if (methodReference2 != null && methodReference != null)
{
GenerateReadFunc(methodReference2);
GenerateWriteFunc(methodReference);
Weaver.DLog(m_TypeDef, "SyncListStructProcessor Done", new object[0]);
}
}
}
}
private void GenerateReadFunc(MethodReference readItemFunc)
{
var text = "_ReadStruct" + m_TypeDef.Name + "_";
if (m_TypeDef.DeclaringType != null)
{
text += m_TypeDef.DeclaringType.Name;
}
else
{
text += "None";
}
var methodDefinition = new MethodDefinition(text, MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType);
methodDefinition.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(Weaver.NetworkReaderType)));
methodDefinition.Parameters.Add(new ParameterDefinition("instance", ParameterAttributes.None, m_TypeDef));
methodDefinition.Body.Variables.Add(new VariableDefinition(Weaver.uint16Type));
methodDefinition.Body.Variables.Add(new VariableDefinition(Weaver.uint16Type));
methodDefinition.Body.InitLocals = true;
var ilprocessor = methodDefinition.Body.GetILProcessor();
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, Weaver.NetworkReadUInt16));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
var method = Helpers.MakeHostInstanceGeneric(Weaver.SyncListClear, new TypeReference[]
{
m_ItemType
});
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, method));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
var instruction = ilprocessor.Create(OpCodes.Nop);
ilprocessor.Append(ilprocessor.Create(OpCodes.Br, instruction));
var instruction2 = ilprocessor.Create(OpCodes.Nop);
ilprocessor.Append(instruction2);
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, readItemFunc));
var self = Weaver.ResolveMethod(Weaver.SyncListStructType, "AddInternal");
var method2 = Helpers.MakeHostInstanceGeneric(self, new TypeReference[]
{
m_ItemType
});
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, method2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Add));
ilprocessor.Append(ilprocessor.Create(OpCodes.Conv_U2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
ilprocessor.Append(instruction);
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Blt, instruction2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
Weaver.RegisterReadByReferenceFunc(m_TypeDef.FullName, methodDefinition);
}
private void GenerateWriteFunc(MethodReference writeItemFunc)
{
var text = "_WriteStruct" + m_TypeDef.GetElementType().Name + "_";
if (m_TypeDef.DeclaringType != null)
{
text += m_TypeDef.DeclaringType.Name;
}
else
{
text += "None";
}
var methodDefinition = new MethodDefinition(text, MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType);
methodDefinition.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(Weaver.NetworkWriterType)));
methodDefinition.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(m_TypeDef)));
methodDefinition.Body.Variables.Add(new VariableDefinition(Weaver.uint16Type));
methodDefinition.Body.Variables.Add(new VariableDefinition(Weaver.uint16Type));
methodDefinition.Body.InitLocals = true;
var ilprocessor = methodDefinition.Body.GetILProcessor();
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
var self = Weaver.ResolveMethod(Weaver.SyncListStructType, "get_Count");
var method = Helpers.MakeHostInstanceGeneric(self, new TypeReference[]
{
m_ItemType
});
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, method));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, Weaver.NetworkWriteUInt16));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
var instruction = ilprocessor.Create(OpCodes.Nop);
ilprocessor.Append(ilprocessor.Create(OpCodes.Br, instruction));
var instruction2 = ilprocessor.Create(OpCodes.Nop);
ilprocessor.Append(instruction2);
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
var self2 = Weaver.ResolveMethod(Weaver.SyncListStructType, "GetItem");
var method2 = Helpers.MakeHostInstanceGeneric(self2, new TypeReference[]
{
m_ItemType
});
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, method2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Callvirt, writeItemFunc));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldc_I4_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Add));
ilprocessor.Append(ilprocessor.Create(OpCodes.Conv_U2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stloc_1));
ilprocessor.Append(instruction);
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Blt, instruction2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
Weaver.RegisterWriteFunc(m_TypeDef.FullName, methodDefinition);
}
private MethodReference GenerateSerialization()
{
Weaver.DLog(m_TypeDef, " SyncListStruct GenerateSerialization", new object[0]);
foreach (var methodDefinition in m_TypeDef.Methods)
{
if (methodDefinition.Name == "SerializeItem")
{
Weaver.DLog(m_TypeDef, " Abort - is SerializeItem", new object[0]);
return methodDefinition;
}
}
var methodDefinition2 = new MethodDefinition("SerializeItem", MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType);
methodDefinition2.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(Weaver.NetworkWriterType)));
methodDefinition2.Parameters.Add(new ParameterDefinition("item", ParameterAttributes.None, m_ItemType));
var ilprocessor = methodDefinition2.Body.GetILProcessor();
MethodReference result;
if (m_ItemType.IsGenericInstance)
{
Weaver.fail = true;
Log.Error("GenerateSerialization for " + Helpers.PrettyPrintType(m_ItemType) + " failed. Struct passed into SyncListStruct<T> can't have generic parameters");
result = null;
}
else
{
foreach (var fieldDefinition in m_ItemType.Resolve().Fields)
{
if (!fieldDefinition.IsStatic && !fieldDefinition.IsPrivate && !fieldDefinition.IsSpecialName)
{
var fieldReference = Weaver.scriptDef.MainModule.ImportReference(fieldDefinition);
var typeDefinition = fieldReference.FieldType.Resolve();
if (typeDefinition.HasGenericParameters)
{
Weaver.fail = true;
Log.Error(string.Concat(new object[]
{
"GenerateSerialization for ",
m_TypeDef.Name,
" [",
typeDefinition,
"/",
typeDefinition.FullName,
"]. UNet [MessageBase] member cannot have generic parameters."
}));
return null;
}
if (typeDefinition.IsInterface)
{
Weaver.fail = true;
Log.Error(string.Concat(new object[]
{
"GenerateSerialization for ",
m_TypeDef.Name,
" [",
typeDefinition,
"/",
typeDefinition.FullName,
"]. UNet [MessageBase] member cannot be an interface."
}));
return null;
}
var writeFunc = Weaver.GetWriteFunc(fieldDefinition.FieldType);
if (writeFunc == null)
{
Weaver.fail = true;
Log.Error(string.Concat(new object[]
{
"GenerateSerialization for ",
m_TypeDef.Name,
" unknown type [",
typeDefinition,
"/",
typeDefinition.FullName,
"]. UNet [MessageBase] member variables must be basic types."
}));
return null;
}
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_2));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldfld, fieldReference));
ilprocessor.Append(ilprocessor.Create(OpCodes.Call, writeFunc));
}
}
ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
m_TypeDef.Methods.Add(methodDefinition2);
result = methodDefinition2;
}
return result;
}
private MethodReference GenerateDeserialization()
{
Weaver.DLog(m_TypeDef, " GenerateDeserialization", new object[0]);
foreach (var methodDefinition in m_TypeDef.Methods)
{
if (methodDefinition.Name == "DeserializeItem")
{
return methodDefinition;
}
}
var methodDefinition2 = new MethodDefinition("DeserializeItem", MethodAttributes.FamANDAssem | MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig, m_ItemType);
methodDefinition2.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.scriptDef.MainModule.ImportReference(Weaver.NetworkReaderType)));
var ilprocessor = methodDefinition2.Body.GetILProcessor();
ilprocessor.Body.InitLocals = true;
ilprocessor.Body.Variables.Add(new VariableDefinition(m_ItemType));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloca, 0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Initobj, m_ItemType));
foreach (var fieldDefinition in m_ItemType.Resolve().Fields)
{
if (!fieldDefinition.IsStatic && !fieldDefinition.IsPrivate && !fieldDefinition.IsSpecialName)
{
var fieldReference = Weaver.scriptDef.MainModule.ImportReference(fieldDefinition);
var typeDefinition = fieldReference.FieldType.Resolve();
var readFunc = Weaver.GetReadFunc(fieldDefinition.FieldType);
if (readFunc == null)
{
Weaver.fail = true;
Log.Error(string.Concat(new object[]
{
"GenerateDeserialization for ",
m_TypeDef.Name,
" unknown type [",
typeDefinition,
"]. UNet [SyncVar] member variables must be basic types."
}));
return null;
}
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloca, 0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldarg_1));
ilprocessor.Append(ilprocessor.Create(OpCodes.Call, readFunc));
ilprocessor.Append(ilprocessor.Create(OpCodes.Stfld, fieldReference));
}
}
ilprocessor.Append(ilprocessor.Create(OpCodes.Ldloc_0));
ilprocessor.Append(ilprocessor.Create(OpCodes.Ret));
m_TypeDef.Methods.Add(methodDefinition2);
return methodDefinition2;
}
private TypeDefinition m_TypeDef;
private TypeReference m_ItemType;
}
}