Make CustomRpc api more generic, PlayerColor enum, bug fixes in RegisterInIl2CppAttribute

This commit is contained in:
js6pak committed 2021-02-03 15:06:04 +01:00
1 parent 0cd79f9162
commit e90f6bcdbc
12 files changed
+230 -84

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@
</PropertyGroup>
<PropertyGroup>
<Mappings>NuclearPowered/Mappings:0.1.0</Mappings>
<Mappings>NuclearPowered/Mappings:0.1.1</Mappings>
</PropertyGroup>
<ItemGroup>
+2 -2
View File
@@ -38,8 +38,8 @@ namespace Reactor.Example
if (Input.GetKeyDown(KeyCode.F3) && AmongUsClient.Instance && PlayerControl.LocalPlayer)
{
Plugin.Log.LogWarning("Sending example rpc");
PlayerControl.LocalPlayer.Send<ExampleRpc>(new ExampleRpc.Data("Cześć :)"));
PlayerControl.LocalPlayer.SendTo<ExampleRpc>(AmongUsClient.Instance.HostId, new ExampleRpc.Data("host :O"));
Rpc<ExampleRpc>.Instance.Send(new ExampleRpc.Data("Cześć :)"));
Rpc<ExampleRpc>.Instance.SendTo(AmongUsClient.Instance.HostId, new ExampleRpc.Data("host :O"));
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ using Hazel;
namespace Reactor.Example
{
[RegisterCustomRpc]
public class ExampleRpc : CustomRpc<ExamplePlugin, PlayerControl, ExampleRpc.Data>
public class ExampleRpc : PlayerCustomRpc<ExamplePlugin, ExampleRpc.Data>
{
public ExampleRpc(ExamplePlugin plugin) : base(plugin)
{
+108 -59
View File
@@ -23,11 +23,13 @@ namespace Reactor
{
public int? Id { get; internal set; }
internal CustomRpcManager Manager { get; set; }
public BasePlugin UnsafePlugin { get; }
public string PluginId { get; }
public abstract Type InnerNetObjectType { get; }
public virtual SendOption SendOption { get; } = SendOption.Reliable;
public abstract RpcLocalHandling LocalHandling { get; }
protected UnsafeCustomRpc(BasePlugin plugin)
@@ -39,6 +41,54 @@ namespace Reactor
public abstract void UnsafeWrite(MessageWriter writer, object data);
public abstract object UnsafeRead(MessageReader reader);
public abstract void UnsafeHandle(InnerNetObject innerNetObject, object data);
public void UnsafeSend(InnerNetObject netObject, object data, bool immediately = false, int targetClientId = -1)
{
if (netObject == null) throw new ArgumentNullException(nameof(netObject));
if (data == null) throw new ArgumentNullException(nameof(data));
if (Id == null)
{
throw new InvalidOperationException("Can't send unregistered CustomRpc");
}
if (LocalHandling == RpcLocalHandling.Before)
{
UnsafeHandle(netObject, data);
}
var writer = immediately switch
{
false => AmongUsClient.Instance.StartRpc(netObject.NetId, CustomRpcManager.CallId, SendOption),
true => AmongUsClient.Instance.StartRpcImmediately(netObject.NetId, CustomRpcManager.CallId, SendOption, targetClientId)
};
if (Manager.PluginIdMap.TryGetValue(PluginId, out var pluginId))
{
writer.WritePacked(pluginId);
}
else
{
writer.Write(PluginId);
}
writer.WritePacked(Id!.Value);
UnsafeWrite(writer, data);
if (immediately)
{
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
else
{
writer.EndMessage();
}
if (LocalHandling == RpcLocalHandling.After)
{
UnsafeHandle(netObject, data);
}
}
}
public abstract class CustomRpc<TPlugin, TInnerNetObject, TData> : UnsafeCustomRpc where TPlugin : BasePlugin where TInnerNetObject : InnerNetObject
@@ -68,6 +118,33 @@ namespace Reactor
{
Handle((TInnerNetObject) innerNetObject, (TData) data);
}
public void Send(InnerNetObject netObject, TData data, bool immediately = false)
{
UnsafeSend(netObject, data, immediately);
}
public void SendTo(InnerNetObject netObject, int targetId, TData data)
{
UnsafeSend(netObject, data, true, targetId);
}
}
public abstract class PlayerCustomRpc<TPlugin, TData> : CustomRpc<TPlugin, PlayerControl, TData> where TPlugin : BasePlugin
{
protected PlayerCustomRpc(TPlugin plugin) : base(plugin)
{
}
public void Send(TData data, bool immediately = false)
{
Send(PlayerControl.LocalPlayer, data, immediately);
}
public void SendTo(int targetId, TData data)
{
SendTo(PlayerControl.LocalPlayer, targetId, data);
}
}
public class CustomRpcManager
@@ -114,72 +191,16 @@ namespace Reactor
public UnsafeCustomRpc Register(UnsafeCustomRpc customRpc)
{
customRpc.Manager = this;
customRpc.Id = List.Count(x => x.UnsafePlugin == customRpc.UnsafePlugin);
_list.Add(customRpc);
_map[customRpc.InnerNetObjectType].Add(customRpc);
typeof(Rpc<>).MakeGenericType(customRpc.GetType()).GetProperty("Instance")!.SetValue(null, customRpc);
return customRpc;
}
public void Send<TCustomRpc>(InnerNetObject netObject, object data, bool immediately = false)
{
UnsafeSend(netObject, List.Single(x => x.GetType() == typeof(TCustomRpc)), data, immediately);
}
public void SendTo<TCustomRpc>(InnerNetObject netObject, int targetId, object data)
{
UnsafeSend(netObject, List.Single(x => x.GetType() == typeof(TCustomRpc)), data, true, targetId);
}
public void UnsafeSend(InnerNetObject netObject, UnsafeCustomRpc customRpc, object data, bool immediately = false, int targetClientId = -1)
{
if (netObject == null) throw new ArgumentNullException(nameof(netObject));
if (customRpc == null) throw new ArgumentNullException(nameof(customRpc));
if (data == null) throw new ArgumentNullException(nameof(data));
if (customRpc.Id == null)
{
throw new InvalidOperationException("Can't send unregistered CustomRpc");
}
if (customRpc.LocalHandling == RpcLocalHandling.Before)
{
customRpc.UnsafeHandle(netObject, data);
}
var writer = immediately switch
{
false => AmongUsClient.Instance.StartRpc(netObject.NetId, CallId, SendOption.Reliable),
true => AmongUsClient.Instance.StartRpcImmediately(netObject.NetId, CallId, SendOption.Reliable, targetClientId)
};
if (PluginIdMap.TryGetValue(customRpc.PluginId, out var pluginId))
{
writer.WritePacked(pluginId);
}
else
{
writer.Write(customRpc.PluginId);
}
writer.WritePacked(customRpc.Id!.Value);
customRpc.UnsafeWrite(writer, data);
if (immediately)
{
AmongUsClient.Instance.FinishRpcImmediately(writer);
}
else
{
writer.EndMessage();
}
if (customRpc.LocalHandling == RpcLocalHandling.After)
{
customRpc.UnsafeHandle(netObject, data);
}
}
[HarmonyPatch]
private static class HandleRpcPatch
{
@@ -225,4 +246,32 @@ namespace Reactor
}
}
}
public static class Rpc<T> where T : UnsafeCustomRpc
{
private static T _instance;
public static T Instance
{
get
{
if (_instance == null)
{
throw new Exception($"{typeof(T).FullName} isn't registered");
}
return _instance;
}
internal set
{
if (_instance != null)
{
throw new Exception($"{typeof(T).FullName} is already registered");
}
_instance = value;
}
}
}
}
+6 -4
View File
@@ -122,14 +122,16 @@ namespace Reactor.Extensions
return type.GetMethods(AccessTools.all).Where(x => x.ReturnType == returnType && x.GetParameters().Select(x => x.ParameterType).SequenceEqual(parameterTypes));
}
public static void Send<TCustomRpc>(this InnerNetObject netObject, object data, bool immediately = false)
[Obsolete("Use Rpc<TCustomRpc>.Instance.Send", true)]
public static void Send<TCustomRpc>(this InnerNetObject netObject, object data, bool immediately = false) where TCustomRpc : UnsafeCustomRpc
{
PluginSingleton<ReactorPlugin>.Instance.CustomRpcManager.Send<TCustomRpc>(netObject, data, immediately);
Rpc<TCustomRpc>.Instance.UnsafeSend(netObject, data, immediately);
}
public static void SendTo<TCustomRpc>(this InnerNetObject netObject, int targetId, object data)
[Obsolete("Use Rpc<TCustomRpc>.Instance.SendTo", true)]
public static void SendTo<TCustomRpc>(this InnerNetObject netObject, int targetId, object data) where TCustomRpc : UnsafeCustomRpc
{
PluginSingleton<ReactorPlugin>.Instance.CustomRpcManager.SendTo<TCustomRpc>(netObject, targetId, data);
Rpc<TCustomRpc>.Instance.UnsafeSend(netObject, data, true, targetId);
}
}
}
+5
View File
@@ -116,6 +116,11 @@ namespace Reactor.Extensions
{
Rect = Rect.ResetSize();
Rect = GUILayout.Window(Id, Rect, Func, Title, new Il2CppReferenceArray<GUILayoutOption>(0));
if (Rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)))
{
Input.ResetInputAxes();
}
}
}
}
@@ -0,0 +1,17 @@
using UnityEngine;
namespace Reactor.Extensions
{
public static class PlayerControlExtensions
{
public static Color32 ToPlayerColor(this PlayerColor color)
{
return Palette.PlayerColors[(byte) color];
}
public static Color32 ToShadowColor(this PlayerColor color)
{
return Palette.ShadowColors[(byte) color];
}
}
}
+11
View File
@@ -0,0 +1,11 @@
// https://stackoverflow.com/questions/64749385/predefined-type-system-runtime-compilerservices-isexternalinit-is-not-defined?noredirect=1&lq=1
// IsExternalInit is not included with netstandard2.1 even tho it could
// ReSharper disable CheckNamespace
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit
{
}
}
+18
View File
@@ -0,0 +1,18 @@
namespace Reactor
{
public enum PlayerColor : byte
{
Red,
Blue,
Green,
Pink,
Orange,
Yellow,
Black,
White,
Purple,
Brown,
Cyan,
Lime
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using BepInEx.IL2CPP;
namespace Reactor
{
public class PluginSingleton<T> where T : BasePlugin
public static class PluginSingleton<T> where T : BasePlugin
{
private static T _instance;
+6 -3
View File
@@ -24,6 +24,7 @@ namespace Reactor
public ConfigEntry<bool> ModdedHandshake;
private GameObject _gameObject;
private RegionInfoWatcher RegionInfoWatcher { get; } = new RegionInfoWatcher();
public override void Load()
@@ -32,9 +33,9 @@ namespace Reactor
ModdedHandshake = Config.Bind("Features", "Modded handshake", true);
var gameObject = new GameObject(nameof(ReactorPlugin)).DontDestroy();
gameObject.AddComponent<ReactorComponent>().Plugin = this;
gameObject.AddComponent<Coroutines.Component>();
_gameObject = new GameObject(nameof(ReactorPlugin)).DontDestroy();
_gameObject.AddComponent<ReactorComponent>().Plugin = this;
_gameObject.AddComponent<Coroutines.Component>();
Harmony.PatchAll();
ReactorVersionShower.Initialize();
@@ -43,6 +44,8 @@ namespace Reactor
public override bool Unload()
{
Harmony.UnpatchSelf();
_gameObject.Destroy();
RegionInfoWatcher?.Dispose();
return base.Unload();
+54 -13
View File
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using UnhollowerRuntimeLib;
@@ -16,24 +17,64 @@ namespace Reactor
Register(Assembly.GetCallingAssembly());
}
private static readonly AccessTools.FieldRef<object, HashSet<string>> _injectedTypes
= AccessTools.FieldRefAccess<HashSet<string>>(typeof(ClassInjector), "InjectedTypes");
private static readonly Func<Type, IntPtr> _readClassPointerForType = AccessTools.MethodDelegate<Func<Type, IntPtr>>(
AccessTools.Method(typeof(ClassInjector), "ReadClassPointerForType")
);
private static bool IsInjected(Type type)
{
if (_readClassPointerForType(type) != IntPtr.Zero)
{
return true;
}
var injectedTypes = _injectedTypes();
lock (injectedTypes)
{
if (injectedTypes.Contains(type.FullName))
{
return true;
}
}
return false;
}
private static void Register(Type type)
{
if (type.BaseType?.GetCustomAttribute<RegisterInIl2CppAttribute>() != null)
{
Register(type.BaseType);
}
if (IsInjected(type))
{
return;
}
try
{
typeof(ClassInjector).GetMethod(nameof(ClassInjector.RegisterTypeInIl2Cpp))!
.MakeGenericMethod(type)
.Invoke(null, new object[0]);
}
catch (Exception e)
{
PluginSingleton<ReactorPlugin>.Instance.Log.LogWarning($"Failed to register {type.FullDescription()}: {e}");
}
}
public static void Register(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterInIl2CppAttribute>();
if (attribute != null)
if (type.GetCustomAttribute<RegisterInIl2CppAttribute>() != null)
{
try
{
typeof(ClassInjector).GetMethod(nameof(ClassInjector.RegisterTypeInIl2Cpp))!
.MakeGenericMethod(type)
.Invoke(null, new object[0]);
}
catch (Exception e)
{
PluginSingleton<ReactorPlugin>.Instance.Log.LogWarning($"Failed to register {type.FullDescription()}: {e}");
}
Register(type);
}
}
}