mirror of
https://github.com/misternebula/quantum-space-buddies.git
synced 2025-01-01 03:32:38 +00:00
175 lines
6.9 KiB
C#
175 lines
6.9 KiB
C#
// [SyncVar] int health;
|
|
// is replaced with:
|
|
// public int Networkhealth { get; set; } properties.
|
|
// this class processes all access to 'health' and replaces it with 'Networkhealth'
|
|
using System;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
|
|
namespace Mirror.Weaver
|
|
{
|
|
public static class SyncVarAttributeAccessReplacer
|
|
{
|
|
// process the module
|
|
public static void Process(ModuleDefinition moduleDef, SyncVarAccessLists syncVarAccessLists)
|
|
{
|
|
DateTime startTime = DateTime.Now;
|
|
|
|
// process all classes in this module
|
|
foreach (TypeDefinition td in moduleDef.Types)
|
|
{
|
|
if (td.IsClass)
|
|
{
|
|
ProcessClass(syncVarAccessLists, td);
|
|
}
|
|
}
|
|
|
|
Console.WriteLine($" ProcessSitesModule {moduleDef.Name} elapsed time:{(DateTime.Now - startTime)}");
|
|
}
|
|
|
|
static void ProcessClass(SyncVarAccessLists syncVarAccessLists, TypeDefinition td)
|
|
{
|
|
//Console.WriteLine($" ProcessClass {td}");
|
|
|
|
// process all methods in this class
|
|
foreach (MethodDefinition md in td.Methods)
|
|
{
|
|
ProcessMethod(syncVarAccessLists, md);
|
|
}
|
|
|
|
// processes all nested classes in this class recursively
|
|
foreach (TypeDefinition nested in td.NestedTypes)
|
|
{
|
|
ProcessClass(syncVarAccessLists, nested);
|
|
}
|
|
}
|
|
|
|
static void ProcessMethod(SyncVarAccessLists syncVarAccessLists, MethodDefinition md)
|
|
{
|
|
// process all references to replaced members with properties
|
|
//Log.Warning($" ProcessSiteMethod {md}");
|
|
|
|
// skip static constructor, "MirrorProcessed", "InvokeUserCode_"
|
|
if (md.Name == ".cctor" ||
|
|
md.Name == NetworkBehaviourProcessor.ProcessedFunctionName ||
|
|
md.Name.StartsWith(Weaver.InvokeRpcPrefix))
|
|
return;
|
|
|
|
// skip abstract
|
|
if (md.IsAbstract)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// go through all instructions of this method
|
|
if (md.Body != null && md.Body.Instructions != null)
|
|
{
|
|
for (int i = 0; i < md.Body.Instructions.Count;)
|
|
{
|
|
Instruction instr = md.Body.Instructions[i];
|
|
i += ProcessInstruction(syncVarAccessLists, md, instr, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ProcessInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, int iCount)
|
|
{
|
|
// stfld (sets value of a field)?
|
|
if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldDefinition opFieldst)
|
|
{
|
|
ProcessSetInstruction(syncVarAccessLists, md, instr, opFieldst);
|
|
}
|
|
|
|
// ldfld (load value of a field)?
|
|
if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldDefinition opFieldld)
|
|
{
|
|
// this instruction gets the value of a field. cache the field reference.
|
|
ProcessGetInstruction(syncVarAccessLists, md, instr, opFieldld);
|
|
}
|
|
|
|
// ldflda (load field address aka reference)
|
|
if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldDefinition opFieldlda)
|
|
{
|
|
// watch out for initobj instruction
|
|
// see https://github.com/vis2k/Mirror/issues/696
|
|
return ProcessLoadAddressInstruction(syncVarAccessLists, md, instr, opFieldlda, iCount);
|
|
}
|
|
|
|
// we processed one instruction (instr)
|
|
return 1;
|
|
}
|
|
|
|
// replaces syncvar write access with the NetworkXYZ.set property calls
|
|
static void ProcessSetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField)
|
|
{
|
|
// don't replace property call sites in constructors
|
|
if (md.Name == ".ctor")
|
|
return;
|
|
|
|
// does it set a field that we replaced?
|
|
if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement))
|
|
{
|
|
//replace with property
|
|
//Log.Warning($" replacing {md.Name}:{i}", opField);
|
|
i.OpCode = OpCodes.Call;
|
|
i.Operand = replacement;
|
|
//Log.Warning($" replaced {md.Name}:{i}", opField);
|
|
}
|
|
}
|
|
|
|
// replaces syncvar read access with the NetworkXYZ.get property calls
|
|
static void ProcessGetInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction i, FieldDefinition opField)
|
|
{
|
|
// don't replace property call sites in constructors
|
|
if (md.Name == ".ctor")
|
|
return;
|
|
|
|
// does it set a field that we replaced?
|
|
if (syncVarAccessLists.replacementGetterProperties.TryGetValue(opField, out MethodDefinition replacement))
|
|
{
|
|
//replace with property
|
|
//Log.Warning($" replacing {md.Name}:{i}");
|
|
i.OpCode = OpCodes.Call;
|
|
i.Operand = replacement;
|
|
//Log.Warning($" replaced {md.Name}:{i}");
|
|
}
|
|
}
|
|
|
|
static int ProcessLoadAddressInstruction(SyncVarAccessLists syncVarAccessLists, MethodDefinition md, Instruction instr, FieldDefinition opField, int iCount)
|
|
{
|
|
// don't replace property call sites in constructors
|
|
if (md.Name == ".ctor")
|
|
return 1;
|
|
|
|
// does it set a field that we replaced?
|
|
if (syncVarAccessLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement))
|
|
{
|
|
// we have a replacement for this property
|
|
// is the next instruction a initobj?
|
|
Instruction nextInstr = md.Body.Instructions[iCount + 1];
|
|
|
|
if (nextInstr.OpCode == OpCodes.Initobj)
|
|
{
|
|
// we need to replace this code with:
|
|
// var tmp = new MyStruct();
|
|
// this.set_Networkxxxx(tmp);
|
|
ILProcessor worker = md.Body.GetILProcessor();
|
|
VariableDefinition tmpVariable = new VariableDefinition(opField.FieldType);
|
|
md.Body.Variables.Add(tmpVariable);
|
|
|
|
worker.InsertBefore(instr, worker.Create(OpCodes.Ldloca, tmpVariable));
|
|
worker.InsertBefore(instr, worker.Create(OpCodes.Initobj, opField.FieldType));
|
|
worker.InsertBefore(instr, worker.Create(OpCodes.Ldloc, tmpVariable));
|
|
worker.InsertBefore(instr, worker.Create(OpCodes.Call, replacement));
|
|
|
|
worker.Remove(instr);
|
|
worker.Remove(nextInstr);
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
}
|