use Serialize/Deserialize in weaver

This commit is contained in:
JohnCorby 2022-01-13 22:38:53 -08:00
parent c6c475c344
commit ff779b2895
3 changed files with 81 additions and 8 deletions

View File

@ -269,7 +269,8 @@ namespace Mirror.Weaver
GenerateNullCheck(worker, ref WeavingFailed);
CreateNew(variable, worker, td, ref WeavingFailed);
ReadAllFields(variable, worker, ref WeavingFailed);
if (!ReadFromDeserialize(td, worker))
ReadAllFields(variable, worker, ref WeavingFailed);
worker.Emit(OpCodes.Ldloc_0);
worker.Emit(OpCodes.Ret);
@ -317,12 +318,10 @@ namespace Mirror.Weaver
// Log.Error($"{variable.Name} can't be deserialized because it has no default constructor. Don't use {variable.Name} in [SyncVar]s, Rpcs, Cmds, etc.", variable);
// WeavingFailed = true;
// return;
var resolvedVariable = variable.Resolve();
var anyCtor = resolvedVariable.Methods.First(m => m.IsConstructor);
var anyCtor = td.Methods.First(m => m.IsConstructor);
ctor = new MethodDefinition(anyCtor.Name, anyCtor.Attributes, anyCtor.ReturnType);
var ctorWorker = ctor.Body.GetILProcessor();
ctorWorker.Emit(OpCodes.Ret);
resolvedVariable.Methods.Add(ctor);
ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
td.Methods.Add(ctor);
}
MethodReference ctorRef = assembly.MainModule.ImportReference(ctor);
@ -332,6 +331,42 @@ namespace Mirror.Weaver
}
}
// try to use Deserialize if this is a message
bool ReadFromDeserialize(TypeDefinition klass, ILProcessor worker)
{
if (!klass.IsDerivedFrom<NetworkMessage>())
return false;
foreach (var method in klass.Methods)
{
if (method.Name != "Deserialize")
continue;
if (method.Parameters.Count != 1)
continue;
if (!method.Parameters[0].ParameterType.Is<NetworkWriter>())
continue;
if (!method.ReturnType.Is(typeof(void)))
continue;
if (method.HasGenericParameters)
continue;
// todo does this even work?
Log.Error($"read using {method}", klass);
// mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime
var opcode = klass.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc;
worker.Emit(opcode, 0); // the klass
worker.Emit(OpCodes.Ldarg_0); // the reader
worker.Emit(OpCodes.Callvirt, method);
return true;
}
return false;
}
void ReadAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed)
{
foreach (FieldDefinition field in variable.FindAllPublicFields())

View File

@ -216,8 +216,9 @@ namespace Mirror.Weaver
if (!variable.Resolve().IsValueType)
WriteNullCheck(worker, ref WeavingFailed);
if (!WriteAllFields(variable, worker, ref WeavingFailed))
return null;
if (!WriteFromSerialize(variable.Resolve(), worker))
if (!WriteAllFields(variable, worker, ref WeavingFailed))
return null;
worker.Emit(OpCodes.Ret);
return writerFunc;
@ -247,6 +248,40 @@ namespace Mirror.Weaver
worker.Emit(OpCodes.Call, GetWriteFunc(weaverTypes.Import<bool>(), ref WeavingFailed));
}
// try to use Serialize if this is a message
bool WriteFromSerialize(TypeDefinition klass, ILProcessor worker)
{
if (!klass.IsDerivedFrom<NetworkMessage>())
return false;
foreach (var method in klass.Methods)
{
if (method.Name != "Serialize")
continue;
if (method.Parameters.Count != 1)
continue;
if (!method.Parameters[0].ParameterType.Is<NetworkReader>())
continue;
if (!method.ReturnType.Is(typeof(void)))
continue;
if (method.HasGenericParameters)
continue;
// todo does this even work?
Log.Error($"write using {method}", klass);
worker.Emit(OpCodes.Ldarg_1); // the klass
worker.Emit(OpCodes.Ldarg_0); // the writer
worker.Emit(OpCodes.Callvirt, method);
return true;
}
return false;
}
// Find all fields in type and write them
bool WriteAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed)
{

View File

@ -196,6 +196,9 @@ namespace QSB.Messaging
/// </summary>
public abstract class QSBMessageRaw : NetworkMessage
{
public virtual void Serialize(NetworkWriter writer) { }
public virtual void Deserialize(NetworkReader reader) { }
public abstract void OnReceive();
public override string ToString() => GetType().Name;
}