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 Reactor.Extensions;
|
||||
using Reactor.Networking;
|
||||
using Reactor.Networking.MethodRpc;
|
||||
using Reactor.Patches;
|
||||
using Reactor.Unstrip;
|
||||
using UnhollowerBaseLib.Attributes;
|
||||
@@ -37,6 +38,7 @@ namespace Reactor
|
||||
PluginSingleton<BasePlugin>.Initialize();
|
||||
RegisterInIl2CppAttribute.Initialize();
|
||||
RegisterCustomRpcAttribute.Initialize();
|
||||
MethodRpcAttribute.Initialize();
|
||||
|
||||
ChainloaderHooks.OnPluginLoad(this);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user