mirror of
https://github.com/NuclearPowered/Reactor.git
synced 2026-10-19 15:51:54 +00:00
method Rpc
This commit is contained in:
1 parent
0f0d322c8c
commit
eb7fce3350
5 files changed
+407
No files matched your search
@@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using BepInEx.IL2CPP;
|
||||||
|
using Hazel;
|
||||||
|
using InnerNet;
|
||||||
|
|
||||||
|
namespace Reactor.Networking.MethodRpc
|
||||||
|
{
|
||||||
|
public class CustomMethodRpc : UnsafeCustomRpc
|
||||||
|
{
|
||||||
|
public static readonly List<CustomMethodRpc> AllCustomMethodRpcs = new();
|
||||||
|
|
||||||
|
internal static bool SkipNextSend;
|
||||||
|
|
||||||
|
public CustomMethodRpc(BasePlugin plugin, MethodInfo method, uint id, SendOption option, RpcLocalHandling localHandling) : base(plugin, id)
|
||||||
|
{
|
||||||
|
Method = method;
|
||||||
|
LocalHandling = localHandling;
|
||||||
|
SendOption = option;
|
||||||
|
|
||||||
|
AllCustomMethodRpcs.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodInfo Method { get; }
|
||||||
|
public override Type InnerNetObjectType { get; } = typeof(PlayerControl);
|
||||||
|
public override RpcLocalHandling LocalHandling { get; }
|
||||||
|
public override SendOption SendOption { get; }
|
||||||
|
|
||||||
|
public override void UnsafeWrite(MessageWriter writer, object data)
|
||||||
|
{
|
||||||
|
var args = (object[]) data;
|
||||||
|
RpcSerializer.SendMassage(writer,args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object UnsafeRead(MessageReader reader)
|
||||||
|
{
|
||||||
|
var args = RpcSerializer.Deserialize(reader,Method.GetParameters().Select(x => x.ParameterType).ToArray());
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UnsafeHandle(InnerNetObject innerNetObject, object data)
|
||||||
|
{
|
||||||
|
SkipNextSend = true;
|
||||||
|
|
||||||
|
var result = Method.Invoke(null,(object[]) data);
|
||||||
|
if (Method.ReturnParameter.ParameterType == typeof(IEnumerator))
|
||||||
|
{
|
||||||
|
Coroutines.Start((IEnumerator) result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Send(object[] args)
|
||||||
|
{
|
||||||
|
UnsafeSend(PlayerControl.LocalPlayer,args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using BepInEx.IL2CPP;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Hazel;
|
||||||
|
|
||||||
|
namespace Reactor.Networking.MethodRpc
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class MethodRpcAttribute : Attribute
|
||||||
|
{
|
||||||
|
public byte id;
|
||||||
|
public SendOption option;
|
||||||
|
public RpcLocalHandling localHandling;
|
||||||
|
|
||||||
|
public MethodRpcAttribute( byte id, SendOption option = SendOption.Reliable, RpcLocalHandling localHandling = RpcLocalHandling.Before)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.option = option;
|
||||||
|
this.localHandling = localHandling;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Register(Assembly assembly, BasePlugin plugin)
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Info($"Registering Method Rpc for {assembly.GetName()}");
|
||||||
|
Dictionary<int, MethodInfo> serializeMethods = new();
|
||||||
|
|
||||||
|
MethodInfo serializeMethod;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while ((serializeMethod = RpcSerializationHandels.GetHandleFor(index)) != null)
|
||||||
|
{
|
||||||
|
serializeMethods.Add(index, serializeMethod);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Registered Rpc Serialization Handle for {index} args");
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rpcMethods = assembly.GetTypes()
|
||||||
|
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
|
||||||
|
.Where(m => m.GetCustomAttributes(typeof(MethodRpcAttribute), false).Length > 0)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var method in rpcMethods)
|
||||||
|
{
|
||||||
|
if (!method.IsStatic)
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Warning("Cannot register non static custom method rpc");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seializer = serializeMethods[method.GetParameters().Length];
|
||||||
|
if (seializer == null)
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Error($"Unsupported Args count, max is {RpcSerializationHandels.GetMaxArgs()}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var attribute = method.GetCustomAttribute<MethodRpcAttribute>();
|
||||||
|
if (attribute.localHandling == RpcLocalHandling.Before)
|
||||||
|
{
|
||||||
|
PluginSingleton<ReactorPlugin>.Instance.Harmony.Patch(method, new HarmonyMethod(seializer));
|
||||||
|
}
|
||||||
|
else if (attribute.localHandling == RpcLocalHandling.After)
|
||||||
|
{
|
||||||
|
PluginSingleton<ReactorPlugin>.Instance.Harmony.Patch(method, postfix: new HarmonyMethod(seializer));
|
||||||
|
}
|
||||||
|
var customRpc = new CustomMethodRpc(plugin, method, attribute.id, attribute.option, attribute.localHandling);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PluginSingleton<ReactorPlugin>.Instance.CustomRpcManager.Register(customRpc);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Initialize()
|
||||||
|
{
|
||||||
|
ChainloaderHooks.PluginLoad += plugin => Register(plugin.GetType().Assembly, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodRpc(123)]
|
||||||
|
static void Hello()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Reactor.Networking;
|
||||||
|
using Reactor.Networking.MethodRpc;
|
||||||
|
|
||||||
|
namespace Reactor.Networking
|
||||||
|
{
|
||||||
|
internal class RpcSerializationHandels
|
||||||
|
{
|
||||||
|
|
||||||
|
public static MethodInfo GetHandleFor(int count)
|
||||||
|
{
|
||||||
|
return typeof(RpcSerializationHandels).GetRuntimeMethod(nameof(Serialize),
|
||||||
|
Enumerable.Repeat(typeof(MethodBase),1).Concat(Enumerable.Repeat(typeof(object), count)).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetMaxArgs()
|
||||||
|
{
|
||||||
|
return typeof(RpcSerializationHandels).GetMethods().OrderBy(x => x.GetParameters().Length).Last().GetParameters().Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool RpcPrefix(MethodBase originalMethod, params object[] list)
|
||||||
|
{
|
||||||
|
if (CustomMethodRpc.SkipNextSend)
|
||||||
|
{
|
||||||
|
CustomMethodRpc.SkipNextSend = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomMethodRpc methodRpc =
|
||||||
|
CustomMethodRpc.AllCustomMethodRpcs.FirstOrDefault(x => x.Method == originalMethod);
|
||||||
|
typeof(CustomMethodRpc).GetMethod(nameof(CustomMethodRpc.Send))?.Invoke(methodRpc,new object[]{list});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Serialize(MethodBase __originalMethod)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Serialize(MethodBase __originalMethod, [HarmonyArgument(0)] object arg0)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod,arg0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Serialize(MethodBase __originalMethod, [HarmonyArgument(0)] object arg0,[HarmonyArgument(1)] object arg1)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod,new object[]{arg0,arg1});
|
||||||
|
}
|
||||||
|
public static bool Serialize(MethodBase __originalMethod, [HarmonyArgument(0)] object arg0 , [HarmonyArgument(1)] object arg1, [HarmonyArgument(2)] object arg2)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod,arg0,arg1,arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Serialize(MethodBase __originalMethod, [HarmonyArgument(0)] object arg0 , [HarmonyArgument(1)] object arg1, [HarmonyArgument(2)] object arg2,[HarmonyArgument(3)] object arg3)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod,arg0,arg1,arg2,arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Serialize(MethodBase __originalMethod, [HarmonyArgument(0)] object arg0 , [HarmonyArgument(1)] object arg1, [HarmonyArgument(2)] object arg2,[HarmonyArgument(3)] object arg3,[HarmonyArgument(4)] object arg4)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod,arg0,arg1,arg2,arg3,arg4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Serialize(MethodBase __originalMethod, [HarmonyArgument(0)] object arg0 , [HarmonyArgument(1)] object arg1, [HarmonyArgument(2)] object arg2,[HarmonyArgument(3)] object arg3,[HarmonyArgument(4)] object arg4,[HarmonyArgument(5)] object arg5)
|
||||||
|
{
|
||||||
|
return RpcPrefix(__originalMethod,arg0,arg1,arg2,arg3,arg4,arg5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using Hazel;
|
||||||
|
using InnerNet;
|
||||||
|
using Reactor;
|
||||||
|
using Reactor.Extensions;
|
||||||
|
using Reactor.Networking.MethodRpc;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Reactor.Networking
|
||||||
|
{
|
||||||
|
public static class RpcSerializer
|
||||||
|
{
|
||||||
|
public static void SendMassage(MessageWriter writer, params object[] list)
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serializing {list.Length} object");
|
||||||
|
|
||||||
|
foreach (var o in list)
|
||||||
|
{
|
||||||
|
if (o.GetType().IsValueType && !o.GetType().IsPrimitive)
|
||||||
|
{
|
||||||
|
var structFeilds = o.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
foreach (var feild in structFeilds)
|
||||||
|
{
|
||||||
|
writer.Write(feild.GetValue(o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.Write(o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(this MessageWriter writer, object o)
|
||||||
|
{
|
||||||
|
switch (o)
|
||||||
|
{
|
||||||
|
case int i:
|
||||||
|
writer.WritePacked(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized int");
|
||||||
|
break;
|
||||||
|
case uint i:
|
||||||
|
writer.WritePacked(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized uint");
|
||||||
|
break;
|
||||||
|
case byte i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized byte");
|
||||||
|
break;
|
||||||
|
case float i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized float");
|
||||||
|
break;
|
||||||
|
case sbyte i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized sbyte");
|
||||||
|
break;
|
||||||
|
case ushort i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized ushort");
|
||||||
|
break;
|
||||||
|
case bool i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized bool");
|
||||||
|
break;
|
||||||
|
case Vector2 i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized Vector2");
|
||||||
|
break;
|
||||||
|
case string i:
|
||||||
|
writer.Write(i);
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized string");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (o.GetType().IsSubclassOf(typeof(InnerNetObject)))
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Debug($"Serialized {nameof(InnerNetObject)}");
|
||||||
|
writer.WritePacked(((InnerNetObject)o).NetId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Warning($"Tried serializing unsupported type {nameof(o)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static object[] Deserialize(MessageReader reader, Type[] list)
|
||||||
|
{
|
||||||
|
Logger<ReactorPlugin>.Debug($"Deserializing {list.Length} object");
|
||||||
|
|
||||||
|
List<object> args = new();
|
||||||
|
|
||||||
|
foreach (var t in list)
|
||||||
|
{
|
||||||
|
if (t.IsValueType && !t.IsPrimitive)
|
||||||
|
{
|
||||||
|
List<object> structArgs = new();
|
||||||
|
var feilds = t.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
foreach (var feild in feilds)
|
||||||
|
{
|
||||||
|
structArgs.Add(reader.Read(feild.GetType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Add(Activator.CreateInstance(t, structArgs));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
args.Add(reader.Read(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static object Read(this MessageReader reader, Type t)
|
||||||
|
{
|
||||||
|
if (t == typeof(int) )
|
||||||
|
{
|
||||||
|
return reader.ReadPackedInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(uint))
|
||||||
|
{
|
||||||
|
return (reader.ReadPackedUInt32());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(byte))
|
||||||
|
{
|
||||||
|
return (reader.ReadByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(float))
|
||||||
|
{
|
||||||
|
return (reader.ReadSingle());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(sbyte))
|
||||||
|
{
|
||||||
|
return (reader.ReadSByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(ushort))
|
||||||
|
{
|
||||||
|
return (reader.ReadUInt16());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(bool))
|
||||||
|
{
|
||||||
|
return (reader.ReadBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(Vector2))
|
||||||
|
{
|
||||||
|
return (reader.ReadVector2());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t == typeof(string))
|
||||||
|
{
|
||||||
|
return (reader.ReadString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.IsSubclassOf(typeof(InnerNetObject)))
|
||||||
|
{
|
||||||
|
return typeof(AmongUsClient).GetMethod(nameof(AmongUsClient.FindObjectByNetId)).MakeGenericMethod(t)
|
||||||
|
.Invoke(AmongUsClient.Instance, new object[]{reader.ReadPackedUInt32()});
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger<ReactorPlugin>.Warning($"Tried deserializing unsupported type {t.BaseType.Name}");
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ using BepInEx.IL2CPP;
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Reactor.Extensions;
|
using Reactor.Extensions;
|
||||||
using Reactor.Networking;
|
using Reactor.Networking;
|
||||||
|
using Reactor.Networking.MethodRpc;
|
||||||
using Reactor.Patches;
|
using Reactor.Patches;
|
||||||
using Reactor.Unstrip;
|
using Reactor.Unstrip;
|
||||||
using UnhollowerBaseLib.Attributes;
|
using UnhollowerBaseLib.Attributes;
|
||||||
@@ -37,6 +38,7 @@ namespace Reactor
|
|||||||
PluginSingleton<BasePlugin>.Initialize();
|
PluginSingleton<BasePlugin>.Initialize();
|
||||||
RegisterInIl2CppAttribute.Initialize();
|
RegisterInIl2CppAttribute.Initialize();
|
||||||
RegisterCustomRpcAttribute.Initialize();
|
RegisterCustomRpcAttribute.Initialize();
|
||||||
|
MethodRpcAttribute.Initialize();
|
||||||
|
|
||||||
ChainloaderHooks.OnPluginLoad(this);
|
ChainloaderHooks.OnPluginLoad(this);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user