public static bool ProcessMethodsValidateTargetRpc(MethodDefinition md, CustomAttribute ca) { if (!md.Name.StartsWith("Target")) { Weaver.Error($"{md} must start with Target. Consider renaming it to Target{md.Name}"); return(false); } if (md.IsStatic) { Weaver.Error($"{md} must not be static"); return(false); } if (!NetworkBehaviourProcessor.ProcessMethodsValidateFunction(md)) { return(false); } // validate return(NetworkBehaviourProcessor.ProcessMethodsValidateParameters(md, ca)); }
public static MethodDefinition ProcessTargetRpcInvoke(TypeDefinition td, MethodDefinition md) { MethodDefinition rpc = new MethodDefinition(RpcProcessor.RpcPrefix + md.Name, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, Weaver.voidType); ILProcessor rpcWorker = rpc.Body.GetILProcessor(); Instruction label = rpcWorker.Create(OpCodes.Nop); NetworkBehaviourProcessor.WriteClientActiveCheck(rpcWorker, md.Name, label, "TargetRPC"); // setup for reader rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_0)); rpcWorker.Append(rpcWorker.Create(OpCodes.Castclass, td)); // NetworkConnection parameter is optional bool hasNetworkConnection = HasNetworkConnectionParameter(md); if (hasNetworkConnection) { //ClientScene.readyconnection rpcWorker.Append(rpcWorker.Create(OpCodes.Call, Weaver.ReadyConnectionReference)); } // process reader parameters and skip first one if first one is NetworkConnection if (!NetworkBehaviourProcessor.ProcessNetworkReaderParameters(md, rpcWorker, hasNetworkConnection)) { return(null); } // invoke actual command function rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, md)); rpcWorker.Append(rpcWorker.Create(OpCodes.Ret)); NetworkBehaviourProcessor.AddInvokeParameters(rpc.Parameters); return(rpc); }
public static MethodDefinition ProcessTargetRpcInvoke(TypeDefinition td, MethodDefinition md, MethodDefinition rpcCallFunc) { MethodDefinition rpc = new MethodDefinition(Weaver.InvokeRpcPrefix + md.Name, MethodAttributes.Family | MethodAttributes.Static | MethodAttributes.HideBySig, WeaverTypes.voidType); ILProcessor worker = rpc.Body.GetILProcessor(); Instruction label = worker.Create(OpCodes.Nop); NetworkBehaviourProcessor.WriteClientActiveCheck(worker, md.Name, label, "TargetRPC"); // setup for reader worker.Append(worker.Create(OpCodes.Ldarg_0)); worker.Append(worker.Create(OpCodes.Castclass, td)); // NetworkConnection parameter is optional if (HasNetworkConnectionParameter(md)) { // if call has NetworkConnection write clients connection as first arg //ClientScene.readyconnection worker.Append(worker.Create(OpCodes.Call, WeaverTypes.ReadyConnectionReference)); } // process reader parameters and skip first one if first one is NetworkConnection if (!NetworkBehaviourProcessor.ReadArguments(md, worker, RemoteCallType.TargetRpc)) { return(null); } // invoke actual command function worker.Append(worker.Create(OpCodes.Callvirt, rpcCallFunc)); worker.Append(worker.Create(OpCodes.Ret)); NetworkBehaviourProcessor.AddInvokeParameters(rpc.Parameters); td.Methods.Add(rpc); return(rpc); }
/* // generates code like: public void CmdThrust(float thrusting, int spin) { NetworkWriter networkWriter = new NetworkWriter(); networkWriter.Write(thrusting); networkWriter.WritePackedUInt32((uint)spin); base.SendCommandInternal(cmdName, networkWriter, cmdName); } public void CallCmdThrust(float thrusting, int spin) { // whatever the user was doing before } Originally HLAPI put the send message code inside the Call function and then proceeded to replace every call to CmdTrust with CallCmdTrust This method moves all the user's code into the "CallCmd" 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 ProcessCommandCall(TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr) { MethodDefinition cmd = MethodProcessor.SubstituteMethod(td, md); ILProcessor worker = md.Body.GetILProcessor(); NetworkBehaviourProcessor.WriteSetupLocals(worker); // NetworkWriter writer = new NetworkWriter(); NetworkBehaviourProcessor.WriteCreateWriter(worker); // write all the arguments that the user passed to the Cmd call if (!NetworkBehaviourProcessor.WriteArguments(worker, md, RemoteCallType.Command)) return null; string cmdName = md.Name; int channel = commandAttr.GetField("channel", 0); bool ignoreAuthority = commandAttr.GetField("ignoreAuthority", false); // invoke internal send and return // load 'base.' to call the SendCommand function with 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, cmdName)); // writer worker.Append(worker.Create(OpCodes.Ldloc_0)); worker.Append(worker.Create(OpCodes.Ldc_I4, channel)); worker.Append(worker.Create(ignoreAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0)); worker.Append(worker.Create(OpCodes.Call, WeaverTypes.sendCommandInternal)); NetworkBehaviourProcessor.WriteRecycleWriter(worker); worker.Append(worker.Create(OpCodes.Ret)); return cmd; }
/* * // generates code like: * public void CmdThrust(float thrusting, int spin) * { * NetworkWriter networkWriter = new NetworkWriter(); * networkWriter.Write(thrusting); * networkWriter.WritePackedUInt32((uint)spin); * base.SendCommandInternal(cmdName, networkWriter, channel); * } * * public void CallCmdThrust(float thrusting, int spin) * { * // whatever the user was doing before * } * * Originally HLAPI put the send message code inside the Call function * and then proceeded to replace every call to CmdTrust with CallCmdTrust * * This method moves all the user's code into the "CallCmd" 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 ProcessCommandCall(WeaverTypes weaverTypes, Writers writers, Logger Log, TypeDefinition td, MethodDefinition md, CustomAttribute commandAttr, ref bool WeavingFailed) { MethodDefinition cmd = MethodProcessor.SubstituteMethod(Log, td, md, ref WeavingFailed); ILProcessor worker = md.Body.GetILProcessor(); NetworkBehaviourProcessor.WriteSetupLocals(worker, weaverTypes); // NetworkWriter writer = new NetworkWriter(); NetworkBehaviourProcessor.WriteCreateWriter(worker, weaverTypes); // write all the arguments that the user passed to the Cmd call if (!NetworkBehaviourProcessor.WriteArguments(worker, writers, Log, md, RemoteCallType.Command, ref WeavingFailed)) { return(null); } int channel = commandAttr.GetField("channel", 0); bool requiresAuthority = commandAttr.GetField("requiresAuthority", true); // invoke internal send and return // load 'base.' to call the SendCommand function with worker.Emit(OpCodes.Ldarg_0); // pass full function name to avoid ClassA.Func <-> ClassB.Func collisions worker.Emit(OpCodes.Ldstr, md.FullName); // writer worker.Emit(OpCodes.Ldloc_0); worker.Emit(OpCodes.Ldc_I4, channel); // requiresAuthority ? 1 : 0 worker.Emit(requiresAuthority ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); worker.Emit(OpCodes.Call, weaverTypes.sendCommandInternal); NetworkBehaviourProcessor.WriteRecycleWriter(worker, weaverTypes); worker.Emit(OpCodes.Ret); return(cmd); }
public static bool ProcessMethodsValidateTargetRpc(TypeDefinition td, MethodDefinition md, CustomAttribute ca) { const string targetPrefix = "Target"; int prefixLen = targetPrefix.Length; if (md.Name.Length > prefixLen && md.Name.Substring(0, prefixLen) != targetPrefix) { Weaver.Error("Target Rpc function [" + td.FullName + ":" + md.Name + "] doesnt have 'Target' prefix"); return(false); } if (md.IsStatic) { Weaver.Error("TargetRpc function [" + td.FullName + ":" + md.Name + "] cant be a static method"); return(false); } if (!NetworkBehaviourProcessor.ProcessMethodsValidateFunction(td, md, "Target Rpc")) { return(false); } if (md.Parameters.Count < 1) { Weaver.Error("Target Rpc function [" + td.FullName + ":" + md.Name + "] must have a NetworkConnection as the first parameter"); return(false); } if (md.Parameters[0].ParameterType.FullName != Weaver.NetworkConnectionType.FullName) { Weaver.Error("Target Rpc function [" + td.FullName + ":" + md.Name + "] first parameter must be a NetworkConnection"); return(false); } // validate return(NetworkBehaviourProcessor.ProcessMethodsValidateParameters(td, md, ca, "Target Rpc")); }
/* generates code like: * public void CallTargetTest (NetworkConnection conn, int param) * { * if (!NetworkServer.get_active ()) { * Debug.LogError((object)"TargetRPC Function TargetTest called on client."); * } else if (((?)conn) is ULocalConnectionToServer) { * Debug.LogError((object)"TargetRPC Function TargetTest called on connection to server"); * } else { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32 ((uint)param); * base.SendTargetRPCInternal (conn, typeof(class), "TargetTest", val); * } * } */ public static MethodDefinition ProcessTargetRpcCall(TypeDefinition td, MethodDefinition md, CustomAttribute ca) { MethodDefinition rpc = new MethodDefinition("Call" + md.Name, MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); // add paramters foreach (ParameterDefinition pd in md.Parameters) { rpc.Parameters.Add(new ParameterDefinition(pd.Name, ParameterAttributes.None, pd.ParameterType)); } ILProcessor rpcWorker = rpc.Body.GetILProcessor(); Instruction label = rpcWorker.Create(OpCodes.Nop); NetworkBehaviourProcessor.WriteSetupLocals(rpcWorker); NetworkBehaviourProcessor.WriteServerActiveCheck(rpcWorker, md.Name, label, "TargetRPC Function"); Instruction labelConnectionCheck = rpcWorker.Create(OpCodes.Nop); // check specifically for ULocalConnectionToServer so a host is not trying to send // an TargetRPC to the "server" from it's local client. rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_1)); rpcWorker.Append(rpcWorker.Create(OpCodes.Isinst, Weaver.ULocalConnectionToServerType)); rpcWorker.Append(rpcWorker.Create(OpCodes.Brfalse, labelConnectionCheck)); rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, string.Format("TargetRPC Function {0} called on connection to server", md.Name))); rpcWorker.Append(rpcWorker.Create(OpCodes.Call, Weaver.logErrorReference)); rpcWorker.Append(rpcWorker.Create(OpCodes.Ret)); rpcWorker.Append(labelConnectionCheck); NetworkBehaviourProcessor.WriteCreateWriter(rpcWorker); // write all the arguments that the user passed to the TargetRpc call if (!NetworkBehaviourProcessor.WriteArguments(rpcWorker, md, "TargetRPC", true)) { return(null); } var rpcName = md.Name; int index = rpcName.IndexOf(k_TargetRpcPrefix); if (index > -1) { rpcName = rpcName.Substring(k_TargetRpcPrefix.Length); } // invoke SendInternal and return rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_0)); // this rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_1)); // connection rpcWorker.Append(rpcWorker.Create(OpCodes.Ldtoken, td)); rpcWorker.Append(rpcWorker.Create(OpCodes.Call, Weaver.getTypeFromHandleReference)); // invokerClass rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, rpcName)); rpcWorker.Append(rpcWorker.Create(OpCodes.Ldloc_0)); // writer rpcWorker.Append(rpcWorker.Create(OpCodes.Ldc_I4, NetworkBehaviourProcessor.GetChannelId(ca))); rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, Weaver.sendTargetRpcInternal)); rpcWorker.Append(rpcWorker.Create(OpCodes.Ret)); return(rpc); }
/* * // generates code like: * public void CmdThrust(float thrusting, int spin) * { * if (isServer) * { * // we are ON the server, invoke directly * CmdThrust(thrusting, spin); * return; * } * * NetworkWriter networkWriter = new NetworkWriter(); * networkWriter.Write(thrusting); * networkWriter.WritePackedUInt32((uint)spin); * base.SendCommandInternal(cmdName, networkWriter, cmdName); * } * * public void CallCmdThrust(float thrusting, int spin) * { * // whatever the user was doing before * } * * Originally HLAPI put the send message code inside the Call function * and then proceeded to replace every call to CmdTrust with CallCmdTrust * * This method moves all the user's code into the "Call" 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 ProcessCommandCall(TypeDefinition td, MethodDefinition md, CustomAttribute ca) { MethodDefinition cmd = new MethodDefinition("Call" + md.Name, MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); // add parameters foreach (ParameterDefinition pd in md.Parameters) { cmd.Parameters.Add(new ParameterDefinition(pd.Name, ParameterAttributes.None, pd.ParameterType)); } // move the old body to the new function MethodBody newBody = cmd.Body; cmd.Body = md.Body; md.Body = newBody; ILProcessor cmdWorker = md.Body.GetILProcessor(); NetworkBehaviourProcessor.WriteSetupLocals(cmdWorker); if (Weaver.GenerateLogErrors) { cmdWorker.Append(cmdWorker.Create(OpCodes.Ldstr, "Call Command function " + md.Name)); cmdWorker.Append(cmdWorker.Create(OpCodes.Call, Weaver.logErrorReference)); } // local client check Instruction localClientLabel = cmdWorker.Create(OpCodes.Nop); cmdWorker.Append(cmdWorker.Create(OpCodes.Ldarg_0)); cmdWorker.Append(cmdWorker.Create(OpCodes.Call, Weaver.getBehaviourIsServer)); cmdWorker.Append(cmdWorker.Create(OpCodes.Brfalse, localClientLabel)); // call the cmd function directly. cmdWorker.Append(cmdWorker.Create(OpCodes.Ldarg_0)); for (int i = 0; i < md.Parameters.Count; i++) { cmdWorker.Append(cmdWorker.Create(OpCodes.Ldarg, i + 1)); } cmdWorker.Append(cmdWorker.Create(OpCodes.Call, cmd)); cmdWorker.Append(cmdWorker.Create(OpCodes.Ret)); cmdWorker.Append(localClientLabel); // NetworkWriter writer = new NetworkWriter(); NetworkBehaviourProcessor.WriteCreateWriter(cmdWorker); // write all the arguments that the user passed to the Cmd call if (!NetworkBehaviourProcessor.WriteArguments(cmdWorker, md, false)) { return(null); } string cmdName = md.Name; int index = cmdName.IndexOf(CmdPrefix); if (index > -1) { cmdName = cmdName.Substring(CmdPrefix.Length); } // invoke internal send and return cmdWorker.Append(cmdWorker.Create(OpCodes.Ldarg_0)); // load 'base.' to call the SendCommand function with cmdWorker.Append(cmdWorker.Create(OpCodes.Ldtoken, td)); cmdWorker.Append(cmdWorker.Create(OpCodes.Call, Weaver.getTypeFromHandleReference)); // invokerClass cmdWorker.Append(cmdWorker.Create(OpCodes.Ldstr, cmdName)); cmdWorker.Append(cmdWorker.Create(OpCodes.Ldloc_0)); // writer cmdWorker.Append(cmdWorker.Create(OpCodes.Ldc_I4, NetworkBehaviourProcessor.GetChannelId(ca))); cmdWorker.Append(cmdWorker.Create(OpCodes.Call, Weaver.sendCommandInternal)); NetworkBehaviourProcessor.WriteRecycleWriter(cmdWorker); cmdWorker.Append(cmdWorker.Create(OpCodes.Ret)); return(cmd); }
/* generates code like: * public void TargetTest (NetworkConnection conn, int param) * { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32 ((uint)param); * base.SendTargetRPCInternal (conn, typeof(class), "TargetTest", val); * } * public void CallTargetTest (NetworkConnection conn, int param) * { * // whatever the user did before * } * * or if optional: * public void TargetTest (int param) * { * NetworkWriter writer = new NetworkWriter (); * writer.WritePackedUInt32 ((uint)param); * base.SendTargetRPCInternal (null, typeof(class), "TargetTest", val); * } * public void CallTargetTest (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 TargetTest with CallTargetTest * * This method moves all the user's code into the "Call" 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 ProcessTargetRpcCall(TypeDefinition td, MethodDefinition md, CustomAttribute ca) { var rpc = new MethodDefinition("Call" + md.Name, MethodAttributes.Public | MethodAttributes.HideBySig, Weaver.voidType); // add parameters foreach (var pd in md.Parameters) { rpc.Parameters.Add(new ParameterDefinition(pd.Name, ParameterAttributes.None, pd.ParameterType)); } // move the old body to the new function var newBody = rpc.Body; rpc.Body = md.Body; md.Body = newBody; var rpcWorker = md.Body.GetILProcessor(); NetworkBehaviourProcessor.WriteSetupLocals(rpcWorker); NetworkBehaviourProcessor.WriteCreateWriter(rpcWorker); // NetworkConnection parameter is optional var hasNetworkConnection = HasNetworkConnectionParameter(md); // write all the arguments that the user passed to the TargetRpc call // (skip first one if first one is NetworkConnection) if (!NetworkBehaviourProcessor.WriteArguments(rpcWorker, md, hasNetworkConnection)) { return(null); } var rpcName = md.Name; var index = rpcName.IndexOf(TargetRpcPrefix); if (index > -1) { rpcName = rpcName.Substring(TargetRpcPrefix.Length); } // invoke SendInternal and return rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_0)); // this if (HasNetworkConnectionParameter(md)) { rpcWorker.Append(rpcWorker.Create(OpCodes.Ldarg_1)); // connection } else { rpcWorker.Append(rpcWorker.Create(OpCodes.Ldnull)); // null } rpcWorker.Append(rpcWorker.Create(OpCodes.Ldtoken, td)); rpcWorker.Append(rpcWorker.Create(OpCodes.Call, Weaver.getTypeFromHandleReference)); // invokerClass rpcWorker.Append(rpcWorker.Create(OpCodes.Ldstr, rpcName)); rpcWorker.Append(rpcWorker.Create(OpCodes.Ldloc_0)); // writer rpcWorker.Append(rpcWorker.Create(OpCodes.Ldc_I4, NetworkBehaviourProcessor.GetChannelId(ca))); rpcWorker.Append(rpcWorker.Create(OpCodes.Callvirt, Weaver.sendTargetRpcInternal)); NetworkBehaviourProcessor.WriteRecycleWriter(rpcWorker); rpcWorker.Append(rpcWorker.Create(OpCodes.Ret)); return(rpc); }