mirror of
https://github.com/NuclearPowered/Reactor.git
synced 2026-10-19 15:51:54 +00:00
Make CustomRpc api more generic, PlayerColor enum, bug fixes in RegisterInIl2CppAttribute
This commit is contained in:
12 files changed
+230
-84
No files matched your search
+1
-1
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Mappings>NuclearPowered/Mappings:0.1.0</Mappings>
|
||||
<Mappings>NuclearPowered/Mappings:0.1.1</Mappings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Reactor
|
||||
{
|
||||
public enum PlayerColor : byte
|
||||
{
|
||||
Red,
|
||||
Blue,
|
||||
Green,
|
||||
Pink,
|
||||
Orange,
|
||||
Yellow,
|
||||
Black,
|
||||
White,
|
||||
Purple,
|
||||
Brown,
|
||||
Cyan,
|
||||
Lime
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user