Refactor (#62)

Co-authored-by: js6pak <kubastaron@hotmail.com>
This commit is contained in:
AlexejheroYTBandjs6pak authored and GitHub committed 2022-10-06 18:47:40 +00:00
1 parent 5cc08363c7
commit b7971d36a8
66 files changed
+649 -615

No files matched your search

+1
View File
@@ -6,3 +6,4 @@ riderModule.iml
.idea
/tools
.vscode
*.DotSettings.user
+2 -1
View File
@@ -6,7 +6,8 @@ using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BepInEx;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Reactor.Extensions;
using Reactor.Utilities;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace Reactor.Benchmarks;
+2 -1
View File
@@ -6,7 +6,8 @@ using HarmonyLib;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using InnerNet;
using Reactor.Extensions;
using Reactor.Utilities.Attributes;
using Reactor.Utilities.ImGui;
using UnityEngine;
namespace Reactor.Debugger;
+1
View File
@@ -1,6 +1,7 @@
using System.Linq;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Reactor.Utilities;
using UnityEngine;
namespace Reactor.Debugger;
+5 -2
View File
@@ -2,9 +2,12 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Il2CppInterop.Runtime.Attributes;
using Reactor.Extensions;
using Reactor.Networking;
using Reactor.Networking.MethodRpc;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
using Reactor.Utilities.Attributes;
using Reactor.Utilities.ImGui;
using UnityEngine;
using Random = UnityEngine.Random;
+2 -1
View File
@@ -1,5 +1,6 @@
using Hazel;
using Reactor.Networking;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
namespace Reactor.Example;
+7
View File
@@ -7,6 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.Example", "Reactor.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.Benchmarks", "Reactor.Benchmarks\Reactor.Benchmarks.csproj", "{99B87307-3AEB-4A6A-BF45-871768308FDD}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{4BBF0A80-F6E6-4FDD-ADD0-8CB4F28F88A4}"
ProjectSection(SolutionItems) = preProject
AmongUs.props = AmongUs.props
Directory.Build.props = Directory.Build.props
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.Networking.Shared", "Reactor.Networking.Shared\Reactor.Networking.Shared.csproj", "{1E68CA20-22EB-4E57-B525-81970BE6363D}"
EndProject
Global
+12
View File
@@ -0,0 +1,12 @@
// ReSharper disable CheckNamespace
namespace System.Runtime.CompilerServices;
/// <summary>
/// This attribute is required by the compiler for the "unmanaged" generic type constraint.
/// It should be generated automatically, but for whatever reason its not.
/// </summary>
[Obsolete("Do not use directly.", true)]
internal class IsUnmanagedAttribute : Attribute
{
}
-128
View File
@@ -1,128 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using Il2CppType = Il2CppInterop.Runtime.Il2CppType;
namespace Reactor.Extensions;
/// <summary>
/// General utilities
/// </summary>
public static class Extensions
{
/// <summary>
/// Adds a delegate to get one notification when a scene has loaded.
/// </summary>
public static void once_sceneLoaded(Action<Scene, LoadSceneMode> value)
{
UnityAction<Scene, LoadSceneMode>? unityAction = null;
unityAction = (Action<Scene, LoadSceneMode>) ((scene, loadMode) =>
{
SceneManager.remove_sceneLoaded(unityAction);
value.Invoke(scene, loadMode);
});
SceneManager.add_sceneLoaded(unityAction);
}
/// <summary>
/// Returns random <typeparamref name="T"/> from <paramref name="input"/>
/// </summary>
public static T? Random<T>(this IEnumerable<T> input)
{
var list = input as IList<T> ?? input.ToList();
return list.Count == 0 ? default : list[UnityEngine.Random.Range(0, list.Count)];
}
/// <summary>
/// Returns the color as a hexadecimal string in the format "RRGGBBAA".
/// </summary>
/// <param name="color">The color to be converted.</param>
/// <returns>Hexadecimal string representing the color.</returns>
/// <remarks>https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html</remarks>
public static string ToHtmlStringRGBA(this Color32 color)
{
return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}";
}
/// <inheritdoc cref="ToHtmlStringRGBA(UnityEngine.Color32)"/>
public static string ToHtmlStringRGBA(this Color color)
{
return ((Color32) color).ToHtmlStringRGBA();
}
/// <summary>
/// Fully read <paramref name="input"/> stream, can be used as workaround for il2cpp streams.
/// </summary>
public static byte[] ReadFully(this Stream input)
{
using (var ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
/// <summary>
/// Sends <see cref="UnityWebRequest"/> and return task that finishes on <paramref name="request"/> completion.
/// </summary>
public static Task SendAsync(this UnityWebRequest request)
{
var task = new TaskCompletionSource<object?>();
request.Send().m_completeCallback = (Action<AsyncOperation>) (_ =>
{
task.SetResult(null);
});
return task.Task;
}
private static readonly int _outline = Shader.PropertyToID("_Outline");
private static readonly int _outlineColor = Shader.PropertyToID("_OutlineColor");
private static readonly int _addColor = Shader.PropertyToID("_AddColor");
/// <summary>
/// Sets color outline for renderers using default Among Us shader
/// </summary>
public static void SetOutline(this Renderer renderer, Color? color)
{
renderer.material.SetFloat(_outline, color.HasValue ? 1 : 0);
if (color.HasValue)
{
renderer.material.SetColor(_outlineColor, color.Value);
renderer.material.SetColor(_addColor, color.Value);
}
}
public static IEnumerable<MethodBase> GetMethods(this Type type, BindingFlags bindingAttr, Type returnType, params Type[] parameterTypes)
{
return type.GetMethods(bindingAttr).Where(x => x.ReturnType == returnType && x.GetParameters().Select(x => x.ParameterType).SequenceEqual(parameterTypes));
}
public static IEnumerable<MethodBase> GetMethods(this Type type, Type returnType, params Type[] parameterTypes)
{
return type.GetMethods(AccessTools.all, returnType, parameterTypes);
}
public static T? LoadAsset<T>(this AssetBundle assetBundle, string name) where T : UnityEngine.Object
{
return assetBundle.LoadAsset(name, Il2CppType.Of<T>())?.Cast<T>();
}
public static unsafe Span<T> ToSpan<T>(this Il2CppStructArray<T> array) where T : unmanaged
{
return new Span<T>(IntPtr.Add(array.Pointer, IntPtr.Size * 4).ToPointer(), array.Length);
}
}
-147
View File
@@ -1,147 +0,0 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Reactor.Extensions;
/// <summary>
/// GUI utilities
/// </summary>
public static class GUIExtensions
{
/// <summary>
/// Shortcut for empty texture
/// </summary>
public static Texture2D CreateEmptyTexture(int width = 0, int height = 0)
{
return new Texture2D(width, height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
}
/// <summary>
/// Clamp Rect to screen size
/// </summary>
public static Rect ClampScreen(this Rect rect)
{
rect.x = Mathf.Clamp(rect.x, 0, Screen.width - rect.width);
rect.y = Mathf.Clamp(rect.y, 0, Screen.height - rect.height);
return rect;
}
/// <summary>
/// Reset Rect size
/// </summary>
public static Rect ResetSize(this Rect rect)
{
rect.width = rect.height = 0;
return rect;
}
/// <summary>
/// Create <see cref="Sprite"/> from <paramref name="tex"/>
/// </summary>
public static Sprite CreateSprite(this Texture2D tex)
{
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100f);
}
public static DefaultControls.Resources StandardResources { get; internal set; } = null!;
public static GameObject CreateCanvas()
{
var gameObject = new GameObject("Canvas");
var canvas = gameObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
var canvasScaler = gameObject.AddComponent<CanvasScaler>();
canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
canvasScaler.referencePixelsPerUnit = 100f;
gameObject.AddComponent<GraphicRaycaster>();
return gameObject;
}
public static GameObject CreateEventSystem()
{
var gameObject = new GameObject("EventSystem");
gameObject.AddComponent<EventSystem>();
gameObject.AddComponent<StandaloneInputModule>();
gameObject.AddComponent<BaseInput>();
return gameObject;
}
public static void SetSize(this RectTransform rectTransform, float width, float height)
{
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
}
}
public class Window
{
private static int _lastWindowId;
public static int NextWindowId()
{
return _lastWindowId++;
}
public int Id { get; set; } = NextWindowId();
public bool Enabled { get; set; } = true;
public Rect Rect { get; set; }
public Action<int> Func { get; set; }
public string Title { get; set; }
public Window(Rect rect, string title, Action<int> func)
{
Rect = rect;
Title = title;
Func = func;
}
public Window(Rect rect, string title, Action func) : this(rect, title, id => func())
{
}
public void OnGUI()
{
if (Enabled)
{
if (Event.current.type == EventType.Layout)
{
Rect = Rect.ResetSize();
}
Rect = GUILayout.Window(Id, Rect, Func, Title);
if ((Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) && Rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)))
{
Input.ResetInputAxes();
}
}
}
}
public class DragWindow : Window
{
public DragWindow(Rect rect, string title, Action<int> func) : base(rect, title, func)
{
Func = id =>
{
func(id);
GUI.DragWindow(new Rect(0, 0, 10000, 20));
Rect = Rect.ClampScreen();
};
}
public DragWindow(Rect rect, string title, Action func) : this(rect, title, id => func())
{
}
}
-92
View File
@@ -1,92 +0,0 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
namespace Reactor.Extensions;
[RegisterInIl2Cpp]
public class StreamWrapper : Il2CppSystem.IO.Stream
{
private readonly Stream _stream;
#pragma warning disable 8618
public StreamWrapper(IntPtr ptr) : base(ptr) { }
#pragma warning restore 8618
public StreamWrapper(Stream stream) : base(ClassInjector.DerivedConstructorPointer<StreamWrapper>())
{
ClassInjector.DerivedConstructorBody(this);
_stream = stream;
}
[HideFromIl2Cpp]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe Span<byte> GetSpan(Il2CppStructArray<byte> buffer, int offset, int count)
{
var rawBuffer = (byte*) buffer.Pointer + 4 * IntPtr.Size;
return new Span<byte>(rawBuffer + offset, count);
}
public override int Read(Il2CppStructArray<byte> buffer, int offset, int count)
{
return _stream.Read(GetSpan(buffer, offset, count));
}
public override void Write(Il2CppStructArray<byte> buffer, int offset, int count)
{
_stream.Write(GetSpan(buffer, offset, count));
}
public override void Flush()
{
_stream.Flush();
}
public override void Close()
{
_stream.Close();
}
public override void Dispose()
{
_stream.Dispose();
}
public override long Seek(long offset, Il2CppSystem.IO.SeekOrigin origin)
{
return _stream.Seek(offset, origin switch
{
Il2CppSystem.IO.SeekOrigin.Begin => SeekOrigin.Begin,
Il2CppSystem.IO.SeekOrigin.Current => SeekOrigin.Current,
Il2CppSystem.IO.SeekOrigin.End => SeekOrigin.End,
_ => throw new ArgumentOutOfRangeException(nameof(origin), origin, null),
});
}
public override void SetLength(long value)
{
_stream.SetLength(value);
}
public override bool CanRead => _stream.CanRead;
public override bool CanSeek => _stream.CanSeek;
public override bool CanWrite => _stream.CanWrite;
public override long Length => _stream.Length;
public override long Position
{
get => _stream.Position;
set => _stream.Position = value;
}
}
public static class StreamExtensions
{
/// <summary>
/// Wraps a System Stream into an Il2Cpp Stream
/// </summary>
public static StreamWrapper AsIl2Cpp(this Stream stream) => new(stream);
}
@@ -3,7 +3,7 @@ using System.Linq;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
namespace Reactor;
namespace Reactor.Localization;
public class CustomStringName
{
@@ -1,15 +1,22 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Reactor.Networking.Serialization;
namespace Reactor.Networking.Serialization;
namespace Reactor.Networking.Attributes;
[AttributeUsage(AttributeTargets.Class)]
public class MessageConverterAttribute : Attribute
{
private static readonly HashSet<Assembly> _registeredAssemblies = new();
public static void Register(Assembly assembly)
{
if (_registeredAssemblies.Contains(assembly)) return;
_registeredAssemblies.Add(assembly);
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<MessageConverterAttribute>();
@@ -1,15 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Hazel;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
namespace Reactor.Networking.MethodRpc;
namespace Reactor.Networking.Attributes;
[AttributeUsage(AttributeTargets.Method)]
public class MethodRpcAttribute : Attribute
{
private static readonly HashSet<Assembly> _registeredAssemblies = new();
public uint Id { get; }
public SendOption Option { get; set; } = SendOption.Reliable;
public RpcLocalHandling LocalHandling { get; set; } = RpcLocalHandling.Before;
@@ -22,6 +27,9 @@ public class MethodRpcAttribute : Attribute
public static void Register(Assembly assembly, BasePlugin plugin)
{
if (_registeredAssemblies.Contains(assembly)) return;
_registeredAssemblies.Add(assembly);
var methods = assembly.GetTypes()
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
@@ -1,9 +1,8 @@
using System;
using System.Linq;
using System.Reflection;
using Reactor.Networking;
namespace Reactor;
namespace Reactor.Networking.Attributes;
[AttributeUsage(AttributeTargets.Class)]
public class ReactorModFlagsAttribute : Attribute
@@ -24,7 +23,7 @@ public class ReactorModFlagsAttribute : Attribute
}
var metadataAttribute = type.Assembly.GetCustomAttributes<AssemblyMetadataAttribute>().SingleOrDefault(x => x.Key == "Reactor.ModFlags");
if (metadataAttribute != null && metadataAttribute.Value != null)
if (metadataAttribute is { Value: not null })
{
return Enum.Parse<ModFlags>(metadataAttribute.Value);
}
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Reactor.Networking;
using Reactor.Networking.Rpc;
using Reactor.Utilities;
namespace Reactor;
namespace Reactor.Networking.Attributes;
/// <summary>
/// Utility attribute for automatically registering CustomRpc
@@ -12,6 +14,8 @@ namespace Reactor;
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomRpcAttribute : Attribute
{
private static readonly HashSet<Assembly> _registeredAssemblies = new();
public uint Id { get; }
public RegisterCustomRpcAttribute(uint id)
@@ -19,8 +23,16 @@ public class RegisterCustomRpcAttribute : Attribute
Id = id;
}
/// <summary>
/// Registers all types marked with <see cref="RegisterCustomRpcAttribute"/> from the specified assembly as custom rpcs
/// </summary>
/// <param name="assembly"></param>
/// <param name="plugin"></param>
public static void Register(Assembly assembly, BasePlugin plugin)
{
if (_registeredAssemblies.Contains(assembly)) return;
_registeredAssemblies.Add(assembly);
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterCustomRpcAttribute>();
@@ -1,9 +1,9 @@
using Hazel;
using InnerNet;
namespace Reactor.Networking;
namespace Reactor.Networking.Extensions;
public static class Extensions
public static class ClientExtensions
{
public static void KickWithReason(this InnerNetClient innerNetClient, int targetClientId, string reason)
{
@@ -3,7 +3,7 @@ using Hazel;
using Hazel.Udp;
using UnityEngine;
namespace Reactor.Extensions;
namespace Reactor.Networking.Extensions;
public static class ExtraMessageExtensions
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using Hazel;
namespace Reactor.Networking;
namespace Reactor.Networking.Messages;
internal static class ModdedHandshakeC2S
{
@@ -1,6 +1,6 @@
using Hazel;
namespace Reactor.Networking;
namespace Reactor.Networking.Messages;
internal static class ModdedHandshakeS2C
{
@@ -1,8 +1,8 @@
using Hazel;
namespace Reactor.Networking;
namespace Reactor.Networking.Messages;
public static class ReactorHeader
internal static class ReactorHeader
{
private const ulong MAGIC = 0x72656163746f72; // "reactor" in ascii, 7 bytes
+15 -14
View File
@@ -5,6 +5,7 @@ using System.Text;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using Hazel;
using Reactor.Networking.Attributes;
using Reactor.Networking.Patches;
namespace Reactor.Networking;
@@ -13,31 +14,31 @@ public static class ModList
{
public static IReadOnlyCollection<Mod> Current { get; private set; } = null!;
private static readonly Dictionary<string, Mod> _mapById = new();
private static readonly Dictionary<Type, Mod> _mapByPluginType = new();
private static readonly Dictionary<string, Mod> _modById = new();
private static readonly Dictionary<Type, Mod> _modByPluginType = new();
public static Mod GetById(string id)
{
return _mapById[id];
return _modById[id];
}
internal static Mod GetByPluginType(Type pluginType)
{
return _mapByPluginType[pluginType];
return _modByPluginType[pluginType];
}
private static readonly Dictionary<uint, Mod> _mapByNetId = new();
private static readonly Dictionary<Mod, uint> _netIdMap = new();
private static readonly Dictionary<uint, Mod> _modByNetId = new();
private static readonly Dictionary<Mod, uint> _netIdByMod = new();
internal static Mod GetByNetId(uint netId)
{
return _mapByNetId[netId];
return _modByNetId[netId];
}
internal static uint GetNetId(this Mod mod)
{
if (!mod.IsRequiredOnAllClients) throw new ArgumentException("Cannot get a net id for a mod without RequireOnAllClients flag", nameof(mod));
return _netIdMap[mod];
return _netIdByMod[mod];
}
public static Mod ReadMod(this MessageReader reader)
@@ -75,16 +76,16 @@ public static class ModList
.ThenBy(x => x.Id, StringComparer.Ordinal)
.ToHashSet();
_mapByNetId.Clear();
_netIdMap.Clear();
_modByNetId.Clear();
_netIdByMod.Clear();
uint netId = 1;
foreach (var mod in Current)
{
if (!mod.IsRequiredOnAllClients) continue;
_mapByNetId[netId] = mod;
_netIdMap[mod] = netId;
_modByNetId[netId] = mod;
_netIdByMod[mod] = netId;
netId++;
}
@@ -110,8 +111,8 @@ public static class ModList
pluginInfo.Metadata.Name
);
_mapById[mod.Id] = mod;
_mapByPluginType[pluginType] = mod;
_modById[mod.Id] = mod;
_modByPluginType[pluginType] = mod;
}
internal static void Initialize()
+3 -4
View File
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using HarmonyLib;
using Hazel;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using InnerNet;
using Reactor.Networking.Extensions;
using Reactor.Networking.Messages;
using Reactor.Networking.Serialization;
using UnityEngine;
namespace Reactor.Networking.Patches;
@@ -3,7 +3,7 @@ using BepInEx.Unity.IL2CPP;
using Hazel;
using InnerNet;
namespace Reactor.Networking;
namespace Reactor.Networking.Rpc;
public abstract class CustomRpc<TPlugin, TInnerNetObject, TData> : UnsafeCustomRpc where TPlugin : BasePlugin where TInnerNetObject : InnerNetObject
{
@@ -6,22 +6,16 @@ using HarmonyLib;
using Hazel;
using Il2CppInterop.Generator.Extensions;
using InnerNet;
using Reactor.Utilities;
namespace Reactor.Networking;
public enum RpcLocalHandling
{
None,
Before,
After,
}
namespace Reactor.Networking.Rpc;
public class CustomRpcManager
{
public const byte CallId = byte.MaxValue;
private readonly List<UnsafeCustomRpc> _list = new();
private readonly Dictionary<Type, Dictionary<Mod, Dictionary<uint, UnsafeCustomRpc>>> _map = new();
internal readonly Dictionary<Type, Dictionary<Mod, Dictionary<uint, UnsafeCustomRpc>>> _map = new();
public IReadOnlyList<UnsafeCustomRpc> List => _list.AsReadOnly();
@@ -29,8 +23,8 @@ public class CustomRpcManager
{
customRpc.Manager = this;
_list.Add(customRpc);
_map.GetOrCreate(customRpc.InnerNetObjectType, _ => new Dictionary<Mod, Dictionary<uint, UnsafeCustomRpc>>())
.GetOrCreate(customRpc.Mod, _ => new Dictionary<uint, UnsafeCustomRpc>())
_map.GetOrCreate(customRpc.InnerNetObjectType, static _ => new Dictionary<Mod, Dictionary<uint, UnsafeCustomRpc>>())
.GetOrCreate(customRpc.Mod, static _ => new Dictionary<uint, UnsafeCustomRpc>())
.Add(customRpc.Id, customRpc);
if (customRpc.IsSingleton)
@@ -41,22 +35,6 @@ public class CustomRpcManager
return customRpc;
}
[HarmonyPatch]
private static class HandleRpcPatch
{
internal static List<Type> InnerNetObjectTypes { get; } = typeof(InnerNetObject).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(InnerNetObject)) && x != typeof(LobbyBehaviour)).ToList();
public static IEnumerable<MethodBase> TargetMethods()
{
return InnerNetObjectTypes.Select(x => x.GetMethod(nameof(InnerNetObject.HandleRpc), AccessTools.allDeclared)).Where(m => m != null)!;
}
public static bool Prefix(InnerNetObject __instance, [HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader)
{
return !HandleRpc(__instance, callId, reader);
}
}
public static bool HandleRpc(InnerNetObject innerNetObject, byte callId, MessageReader reader)
{
if (callId == CallId)
@@ -76,32 +54,20 @@ public class CustomRpcManager
return false;
}
[HarmonyPatch]
internal static class HandleRpcPatch
{
private static List<Type> InnerNetObjectTypes { get; } = typeof(InnerNetObject).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(InnerNetObject)) && x != typeof(LobbyBehaviour)).ToList();
public static IEnumerable<MethodBase> TargetMethods()
{
return InnerNetObjectTypes.Select(x => x.GetMethod(nameof(InnerNetObject.HandleRpc), AccessTools.allDeclared)).Where(m => m != null)!;
}
public static class Rpc<T> where T : UnsafeCustomRpc
public static bool Prefix(InnerNetObject __instance, [HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader)
{
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;
return !HandleRpc(__instance, callId, reader);
}
}
}
@@ -11,8 +11,9 @@ using InnerNet;
using MonoMod.RuntimeDetour;
using MonoMod.Utils;
using Reactor.Networking.Serialization;
using Reactor.Utilities;
namespace Reactor.Networking.MethodRpc;
namespace Reactor.Networking.Rpc;
public class MethodRpc : UnsafeCustomRpc
{
@@ -1,7 +1,7 @@
using System;
using BepInEx.Unity.IL2CPP;
namespace Reactor.Networking;
namespace Reactor.Networking.Rpc;
public abstract class PlayerCustomRpc<TPlugin, TData> : CustomRpc<TPlugin, PlayerControl, TData> where TPlugin : BasePlugin
{
+31
View File
@@ -0,0 +1,31 @@
using System;
namespace Reactor.Networking.Rpc;
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;
}
}
}
@@ -0,0 +1,8 @@
namespace Reactor.Networking.Rpc;
public enum RpcLocalHandling
{
None,
Before,
After,
}
@@ -6,9 +6,10 @@ using Hazel;
using Hazel.Udp;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using InnerNet;
using Reactor.Utilities;
using Buffer = Il2CppSystem.Buffer;
namespace Reactor.Networking;
namespace Reactor.Networking.Rpc;
public abstract class UnsafeCustomRpc
{
@@ -4,6 +4,7 @@ using System.Reflection;
using HarmonyLib;
using Hazel;
using InnerNet;
using Reactor.Networking.Attributes;
namespace Reactor.Networking.Serialization;
@@ -2,7 +2,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Hazel;
using Reactor.Extensions;
using Reactor.Networking.Extensions;
using Reactor.Networking.Rpc;
using UnityEngine;
namespace Reactor.Networking.Serialization;
@@ -83,7 +84,7 @@ public static class MessageSerializer
}
}
public static object[] Deserialize(MessageReader reader, MethodRpc.MethodRpc methodRpc)
public static object[] Deserialize(MessageReader reader, MethodRpc methodRpc)
{
var parameters = methodRpc.Method.GetParameters();
var args = new object[parameters.Length - 1];
-17
View File
@@ -1,17 +0,0 @@
using HarmonyLib;
using InnerNet;
namespace Reactor.Patches;
internal static class BetterGuestPatch
{
[HarmonyPatch(typeof(SaveManager), nameof(SaveManager.ChatModeType), MethodType.Getter)]
public static class ChatModeTypePatch
{
public static bool Prefix(out QuickChatModes __result)
{
__result = QuickChatModes.FreeChatOrQuickChat;
return false;
}
}
}
@@ -3,7 +3,7 @@ using HarmonyLib;
using TMPro;
using UnityEngine;
namespace Reactor.Patches;
namespace Reactor.Patches.Fixes;
/// <summary>
/// "Fixes" an issue where empty TextBoxes have wrong cursor positions
@@ -5,24 +5,24 @@ using System.Net.Sockets;
using HarmonyLib;
using InnerNet;
namespace Reactor.Patches;
namespace Reactor.Patches.Fixes;
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Connect))]
public static class ConnectPatch
[HarmonyPatch]
internal static class ServerInfoFixesPatch
{
public static void Prefix(InnerNetClient __instance)
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Connect))]
[HarmonyPrefix]
public static void LogConnect(InnerNetClient __instance)
{
Info($"Joining {__instance.networkAddress}:{__instance.networkPort}");
}
}
/// <summary>
/// Fixes hardcoded port and filters out IPv6 in DnsRegionInfo
/// </summary>
[HarmonyPatch(typeof(DnsRegionInfo), nameof(DnsRegionInfo.PopulateServers))]
public static class PopulateServersPatch
{
public static bool Prefix(DnsRegionInfo __instance)
[HarmonyPrefix]
public static bool FixPopulateServers(DnsRegionInfo __instance)
{
try
{
@@ -1,6 +1,6 @@
using HarmonyLib;
namespace Reactor.Patches;
namespace Reactor.Patches.Miscellaneous;
[HarmonyPatch]
internal static class CustomServersPatch
@@ -8,7 +8,7 @@ internal static class CustomServersPatch
[HarmonyPatch(typeof(AuthManager._CoConnect_d__4), nameof(AuthManager._CoConnect_d__4.MoveNext))]
[HarmonyPatch(typeof(AuthManager._CoWaitForNonce_d__6), nameof(AuthManager._CoWaitForNonce_d__6.MoveNext))]
[HarmonyPrefix]
public static bool DisableAuthServerPatch(out bool __result)
public static bool DisableAuthServer(out bool __result)
{
__result = false;
return false;
@@ -16,7 +16,7 @@ internal static class CustomServersPatch
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__1), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__1.MoveNext))]
[HarmonyPrefix]
public static void EnableUdpMatchmakingPatch(AmongUsClient._CoJoinOnlinePublicGame_d__1 __instance)
public static void EnableUdpMatchmaking(AmongUsClient._CoJoinOnlinePublicGame_d__1 __instance)
{
// Skip to state 1 which just calls CoJoinOnlineGameDirect
if (__instance.__1__state == 0 && !ServerManager.Instance.IsHttp)
@@ -7,18 +7,17 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace Reactor.Patches;
namespace Reactor.Patches.Miscellaneous;
public static class FreeNamePatch
[HarmonyPatch]
internal static class FreeNamePatch
{
[HarmonyPatch(typeof(AccountManager), nameof(AccountManager.CheckAndRegenerateName))]
internal static class ValidateRandomNamePatch
{
public static bool Prefix()
[HarmonyPrefix]
public static bool DontRegenerateNames()
{
return false;
}
}
public static void Initialize()
{
@@ -1,24 +1,24 @@
using System.Reflection;
using HarmonyLib;
namespace Reactor.Patches;
namespace Reactor.Patches.Miscellaneous;
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Start))]
internal static class SplashSkip
{
private static void Prefix(SplashManager __instance)
{
__instance.minimumSecondsBeforeSceneChange = 0;
}
}
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Update))]
internal static class WaitForEpicAuth
[HarmonyPatch]
internal static class SplashSkipPatch
{
private static readonly PropertyInfo? _localUserIdProperty = typeof(EpicManager).GetProperty("localUserId", BindingFlags.Static | BindingFlags.Public);
private static bool _loginFinished;
private static bool Prefix(SplashManager __instance)
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Start))]
[HarmonyPrefix]
private static void RemoveMinimumWait(SplashManager __instance)
{
__instance.minimumSecondsBeforeSceneChange = 0;
}
[HarmonyPatch(typeof(SplashManager), nameof(SplashManager.Update))]
[HarmonyPrefix]
private static bool WaitForLogin(SplashManager __instance)
{
if (__instance.startedSceneLoad) return true;
@@ -32,11 +32,9 @@ internal static class WaitForEpicAuth
// EpicManager calls SaveManager.LoadPlayerPrefs(true) both on successful and unsuccessful EOS login
[HarmonyPatch(typeof(SaveManager), nameof(SaveManager.LoadPlayerPrefs))]
private static class LoadPlayerPrefsPatch
{
private static void Postfix(bool overrideLoad)
[HarmonyPostfix]
private static void WaitForEpicAuth(bool overrideLoad)
{
if (overrideLoad) _loginFinished = true;
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.IO;
using System.Reflection;
using HarmonyLib;
namespace Reactor.Patches.Miscellaneous;
/// <summary>
/// Allows launching steam builds outside of steam
/// </summary>
[HarmonyPatch]
internal static class StandaloneSteamPatch
{
private static readonly Type? _type = Type.GetType("Steamworks.SteamAPI, Assembly-CSharp-firstpass", false);
[HarmonyPrepare]
public static bool Prepare()
{
return _type != null;
}
[HarmonyTargetMethod]
public static MethodBase TargetMethod()
{
return AccessTools.Method(_type, "RestartAppIfNecessary");
}
public static bool Prefix(out bool __result)
{
const string file = "steam_appid.txt";
if (!File.Exists(file))
{
File.WriteAllText(file, "945360");
}
return __result = false;
}
}
-25
View File
@@ -1,25 +0,0 @@
using System.Linq;
using HarmonyLib;
namespace Reactor.Patches;
internal static class PlayerIdPatch
{
[HarmonyPatch(typeof(GameData), nameof(GameData.GetAvailableId))]
public static class GetAvailableIdPatch
{
public static bool Prefix(GameData __instance, out sbyte __result)
{
sbyte i = 0;
while (__instance.AllPlayers.ToArray().Any(p => p.PlayerId == i))
{
i++;
}
__result = i;
return false;
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ using System;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Reactor.Extensions;
using Reactor.Utilities.Extensions;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
-38
View File
@@ -1,38 +0,0 @@
using System;
using System.IO;
using System.Reflection;
using HarmonyLib;
namespace Reactor.Patches;
internal static class SteamPatch
{
[HarmonyPatch]
public static class RestartAppIfNecessaryPatch
{
public const string TypeName = "Steamworks.SteamAPI, Assembly-CSharp-firstpass";
public const string MethodName = "RestartAppIfNecessary";
public static bool Prepare()
{
return Type.GetType(TypeName, false) != null;
}
public static MethodBase TargetMethod()
{
return AccessTools.Method(TypeName + ":" + MethodName);
}
public static bool Prefix(out bool __result)
{
const string file = "steam_appid.txt";
if (!File.Exists(file))
{
File.WriteAllText(file, "945360");
}
return __result = false;
}
}
}
+1 -1
View File
@@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<Compile Include="..\Reactor.Networking.Shared\**\*.cs" LinkBase="Reactor.Networking.Shared" Visible="false" />
<Compile Include="..\Reactor.Networking.Shared\**\*.cs" LinkBase="Networking" Visible="false" />
</ItemGroup>
<ItemGroup>
+8 -7
View File
@@ -1,17 +1,18 @@
global using static Reactor.Logger<Reactor.ReactorPlugin>;
global using static Reactor.Utilities.Logger<Reactor.ReactorPlugin>;
using System;
using System.Linq;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Reactor.Extensions;
using Reactor.Networking;
using Reactor.Networking.MethodRpc;
using Reactor.Networking.Serialization;
using Reactor.Patches;
using Reactor.Unstrip;
using Il2CppInterop.Runtime.Attributes;
using Reactor.Networking.Attributes;
using Reactor.Networking.Rpc;
using Reactor.Patches;
using Reactor.Patches.Miscellaneous;
using Reactor.Utilities;
using Reactor.Utilities.Attributes;
using UnityEngine;
using UnityEngine.SceneManagement;
@@ -65,7 +66,7 @@ public partial class ReactorPlugin : BasePlugin
public override bool Unload()
{
Harmony.UnpatchSelf();
RegionInfoWatcher?.Dispose();
RegionInfoWatcher.Dispose();
return base.Unload();
}
-7
View File
@@ -1,7 +0,0 @@
// ReSharper disable CheckNamespace
namespace System.Runtime.CompilerServices;
public class IsUnmanagedAttribute : Attribute
{
}
@@ -5,10 +5,10 @@ using System.Linq;
using System.Reflection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MonoMod.Utils;
using Reactor.Extensions;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace Reactor;
namespace Reactor.Utilities;
public static class AssetBundleManager
{
@@ -1,10 +1,11 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
namespace Reactor;
namespace Reactor.Utilities.Attributes;
/// <summary>
/// Utility attribute for automatically calling <see cref="ClassInjector.RegisterTypeInIl2Cpp{T}()"/>
@@ -12,6 +13,8 @@ namespace Reactor;
[AttributeUsage(AttributeTargets.Class)]
public class RegisterInIl2CppAttribute : Attribute
{
private static readonly HashSet<Assembly> _registeredAssemblies = new();
public Type[] Interfaces { get; }
public RegisterInIl2CppAttribute()
@@ -24,12 +27,12 @@ public class RegisterInIl2CppAttribute : Attribute
Interfaces = interfaces;
}
private static void Register(Type type, Type[] interfaces)
private static void RegisterType(Type type, Type[] interfaces)
{
var baseTypeAttribute = type.BaseType?.GetCustomAttribute<RegisterInIl2CppAttribute>();
if (baseTypeAttribute != null)
{
Register(type.BaseType!, baseTypeAttribute.Interfaces);
RegisterType(type.BaseType!, baseTypeAttribute.Interfaces);
}
if (ClassInjector.IsTypeRegisteredInIl2Cpp(type))
@@ -47,14 +50,21 @@ public class RegisterInIl2CppAttribute : Attribute
}
}
/// <summary>
/// Registers in Il2Cpp all types marked with <see cref="RegisterInIl2CppAttribute"/> from the specified assembly.<br/>
/// </summary>
/// <param name="assembly"></param>
public static void Register(Assembly assembly)
{
if (_registeredAssemblies.Contains(assembly)) return;
_registeredAssemblies.Add(assembly);
foreach (var type in assembly.GetTypes())
{
var attribute = type.GetCustomAttribute<RegisterInIl2CppAttribute>();
if (attribute != null)
{
Register(type, attribute.Interfaces);
RegisterType(type, attribute.Interfaces);
}
}
}
+43
View File
@@ -0,0 +1,43 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Reactor.Utilities;
public static class CanvasUtilities
{
public static GameObject CreateCanvas()
{
var gameObject = new GameObject("Canvas");
var canvas = gameObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
var canvasScaler = gameObject.AddComponent<CanvasScaler>();
canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
canvasScaler.referencePixelsPerUnit = 100f;
gameObject.AddComponent<GraphicRaycaster>();
return gameObject;
}
public static GameObject CreateEventSystem()
{
var gameObject = new GameObject("EventSystem");
gameObject.AddComponent<EventSystem>();
gameObject.AddComponent<StandaloneInputModule>();
gameObject.AddComponent<BaseInput>();
return gameObject;
}
/// <summary>
/// Shortcut for empty texture
/// </summary>
public static Texture2D CreateEmptyTexture(int width = 0, int height = 0)
{
return new Texture2D(width, height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
}
}
@@ -3,9 +3,10 @@ using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using BepInEx.Unity.IL2CPP.Utils;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace Reactor;
namespace Reactor.Utilities;
public static class Coroutines
{
@@ -1,12 +1,14 @@
using Reactor.Extensions;
using Reactor.Utilities.Extensions;
using UnityEngine;
using UnityEngine.UI;
namespace Reactor.Unstrip;
namespace Reactor.Utilities;
internal static class DefaultBundle
public static class DefaultBundle
{
public static void Load()
public static DefaultControls.Resources StandardResources { get; internal set; } = null!;
internal static void Load()
{
var bundle = AssetBundleManager.Load("default");
@@ -17,7 +19,7 @@ internal static class DefaultBundle
Graphic.defaultGraphicMaterial.shader = backupShader;
}
GUIExtensions.StandardResources = new DefaultControls.Resources
StandardResources = new DefaultControls.Resources
{
background = bundle.LoadAsset<Sprite>("Background")!.DontUnload(),
checkmark = bundle.LoadAsset<Sprite>("Checkmark")!.DontUnload(),
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using Il2CppInterop.Runtime.Attributes;
using Reactor.Utilities.Attributes;
using UnityEngine;
namespace Reactor.Extensions;
namespace Reactor.Utilities;
[RegisterInIl2Cpp]
public class Dispatcher : MonoBehaviour
@@ -0,0 +1,12 @@
using Il2CppInterop.Runtime;
using UnityEngine;
namespace Reactor.Utilities.Extensions;
public static class AssetBundleExtensions
{
public static T? LoadAsset<T>(this AssetBundle assetBundle, string name) where T : Object
{
return assetBundle.LoadAsset(name, Il2CppType.Of<T>())?.Cast<T>();
}
}
@@ -0,0 +1,44 @@
using UnityEngine;
namespace Reactor.Utilities.Extensions;
/// <summary>
/// GUI utilities
/// </summary>
public static class GUIExtensions
{
/// <summary>
/// Clamp Rect to screen size
/// </summary>
public static Rect ClampScreen(this Rect rect)
{
rect.x = Mathf.Clamp(rect.x, 0, Screen.width - rect.width);
rect.y = Mathf.Clamp(rect.y, 0, Screen.height - rect.height);
return rect;
}
/// <summary>
/// Reset Rect size
/// </summary>
public static Rect ResetSize(this Rect rect)
{
rect.width = rect.height = 0;
return rect;
}
/// <summary>
/// Create <see cref="Sprite"/> from <paramref name="tex"/>
/// </summary>
public static Sprite CreateSprite(this Texture2D tex)
{
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100f);
}
public static void SetSize(this RectTransform rectTransform, float width, float height)
{
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
}
}
@@ -0,0 +1,12 @@
using System;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
namespace Reactor.Utilities.Extensions;
public static class Il2CppInteropExtensions
{
public static unsafe Span<T> ToSpan<T>(this Il2CppStructArray<T> array) where T : unmanaged
{
return new Span<T>(IntPtr.Add(array.Pointer, IntPtr.Size * 4).ToPointer(), array.Length);
}
}
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Reactor.Extensions;
namespace Reactor.Utilities.Extensions;
public static class RandomExtensions
{
@@ -20,4 +20,13 @@ public static class RandomExtensions
{
return random.NextDouble() * (maxValue - minValue) + minValue;
}
/// <summary>
/// Returns random <typeparamref name="T"/> from <paramref name="input"/>
/// </summary>
public static T? Random<T>(this IEnumerable<T> input)
{
var list = input as IList<T> ?? input.ToList();
return list.Count == 0 ? default : list[UnityEngine.Random.Range(0, list.Count)];
}
}
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using HarmonyLib;
using Il2CppInterop.Common;
@@ -10,7 +12,7 @@ using MethodInfo = System.Reflection.MethodInfo;
using Il2CppMethodInfo = Il2CppSystem.Reflection.MethodInfo;
using Il2CppSystemType = Il2CppSystem.Type;
namespace Reactor.Extensions;
namespace Reactor.Utilities.Extensions;
public static class ReflectionExtensions
{
@@ -53,4 +55,14 @@ public static class ReflectionExtensions
{
return AccessTools.Method(AccessTools.Method(type, methodName).ToIl2CppMethodInfo().GetEnumeratorMoveNextType().ToSystemType(), "MoveNext");
}
public static IEnumerable<MethodBase> GetMethods(this Type type, BindingFlags bindingAttr, Type returnType, params Type[] parameterTypes)
{
return type.GetMethods(bindingAttr).Where(x => x.ReturnType == returnType && x.GetParameters().Select(x => x.ParameterType).SequenceEqual(parameterTypes));
}
public static IEnumerable<MethodBase> GetMethods(this Type type, Type returnType, params Type[] parameterTypes)
{
return type.GetMethods(AccessTools.all, returnType, parameterTypes);
}
}
@@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
using Il2CppInterop.Runtime.Attributes;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Reactor.Utilities.Attributes;
namespace Reactor.Utilities.Extensions;
public static class StreamExtensions
{
[RegisterInIl2Cpp]
public class StreamWrapper : Il2CppSystem.IO.Stream
{
private readonly Stream _stream;
#pragma warning disable 8618
public StreamWrapper(IntPtr ptr) : base(ptr) { }
#pragma warning restore 8618
public StreamWrapper(Stream stream) : base(ClassInjector.DerivedConstructorPointer<StreamWrapper>())
{
ClassInjector.DerivedConstructorBody(this);
_stream = stream;
}
[HideFromIl2Cpp]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe Span<byte> GetSpan(Il2CppStructArray<byte> buffer, int offset, int count)
{
var rawBuffer = (byte*) buffer.Pointer + 4 * IntPtr.Size;
return new Span<byte>(rawBuffer + offset, count);
}
public override int Read(Il2CppStructArray<byte> buffer, int offset, int count)
{
return _stream.Read(GetSpan(buffer, offset, count));
}
public override void Write(Il2CppStructArray<byte> buffer, int offset, int count)
{
_stream.Write(GetSpan(buffer, offset, count));
}
public override void Flush()
{
_stream.Flush();
}
public override void Close()
{
_stream.Close();
}
public override void Dispose()
{
_stream.Dispose();
}
public override long Seek(long offset, Il2CppSystem.IO.SeekOrigin origin)
{
return _stream.Seek(offset, origin switch
{
Il2CppSystem.IO.SeekOrigin.Begin => SeekOrigin.Begin,
Il2CppSystem.IO.SeekOrigin.Current => SeekOrigin.Current,
Il2CppSystem.IO.SeekOrigin.End => SeekOrigin.End,
_ => throw new ArgumentOutOfRangeException(nameof(origin), origin, null),
});
}
public override void SetLength(long value)
{
_stream.SetLength(value);
}
public override bool CanRead => _stream.CanRead;
public override bool CanSeek => _stream.CanSeek;
public override bool CanWrite => _stream.CanWrite;
public override long Length => _stream.Length;
public override long Position
{
get => _stream.Position;
set => _stream.Position = value;
}
}
/// <summary>
/// Wraps a System Stream into an Il2Cpp Stream
/// </summary>
public static StreamWrapper AsIl2Cpp(this Stream stream) => new(stream);
/// <summary>
/// Fully read <paramref name="input"/> stream, can be used as workaround for il2cpp streams.
/// </summary>
public static byte[] ReadFully(this Stream input)
{
using var ms = new MemoryStream();
input.CopyTo(ms);
return ms.ToArray();
}
}
@@ -0,0 +1,41 @@
using UnityEngine;
namespace Reactor.Utilities.Extensions;
public static class UnityExtensions
{
/// <summary>
/// Returns the color as a hexadecimal string in the format "RRGGBBAA".
/// </summary>
/// <param name="color">The color to be converted.</param>
/// <returns>Hexadecimal string representing the color.</returns>
/// <remarks>https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html</remarks>
public static string ToHtmlStringRGBA(this Color32 color)
{
return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}";
}
/// <inheritdoc cref="ToHtmlStringRGBA(UnityEngine.Color32)"/>
public static string ToHtmlStringRGBA(this Color color)
{
return ((Color32) color).ToHtmlStringRGBA();
}
private static readonly int _outline = Shader.PropertyToID("_Outline");
private static readonly int _outlineColor = Shader.PropertyToID("_OutlineColor");
private static readonly int _addColor = Shader.PropertyToID("_AddColor");
/// <summary>
/// Sets color outline for renderers using default Among Us shader
/// </summary>
public static void SetOutline(this Renderer renderer, Color? color)
{
renderer.material.SetFloat(_outline, color.HasValue ? 1 : 0);
if (color.HasValue)
{
renderer.material.SetColor(_outlineColor, color.Value);
renderer.material.SetColor(_addColor, color.Value);
}
}
}
@@ -1,6 +1,6 @@
using UnityEngine;
namespace Reactor.Extensions;
namespace Reactor.Utilities.Extensions;
public static class UnityObjectExtensions
{
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using Il2CppSystem.Runtime.CompilerServices;
namespace Reactor;
namespace Reactor.Utilities;
public sealed class Il2CppEqualityComparer<T> : IEqualityComparer<T> where T : Il2CppSystem.Object
{
+23
View File
@@ -0,0 +1,23 @@
using System;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace Reactor.Utilities.ImGui;
public class DragWindow : Window
{
public DragWindow(Rect rect, string title, Action<int> func) : base(rect, title, func)
{
Func = id =>
{
func(id);
UnityEngine.GUI.DragWindow(new Rect(0, 0, 10000, 20));
Rect = Rect.ClampScreen();
};
}
public DragWindow(Rect rect, string title, Action func) : this(rect, title, id => func())
{
}
}
+51
View File
@@ -0,0 +1,51 @@
using System;
using Reactor.Utilities.Extensions;
using UnityEngine;
namespace Reactor.Utilities.ImGui;
public class Window
{
private static int _lastWindowId;
public static int NextWindowId()
{
return _lastWindowId++;
}
public int Id { get; set; } = NextWindowId();
public bool Enabled { get; set; } = true;
public Rect Rect { get; set; }
public Action<int> Func { get; set; }
public string Title { get; set; }
public Window(Rect rect, string title, Action<int> func)
{
Rect = rect;
Title = title;
Func = func;
}
public Window(Rect rect, string title, Action func) : this(rect, title, id => func())
{
}
public void OnGUI()
{
if (Enabled)
{
if (Event.current.type == EventType.Layout)
{
Rect = Rect.ResetSize();
}
Rect = GUILayout.Window(Id, Rect, Func, Title);
if ((Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1)) && Rect.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)))
{
Input.ResetInputAxes();
}
}
}
}
@@ -1,9 +1,9 @@
using System.Runtime.CompilerServices;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Unity.IL2CPP;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
namespace Reactor;
namespace Reactor.Utilities;
public static class Logger<T> where T : BasePlugin
{
@@ -3,7 +3,7 @@ using System.Linq;
using System.Reflection;
using BepInEx.Unity.IL2CPP;
namespace Reactor;
namespace Reactor.Utilities;
public static class PluginSingleton<T> where T : BasePlugin
{
@@ -2,11 +2,10 @@ using System;
using System.IO;
using System.Reflection;
using HarmonyLib;
using Reactor.Extensions;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Reactor.Patches;
namespace Reactor.Utilities;
internal class RegionInfoWatcher : IDisposable
{
@@ -57,14 +56,16 @@ internal class RegionInfoWatcher : IDisposable
}
[HarmonyPatch]
private static class WritePatch
internal static class WriteAllTextPatch
{
[HarmonyTargetMethod]
public static MethodBase TargetMethod()
{
var type = Type.GetType("FileIO, Assembly-CSharp", false) ?? Type.GetType("Innersloth.IO.FileIO, Assembly-CSharp", true);
return AccessTools.Method(type, "WriteAllText");
}
[HarmonyPrefix]
public static bool Prefix(string path, string contents)
{
// Among Us' region loading code unfortunately contains a call
@@ -0,0 +1,24 @@
using System;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
namespace Reactor.Utilities;
public static class SceneManagerUtilities
{
/// <summary>
/// Adds a delegate to get one notification when a scene has loaded.
/// </summary>
public static void once_sceneLoaded(Action<Scene, LoadSceneMode> value)
{
UnityAction<Scene, LoadSceneMode>? unityAction = null;
unityAction = (Action<Scene, LoadSceneMode>) ((scene, loadMode) =>
{
SceneManager.remove_sceneLoaded(unityAction);
value.Invoke(scene, loadMode);
});
SceneManager.add_sceneLoaded(unityAction);
}
}