Add ReaderWriter for custom types

This commit is contained in:
Zirno authored and js6pak committed 2021-07-26 17:07:57 +02:00
1 parent c6d2ff686b
commit 41413eea20
7 files changed
+150 -68

No files matched your search

@@ -27,32 +27,29 @@ namespace Reactor.Networking.MethodRpc
var methodParameters = method.GetParameters(); var methodParameters = method.GetParameters();
List<Type> allArgs = new List<Type>(); Parameters = new Type[methodParameters.Length];
foreach (var arg in methodParameters) for (var i = 0; i < methodParameters.Length; i++)
{ {
var arg = methodParameters[i];
var argType = arg.ParameterType; var argType = arg.ParameterType;
if (argType.IsValueType && !argType.IsPrimitive && !StructBook.ContainsKey(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(); .ToArray();
StructBook.Add(argType, feilds); StructBook.Add(argType, fields);
} }
allArgs.Add(argType); Parameters[i] = argType;
} }
Parameters = allArgs.ToArray();
Invoker = RpcPrefixHandle.GenerateCaller(Method); Invoker = RpcPrefixHandle.GenerateCaller(Method);
allMethodRPCsFast.Add(Method, this); allMethodRPCsFast.Add(Method, this);
} }
public MethodInfo Method { get; } public MethodInfo Method { get; }
public override Type InnerNetObjectType { get; } = typeof(PlayerControl); 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 SendOption SendOption { get; }
public override void UnsafeWrite(MessageWriter writer, object data) public override void UnsafeWrite(MessageWriter writer, object data)
@@ -76,6 +73,8 @@ namespace Reactor.Networking.MethodRpc
{ {
Coroutines.Start((IEnumerator) result); Coroutines.Start((IEnumerator) result);
} }
SkipNextSend = false;
} }
public void Send(object[] args) public void Send(object[] args)
@@ -58,10 +58,9 @@ namespace Reactor.Networking.MethodRpc
// ignored // ignored
} }
} }
} }
internal static void Initialize() internal static void Initialize()
{ {
ChainloaderHooks.PluginLoad += plugin => Register(plugin.GetType().Assembly, plugin); ChainloaderHooks.PluginLoad += plugin => Register(plugin.GetType().Assembly, plugin);
@@ -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<Type, UnsafeReaderWriter> _readerWriters = new();
public IReadOnlyDictionary<Type, UnsafeReaderWriter> All => _readerWriters;
public void Register(UnsafeReaderWriter readerWriter)
{
Logger<ReactorPlugin>.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<T> : 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);
}
}
@@ -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<ReaderWriterAttribute>();
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);
}
}
}
@@ -14,11 +14,7 @@ namespace Reactor.Networking.MethodRpc
public static bool RpcPrefix(MethodBase originalMethod, params object[] list) public static bool RpcPrefix(MethodBase originalMethod, params object[] list)
{ {
if (CustomMethodRpc.SkipNextSend) if (CustomMethodRpc.SkipNextSend) return true;
{
CustomMethodRpc.SkipNextSend = false;
return true;
}
var methodRpc = CustomMethodRpc.allMethodRPCsFast[originalMethod]; var methodRpc = CustomMethodRpc.allMethodRPCsFast[originalMethod];
methodRpc?.Send(list); methodRpc?.Send(list);
+57 -51
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using Hazel; using Hazel;
using InnerNet; using InnerNet;
@@ -10,29 +11,6 @@ namespace Reactor.Networking.MethodRpc
{ {
public static class RpcSerializer public static class RpcSerializer
{ {
#region GetFindObjectByNetIdInvoker
private static readonly MethodInfo _findObjectByNetId =
typeof(RpcSerializer).GetMethod(nameof(FindObjectByNetId), BindingFlags.Static | BindingFlags.NonPublic);
private static Dictionary<Type, Func<object[], object>> _findObjectByNetIdInvokers = new();
static Func<object[], object> 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<T>(uint id) where T : InnerNetObject
{
return AmongUsClient.Instance.FindObjectByNetId<T>(id);
}
#endregion
public static void Serialize(MessageWriter writer,CustomMethodRpc methodRpc, params object[] list) public static void Serialize(MessageWriter writer,CustomMethodRpc methodRpc, params object[] list)
{ {
foreach (var o in list) foreach (var o in list)
@@ -49,6 +27,7 @@ namespace Reactor.Networking.MethodRpc
{ {
writer.Write(o); writer.Write(o);
} }
Logger<ReactorPlugin>.Debug($"Serialized {o.GetType()}");
} }
} }
@@ -58,51 +37,44 @@ namespace Reactor.Networking.MethodRpc
{ {
case int i: case int i:
writer.WritePacked(i); writer.WritePacked(i);
Logger<ReactorPlugin>.Debug($"Serialized int");
break; break;
case uint i: case uint i:
writer.WritePacked(i); writer.WritePacked(i);
Logger<ReactorPlugin>.Debug($"Serialized uint");
break; break;
case byte i: case byte i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized byte");
break; break;
case float i: case float i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized float");
break; break;
case sbyte i: case sbyte i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized sbyte");
break; break;
case ushort i: case ushort i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized ushort");
break; break;
case bool i: case bool i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized bool");
break; break;
case Vector2 i: case Vector2 i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized Vector2");
break; break;
case string i: case string i:
writer.Write(i); writer.Write(i);
Logger<ReactorPlugin>.Debug($"Serialized string");
break; break;
default: default:
if (o.GetType().IsSubclassOf(typeof(InnerNetObject))) if (o.GetType().IsSubclassOf(typeof(InnerNetObject)))
{ {
Logger<ReactorPlugin>.Debug($"Serialized {nameof(InnerNetObject)}"); writer.WriteNetObject((InnerNetObject) o);
writer.WritePacked(((InnerNetObject)o).NetId); }
else if (ReaderWriterManager.Instance.All.ContainsKey(o.GetType()))
{
ReaderWriterManager.Instance.All[o.GetType()].UnsafeWrite(writer, o);
} }
else else
{ {
Logger<ReactorPlugin>.Warning($"Tried serializing unsupported type {nameof(o)}"); Logger<ReactorPlugin>.Warning($"Tried serializing unsupported type {o.GetType()}");
} }
break; break;
} }
} }
@@ -112,33 +84,34 @@ namespace Reactor.Networking.MethodRpc
{ {
Logger<ReactorPlugin>.Debug($"Deserializing {methodRpc.Parameters.Length} object"); Logger<ReactorPlugin>.Debug($"Deserializing {methodRpc.Parameters.Length} object");
List<object> 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) if (t.IsValueType && !t.IsPrimitive)
{ {
List<object> structArgs = new(); var fields = methodRpc.StructBook[t];
var feilds = methodRpc.StructBook[t];
foreach (var feild in feilds)
{
var value = reader.Read(feild.FieldType);
Logger<ReactorPlugin>.Debug($"Read {value} of {feild.FieldType.Name} for {methodRpc.Method.Name}");
structArgs.Add(value);
}
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<ReactorPlugin>.Debug($"Deserialized {fields[n].FieldType} from {t}");
}
t.GetType().GetConstructors().ToList().ForEach(x=>Logger<ReactorPlugin>.Info($"Gw {x.GetParameters().Length}"));
args[i] = Activator.CreateInstance(t,structArgs);
} }
else else
{ {
var value = reader.Read(t); var value = reader.Read(t);
Logger<ReactorPlugin>.Debug($"Read {value} of {t.Name} for {methodRpc.Method.Name}"); Logger<ReactorPlugin>.Debug($"Read {value} of {t.Name} for {methodRpc.Method.Name}");
args[i] = value;
args.Add(value);
} }
} }
return args.ToArray(); return args;
} }
@@ -191,12 +164,45 @@ namespace Reactor.Networking.MethodRpc
if (t.IsSubclassOf(typeof(InnerNetObject))) 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<ReactorPlugin>.Warning($"Tried deserializing unsupported type {t.BaseType.Name}"); Logger<ReactorPlugin>.Warning($"Tried deserializing unsupported type {t.BaseType.Name}");
return null; return null;
} }
#region InnerNetObject
private static readonly MethodInfo _findObjectByNetId =
typeof(RpcSerializer).GetMethod(nameof(FindObjectByNetId), BindingFlags.Static | BindingFlags.NonPublic);
private static Dictionary<Type, Func<object[], object>> _findObjectByNetIdInvokers = new();
static Func<object[], object> 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<T>(uint id) where T : InnerNetObject
{
return AmongUsClient.Instance.FindObjectByNetId<T>(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
} }
} }
+1
View File
@@ -39,6 +39,7 @@ namespace Reactor
RegisterInIl2CppAttribute.Initialize(); RegisterInIl2CppAttribute.Initialize();
RegisterCustomRpcAttribute.Initialize(); RegisterCustomRpcAttribute.Initialize();
MethodRpcAttribute.Initialize(); MethodRpcAttribute.Initialize();
ReaderWriterAttribute.Initialize();
ChainloaderHooks.OnPluginLoad(this); ChainloaderHooks.OnPluginLoad(this);
} }