diff --git a/Reactor/Networking/MethodRpc/CustomMethodRpc.cs b/Reactor/Networking/MethodRpc/CustomMethodRpc.cs index 12d8dd2..1140634 100644 --- a/Reactor/Networking/MethodRpc/CustomMethodRpc.cs +++ b/Reactor/Networking/MethodRpc/CustomMethodRpc.cs @@ -27,32 +27,29 @@ namespace Reactor.Networking.MethodRpc var methodParameters = method.GetParameters(); - List allArgs = new List(); - foreach (var arg in methodParameters) + Parameters = new Type[methodParameters.Length]; + for (var i = 0; i < methodParameters.Length; i++) { + var arg = methodParameters[i]; var argType = arg.ParameterType; if (argType.IsValueType && !argType.IsPrimitive && !StructBook.ContainsKey(arg.ParameterType)) { - var feilds = argType.GetFields(BindingFlags.Instance | BindingFlags.Public) + var fields = argType.GetFields(BindingFlags.Instance | BindingFlags.Public) .ToArray(); - StructBook.Add(argType, feilds); + StructBook.Add(argType, fields); } - allArgs.Add(argType); - + Parameters[i] = argType; } - Parameters = allArgs.ToArray(); - Invoker = RpcPrefixHandle.GenerateCaller(Method); allMethodRPCsFast.Add(Method, this); - } public MethodInfo Method { get; } public override Type InnerNetObjectType { get; } = typeof(PlayerControl); - public override RpcLocalHandling LocalHandling { get; } = RpcLocalHandling.None; + public override RpcLocalHandling LocalHandling { get; } public override SendOption SendOption { get; } public override void UnsafeWrite(MessageWriter writer, object data) @@ -76,6 +73,8 @@ namespace Reactor.Networking.MethodRpc { Coroutines.Start((IEnumerator) result); } + + SkipNextSend = false; } public void Send(object[] args) diff --git a/Reactor/Networking/MethodRpc/MethodRpcAttribute.cs b/Reactor/Networking/MethodRpc/MethodRpcAttribute.cs index 063ab10..78081b3 100644 --- a/Reactor/Networking/MethodRpc/MethodRpcAttribute.cs +++ b/Reactor/Networking/MethodRpc/MethodRpcAttribute.cs @@ -37,7 +37,7 @@ namespace Reactor.Networking.MethodRpc foreach (var method in rpcMethods) { - + if (!method.IsStatic) { Logger.Warning("Cannot register non static custom method rpc"); @@ -45,7 +45,7 @@ namespace Reactor.Networking.MethodRpc } var attribute = method.GetCustomAttribute(); - + var customRpc = new CustomMethodRpc(plugin, method, attribute.id, attribute.option, attribute.localHandling); PluginSingleton.Instance.Harmony.Patch(method, new HarmonyMethod(_rpcPrefixGenerator)); @@ -58,9 +58,8 @@ namespace Reactor.Networking.MethodRpc // ignored } } - } - + } internal static void Initialize() { diff --git a/Reactor/Networking/MethodRpc/ReaderWriter.cs b/Reactor/Networking/MethodRpc/ReaderWriter.cs new file mode 100644 index 0000000..9b60115 --- /dev/null +++ b/Reactor/Networking/MethodRpc/ReaderWriter.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using Hazel; + +namespace Reactor.Networking.MethodRpc +{ + + internal class ReaderWriterManager + { + public static ReaderWriterManager Instance { get; } = new(); + + private readonly Dictionary _readerWriters = new(); + + public IReadOnlyDictionary All => _readerWriters; + + public void Register(UnsafeReaderWriter readerWriter) + { + Logger.Instance.LogInfo($"Registered ReaderWriter for {readerWriter.Type}"); + if(_readerWriters.ContainsKey(readerWriter.Type)) return; + + _readerWriters.Add(readerWriter.Type, readerWriter); + } + } + + public abstract class UnsafeReaderWriter + { + public abstract Type Type { get; } + public abstract void UnsafeWrite(MessageWriter writer, object dateTime); + + public abstract object UnsafeRead(MessageReader reader); + } + + public abstract class ReaderWriter : UnsafeReaderWriter + { + public abstract void Write(MessageWriter writer, T o); + + public abstract T Read(MessageReader reader); + + public override Type Type { get; } = typeof(T); + + public override void UnsafeWrite(MessageWriter writer, object o) => Write(writer, (T) o); + + public override object UnsafeRead(MessageReader reader) => Read(reader); + } +} diff --git a/Reactor/Networking/MethodRpc/ReaderWriterAttribute.cs b/Reactor/Networking/MethodRpc/ReaderWriterAttribute.cs new file mode 100644 index 0000000..3c62ad1 --- /dev/null +++ b/Reactor/Networking/MethodRpc/ReaderWriterAttribute.cs @@ -0,0 +1,36 @@ +using System; +using System.Reflection; +using BepInEx.IL2CPP; +using HarmonyLib; + +namespace Reactor.Networking.MethodRpc +{ + [AttributeUsage(AttributeTargets.Class)] + public class ReaderWriterAttribute : System.Attribute + { + + public static void Register(Assembly assembly, BasePlugin plugin) + { + foreach (var type in assembly.GetTypes()) + { + var attribute = type.GetCustomAttribute(); + + if (attribute != null) + { + if (!type.IsSubclassOf(typeof(UnsafeReaderWriter))) + { + throw new InvalidOperationException($"Type {type.FullDescription()} has {nameof(ReaderWriterAttribute)} but doesn't extend {nameof(UnsafeReaderWriter)}."); + } + + var customRpc = (UnsafeReaderWriter) Activator.CreateInstance(type); + ReaderWriterManager.Instance.Register(customRpc); + } + } + } + + internal static void Initialize() + { + ChainloaderHooks.PluginLoad += plugin => Register(plugin.GetType().Assembly, plugin); + } + } +} diff --git a/Reactor/Networking/MethodRpc/RpcPrefixHandle.cs b/Reactor/Networking/MethodRpc/RpcPrefixHandle.cs index 718886e..1c959a3 100644 --- a/Reactor/Networking/MethodRpc/RpcPrefixHandle.cs +++ b/Reactor/Networking/MethodRpc/RpcPrefixHandle.cs @@ -14,11 +14,7 @@ namespace Reactor.Networking.MethodRpc public static bool RpcPrefix(MethodBase originalMethod, params object[] list) { - if (CustomMethodRpc.SkipNextSend) - { - CustomMethodRpc.SkipNextSend = false; - return true; - } + if (CustomMethodRpc.SkipNextSend) return true; var methodRpc = CustomMethodRpc.allMethodRPCsFast[originalMethod]; methodRpc?.Send(list); diff --git a/Reactor/Networking/MethodRpc/RpcSerializer.cs b/Reactor/Networking/MethodRpc/RpcSerializer.cs index bcf1c30..0c7d3b5 100644 --- a/Reactor/Networking/MethodRpc/RpcSerializer.cs +++ b/Reactor/Networking/MethodRpc/RpcSerializer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Hazel; using InnerNet; @@ -10,29 +11,6 @@ namespace Reactor.Networking.MethodRpc { public static class RpcSerializer { - #region GetFindObjectByNetIdInvoker - - private static readonly MethodInfo _findObjectByNetId = - typeof(RpcSerializer).GetMethod(nameof(FindObjectByNetId), BindingFlags.Static | BindingFlags.NonPublic); - - private static Dictionary> _findObjectByNetIdInvokers = new(); - static Func GetFindObjectByNetIdInvoker(Type type) - { - if (_findObjectByNetIdInvokers.ContainsKey(type)) return _findObjectByNetIdInvokers[type]; - - var invoker = RpcPrefixHandle.GenerateCaller(_findObjectByNetId.MakeGenericMethod(type)); - _findObjectByNetIdInvokers.Add(type, invoker); - return invoker; - } - - private static InnerNetObject FindObjectByNetId(uint id) where T : InnerNetObject - { - return AmongUsClient.Instance.FindObjectByNetId(id); - } - - #endregion - - public static void Serialize(MessageWriter writer,CustomMethodRpc methodRpc, params object[] list) { foreach (var o in list) @@ -49,6 +27,7 @@ namespace Reactor.Networking.MethodRpc { writer.Write(o); } + Logger.Debug($"Serialized {o.GetType()}"); } } @@ -58,51 +37,44 @@ namespace Reactor.Networking.MethodRpc { case int i: writer.WritePacked(i); - Logger.Debug($"Serialized int"); break; case uint i: writer.WritePacked(i); - Logger.Debug($"Serialized uint"); break; case byte i: writer.Write(i); - Logger.Debug($"Serialized byte"); break; case float i: writer.Write(i); - Logger.Debug($"Serialized float"); break; case sbyte i: writer.Write(i); - Logger.Debug($"Serialized sbyte"); break; case ushort i: writer.Write(i); - Logger.Debug($"Serialized ushort"); break; case bool i: writer.Write(i); - Logger.Debug($"Serialized bool"); break; case Vector2 i: writer.Write(i); - Logger.Debug($"Serialized Vector2"); break; case string i: writer.Write(i); - Logger.Debug($"Serialized string"); break; default: if (o.GetType().IsSubclassOf(typeof(InnerNetObject))) { - Logger.Debug($"Serialized {nameof(InnerNetObject)}"); - writer.WritePacked(((InnerNetObject)o).NetId); + writer.WriteNetObject((InnerNetObject) o); + } + else if (ReaderWriterManager.Instance.All.ContainsKey(o.GetType())) + { + ReaderWriterManager.Instance.All[o.GetType()].UnsafeWrite(writer, o); } else { - Logger.Warning($"Tried serializing unsupported type {nameof(o)}"); + Logger.Warning($"Tried serializing unsupported type {o.GetType()}"); } - break; } } @@ -112,33 +84,34 @@ namespace Reactor.Networking.MethodRpc { Logger.Debug($"Deserializing {methodRpc.Parameters.Length} object"); - List args = new(); + object[] args = new object[methodRpc.Parameters.Length]; - foreach (var t in methodRpc.Parameters) + for (int i = 0; i < args.Length; i++) { + Type t = methodRpc.Parameters[i]; if (t.IsValueType && !t.IsPrimitive) { - List structArgs = new(); - var feilds = methodRpc.StructBook[t]; - foreach (var feild in feilds) - { - var value = reader.Read(feild.FieldType); - Logger.Debug($"Read {value} of {feild.FieldType.Name} for {methodRpc.Method.Name}"); - structArgs.Add(value); - } + var fields = methodRpc.StructBook[t]; - args.Add(Activator.CreateInstance(t,args: structArgs.ToArray())); //Todo: Improve somehow + object[] structArgs = new object[fields.Length]; + for (int n = 0; n < structArgs.Length; n++) + { + structArgs[n] = reader.Read(fields[n].FieldType); + Logger.Debug($"Deserialized {fields[n].FieldType} from {t}"); + } + t.GetType().GetConstructors().ToList().ForEach(x=>Logger.Info($"Gw {x.GetParameters().Length}")); + + args[i] = Activator.CreateInstance(t,structArgs); } else { var value = reader.Read(t); Logger.Debug($"Read {value} of {t.Name} for {methodRpc.Method.Name}"); - - args.Add(value); + args[i] = value; } } - return args.ToArray(); + return args; } @@ -191,12 +164,45 @@ namespace Reactor.Networking.MethodRpc if (t.IsSubclassOf(typeof(InnerNetObject))) { - return GetFindObjectByNetIdInvoker(t)(new object[]{reader.ReadPackedUInt32()}); + return reader.ReadNetObject(t); + } + + if (ReaderWriterManager.Instance.All.ContainsKey(t.BaseType!)) + { + return ReaderWriterManager.Instance.All[t.BaseType].UnsafeRead(reader); } Logger.Warning($"Tried deserializing unsupported type {t.BaseType.Name}"); return null; } + + #region InnerNetObject + + private static readonly MethodInfo _findObjectByNetId = + typeof(RpcSerializer).GetMethod(nameof(FindObjectByNetId), BindingFlags.Static | BindingFlags.NonPublic); + + private static Dictionary> _findObjectByNetIdInvokers = new(); + static Func GetFindObjectByNetIdInvoker(Type type) + { + if (_findObjectByNetIdInvokers.ContainsKey(type)) return _findObjectByNetIdInvokers[type]; + + var invoker = RpcPrefixHandle.GenerateCaller(_findObjectByNetId.MakeGenericMethod(type)); + _findObjectByNetIdInvokers.Add(type, invoker); + return invoker; + } + + private static InnerNetObject FindObjectByNetId(uint id) where T : InnerNetObject + { + return AmongUsClient.Instance.FindObjectByNetId(id); + } + + public static void WriteNetObject(this MessageWriter writer, InnerNetObject o) => writer.WritePacked(o.NetId); + + public static InnerNetObject ReadNetObject(this MessageReader reader, Type t) => + (InnerNetObject) GetFindObjectByNetIdInvoker(t)(new object[] {reader.ReadPackedUInt32()}); + + #endregion + } } diff --git a/Reactor/ReactorPlugin.cs b/Reactor/ReactorPlugin.cs index f70fc0a..eb83a9e 100644 --- a/Reactor/ReactorPlugin.cs +++ b/Reactor/ReactorPlugin.cs @@ -39,6 +39,7 @@ namespace Reactor RegisterInIl2CppAttribute.Initialize(); RegisterCustomRpcAttribute.Initialize(); MethodRpcAttribute.Initialize(); + ReaderWriterAttribute.Initialize(); ChainloaderHooks.OnPluginLoad(this); }