From b7971d36a8070044267d342730896ceb13a74cd4 Mon Sep 17 00:00:00 2001 From: AlexejheroYTB Date: Thu, 6 Oct 2022 21:47:40 +0300 Subject: [PATCH] Refactor (#62) Co-authored-by: js6pak --- .gitignore | 1 + Reactor.Benchmarks/AssetBundleBenchmarks.cs | 3 +- Reactor.Debugger/DebuggerPlugin.cs | 3 +- Reactor.Debugger/Patches.cs | 1 + Reactor.Example/ExamplePlugin.cs | 7 +- Reactor.Example/ExampleRpc.cs | 3 +- Reactor.sln | 7 + Reactor/Compiler/IsUnmanagedAttribute.cs | 12 ++ Reactor/Extensions/Extensions.cs | 128 --------------- Reactor/Extensions/GUIExtensions.cs | 147 ------------------ Reactor/Extensions/StreamExtensions.cs | 92 ----------- .../{ => Localization}/CustomStringName.cs | 2 +- .../MessageConverterAttribute.cs | 9 +- .../MethodRpcAttribute.cs | 10 +- .../Attributes}/ReactorModFlagsAttribute.cs | 5 +- .../Attributes}/RegisterCustomRpcAttribute.cs | 16 +- .../ClientExtensions.cs} | 4 +- .../Extensions/ExtraMessageExtensions.cs | 2 +- .../{ => Messages}/ModdedHandshakeC2S.cs | 2 +- .../{ => Messages}/ModdedHandshakeS2C.cs | 2 +- .../{ => Messages}/ReactorHeader.cs | 4 +- Reactor/Networking/ModList.cs | 29 ++-- Reactor/Networking/Patches/ClientPatches.cs | 7 +- Reactor/Networking/{ => Rpc}/CustomRpc.cs | 2 +- .../Networking/{ => Rpc}/CustomRpcManager.cs | 60 ++----- .../{MethodRpc => Rpc}/MethodRpc.cs | 3 +- .../Networking/{ => Rpc}/PlayerCustomRpc.cs | 2 +- Reactor/Networking/Rpc/Rpc.cs | 31 ++++ Reactor/Networking/Rpc/RpcLocalHandling.cs | 8 + .../Networking/{ => Rpc}/UnsafeCustomRpc.cs | 3 +- .../Serialization/InnerNetObjectConverter.cs | 1 + .../Serialization/MessageSerializer.cs | 5 +- Reactor/Patches/BetterGuestPatch.cs | 17 -- Reactor/Patches/{ => Fixes}/CursorPosPatch.cs | 2 +- .../ServerInfoFixesPatch.cs} | 24 +-- .../{ => Miscellaneous}/CustomServersPatch.cs | 6 +- .../{ => Miscellaneous}/FreeNamePatch.cs | 13 +- .../SplashSkipPatch.cs} | 34 ++-- .../Miscellaneous/StandaloneSteamPatch.cs | 39 +++++ Reactor/Patches/PlayerIdPatch.cs | 25 --- Reactor/Patches/ReactorVersionShower.cs | 2 +- Reactor/Patches/SteamPatch.cs | 38 ----- Reactor/Reactor.csproj | 2 +- Reactor/ReactorPlugin.cs | 15 +- Reactor/Unstrip/IsUnmanagedAttribute.cs | 7 - Reactor/{ => Utilities}/AssetBundleManager.cs | 4 +- .../Attributes}/RegisterInIl2CppAttribute.cs | 18 ++- Reactor/Utilities/CanvasUtilities.cs | 43 +++++ Reactor/{ => Utilities}/Coroutines.cs | 3 +- .../{Unstrip => Utilities}/DefaultBundle.cs | 12 +- .../{Extensions => Utilities}/Dispatcher.cs | 3 +- .../Extensions/AssetBundleExtensions.cs | 12 ++ Reactor/Utilities/Extensions/GUIExtensions.cs | 44 ++++++ .../Extensions/Il2CppInteropExtensions.cs | 12 ++ .../Extensions/RandomExtensions.cs | 11 +- .../Extensions/ReflectionExtensions.cs | 14 +- .../Utilities/Extensions/StreamExtensions.cs | 103 ++++++++++++ .../Utilities/Extensions/UnityExtensions.cs | 41 +++++ .../Extensions/UnityObjectExtensions.cs | 2 +- .../{ => Utilities}/Il2CppEqualityComparer.cs | 2 +- Reactor/Utilities/ImGui/DragWindow.cs | 23 +++ Reactor/Utilities/ImGui/Window.cs | 51 ++++++ Reactor/{ => Utilities}/Logger.cs | 4 +- Reactor/{ => Utilities}/PluginSingleton.cs | 2 +- .../RegionInfoWatcher.cs | 7 +- Reactor/Utilities/SceneManagerUtilities.cs | 24 +++ 66 files changed, 652 insertions(+), 618 deletions(-) create mode 100644 Reactor/Compiler/IsUnmanagedAttribute.cs delete mode 100644 Reactor/Extensions/Extensions.cs delete mode 100644 Reactor/Extensions/GUIExtensions.cs delete mode 100644 Reactor/Extensions/StreamExtensions.cs rename Reactor/{ => Localization}/CustomStringName.cs (97%) rename Reactor/Networking/{Serialization => Attributes}/MessageConverterAttribute.cs (80%) rename Reactor/Networking/{MethodRpc => Attributes}/MethodRpcAttribute.cs (83%) rename Reactor/{ => Networking/Attributes}/ReactorModFlagsAttribute.cs (86%) rename Reactor/{ => Networking/Attributes}/RegisterCustomRpcAttribute.cs (70%) rename Reactor/Networking/{Extensions.cs => Extensions/ClientExtensions.cs} (92%) rename Reactor/{ => Networking}/Extensions/ExtraMessageExtensions.cs (97%) rename Reactor/Networking/{ => Messages}/ModdedHandshakeC2S.cs (96%) rename Reactor/Networking/{ => Messages}/ModdedHandshakeS2C.cs (93%) rename Reactor/Networking/{ => Messages}/ReactorHeader.cs (90%) rename Reactor/Networking/{ => Rpc}/CustomRpc.cs (98%) rename Reactor/Networking/{ => Rpc}/CustomRpcManager.cs (60%) rename Reactor/Networking/{MethodRpc => Rpc}/MethodRpc.cs (99%) rename Reactor/Networking/{ => Rpc}/PlayerCustomRpc.cs (96%) create mode 100644 Reactor/Networking/Rpc/Rpc.cs create mode 100644 Reactor/Networking/Rpc/RpcLocalHandling.cs rename Reactor/Networking/{ => Rpc}/UnsafeCustomRpc.cs (98%) delete mode 100644 Reactor/Patches/BetterGuestPatch.cs rename Reactor/Patches/{ => Fixes}/CursorPosPatch.cs (95%) rename Reactor/Patches/{ServerInfoFixes.cs => Fixes/ServerInfoFixesPatch.cs} (69%) rename Reactor/Patches/{ => Miscellaneous}/CustomServersPatch.cs (81%) rename Reactor/Patches/{ => Miscellaneous}/FreeNamePatch.cs (91%) rename Reactor/Patches/{SplashSkip.cs => Miscellaneous/SplashSkipPatch.cs} (56%) create mode 100644 Reactor/Patches/Miscellaneous/StandaloneSteamPatch.cs delete mode 100644 Reactor/Patches/PlayerIdPatch.cs delete mode 100644 Reactor/Patches/SteamPatch.cs delete mode 100644 Reactor/Unstrip/IsUnmanagedAttribute.cs rename Reactor/{ => Utilities}/AssetBundleManager.cs (98%) rename Reactor/{ => Utilities/Attributes}/RegisterInIl2CppAttribute.cs (69%) create mode 100644 Reactor/Utilities/CanvasUtilities.cs rename Reactor/{ => Utilities}/Coroutines.cs (94%) rename Reactor/{Unstrip => Utilities}/DefaultBundle.cs (77%) rename Reactor/{Extensions => Utilities}/Dispatcher.cs (92%) create mode 100644 Reactor/Utilities/Extensions/AssetBundleExtensions.cs create mode 100644 Reactor/Utilities/Extensions/GUIExtensions.cs create mode 100644 Reactor/Utilities/Extensions/Il2CppInteropExtensions.cs rename Reactor/{ => Utilities}/Extensions/RandomExtensions.cs (66%) rename Reactor/{ => Utilities}/Extensions/ReflectionExtensions.cs (79%) create mode 100644 Reactor/Utilities/Extensions/StreamExtensions.cs create mode 100644 Reactor/Utilities/Extensions/UnityExtensions.cs rename Reactor/{ => Utilities}/Extensions/UnityObjectExtensions.cs (95%) rename Reactor/{ => Utilities}/Il2CppEqualityComparer.cs (96%) create mode 100644 Reactor/Utilities/ImGui/DragWindow.cs create mode 100644 Reactor/Utilities/ImGui/Window.cs rename Reactor/{ => Utilities}/Logger.cs (98%) rename Reactor/{ => Utilities}/PluginSingleton.cs (96%) rename Reactor/{Patches => Utilities}/RegionInfoWatcher.cs (95%) create mode 100644 Reactor/Utilities/SceneManagerUtilities.cs diff --git a/.gitignore b/.gitignore index e934bbe..f04d691 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ riderModule.iml .idea /tools .vscode +*.DotSettings.user \ No newline at end of file diff --git a/Reactor.Benchmarks/AssetBundleBenchmarks.cs b/Reactor.Benchmarks/AssetBundleBenchmarks.cs index 1c238dd..ec9d4bc 100644 --- a/Reactor.Benchmarks/AssetBundleBenchmarks.cs +++ b/Reactor.Benchmarks/AssetBundleBenchmarks.cs @@ -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; diff --git a/Reactor.Debugger/DebuggerPlugin.cs b/Reactor.Debugger/DebuggerPlugin.cs index b601747..a0a401a 100644 --- a/Reactor.Debugger/DebuggerPlugin.cs +++ b/Reactor.Debugger/DebuggerPlugin.cs @@ -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; diff --git a/Reactor.Debugger/Patches.cs b/Reactor.Debugger/Patches.cs index d8388a8..1dd62e9 100644 --- a/Reactor.Debugger/Patches.cs +++ b/Reactor.Debugger/Patches.cs @@ -1,6 +1,7 @@ using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; +using Reactor.Utilities; using UnityEngine; namespace Reactor.Debugger; diff --git a/Reactor.Example/ExamplePlugin.cs b/Reactor.Example/ExamplePlugin.cs index 3911819..24b8d9f 100644 --- a/Reactor.Example/ExamplePlugin.cs +++ b/Reactor.Example/ExamplePlugin.cs @@ -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; diff --git a/Reactor.Example/ExampleRpc.cs b/Reactor.Example/ExampleRpc.cs index b068e6e..c60b8ec 100644 --- a/Reactor.Example/ExampleRpc.cs +++ b/Reactor.Example/ExampleRpc.cs @@ -1,5 +1,6 @@ using Hazel; -using Reactor.Networking; +using Reactor.Networking.Attributes; +using Reactor.Networking.Rpc; namespace Reactor.Example; diff --git a/Reactor.sln b/Reactor.sln index 1d1945a..11fdc70 100644 --- a/Reactor.sln +++ b/Reactor.sln @@ -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 diff --git a/Reactor/Compiler/IsUnmanagedAttribute.cs b/Reactor/Compiler/IsUnmanagedAttribute.cs new file mode 100644 index 0000000..4c137c3 --- /dev/null +++ b/Reactor/Compiler/IsUnmanagedAttribute.cs @@ -0,0 +1,12 @@ +// ReSharper disable CheckNamespace + +namespace System.Runtime.CompilerServices; + +/// +/// This attribute is required by the compiler for the "unmanaged" generic type constraint. +/// It should be generated automatically, but for whatever reason its not. +/// +[Obsolete("Do not use directly.", true)] +internal class IsUnmanagedAttribute : Attribute +{ +} diff --git a/Reactor/Extensions/Extensions.cs b/Reactor/Extensions/Extensions.cs deleted file mode 100644 index 5cfa593..0000000 --- a/Reactor/Extensions/Extensions.cs +++ /dev/null @@ -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; - -/// -/// General utilities -/// -public static class Extensions -{ - /// - /// Adds a delegate to get one notification when a scene has loaded. - /// - public static void once_sceneLoaded(Action value) - { - UnityAction? unityAction = null; - - unityAction = (Action) ((scene, loadMode) => - { - SceneManager.remove_sceneLoaded(unityAction); - value.Invoke(scene, loadMode); - }); - - SceneManager.add_sceneLoaded(unityAction); - } - - /// - /// Returns random from - /// - public static T? Random(this IEnumerable input) - { - var list = input as IList ?? input.ToList(); - return list.Count == 0 ? default : list[UnityEngine.Random.Range(0, list.Count)]; - } - - /// - /// Returns the color as a hexadecimal string in the format "RRGGBBAA". - /// - /// The color to be converted. - /// Hexadecimal string representing the color. - /// https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html - public static string ToHtmlStringRGBA(this Color32 color) - { - return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}"; - } - - /// - public static string ToHtmlStringRGBA(this Color color) - { - return ((Color32) color).ToHtmlStringRGBA(); - } - - /// - /// Fully read stream, can be used as workaround for il2cpp streams. - /// - public static byte[] ReadFully(this Stream input) - { - using (var ms = new MemoryStream()) - { - input.CopyTo(ms); - return ms.ToArray(); - } - } - - /// - /// Sends and return task that finishes on completion. - /// - public static Task SendAsync(this UnityWebRequest request) - { - var task = new TaskCompletionSource(); - - request.Send().m_completeCallback = (Action) (_ => - { - 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"); - - /// - /// Sets color outline for renderers using default Among Us shader - /// - 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 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 GetMethods(this Type type, Type returnType, params Type[] parameterTypes) - { - return type.GetMethods(AccessTools.all, returnType, parameterTypes); - } - - public static T? LoadAsset(this AssetBundle assetBundle, string name) where T : UnityEngine.Object - { - return assetBundle.LoadAsset(name, Il2CppType.Of())?.Cast(); - } - - public static unsafe Span ToSpan(this Il2CppStructArray array) where T : unmanaged - { - return new Span(IntPtr.Add(array.Pointer, IntPtr.Size * 4).ToPointer(), array.Length); - } -} diff --git a/Reactor/Extensions/GUIExtensions.cs b/Reactor/Extensions/GUIExtensions.cs deleted file mode 100644 index 69b0c07..0000000 --- a/Reactor/Extensions/GUIExtensions.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using UnityEngine; -using UnityEngine.EventSystems; -using UnityEngine.UI; - -namespace Reactor.Extensions; - -/// -/// GUI utilities -/// -public static class GUIExtensions -{ - /// - /// Shortcut for empty texture - /// - public static Texture2D CreateEmptyTexture(int width = 0, int height = 0) - { - return new Texture2D(width, height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero); - } - - /// - /// Clamp Rect to screen size - /// - 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; - } - - /// - /// Reset Rect size - /// - public static Rect ResetSize(this Rect rect) - { - rect.width = rect.height = 0; - - return rect; - } - - /// - /// Create from - /// - 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.renderMode = RenderMode.ScreenSpaceOverlay; - - var canvasScaler = gameObject.AddComponent(); - - canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; - canvasScaler.referencePixelsPerUnit = 100f; - - gameObject.AddComponent(); - - return gameObject; - } - - public static GameObject CreateEventSystem() - { - var gameObject = new GameObject("EventSystem"); - gameObject.AddComponent(); - gameObject.AddComponent(); - gameObject.AddComponent(); - - 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 Func { get; set; } - public string Title { get; set; } - - public Window(Rect rect, string title, Action 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 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()) - { - } -} diff --git a/Reactor/Extensions/StreamExtensions.cs b/Reactor/Extensions/StreamExtensions.cs deleted file mode 100644 index 46e270d..0000000 --- a/Reactor/Extensions/StreamExtensions.cs +++ /dev/null @@ -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()) - { - ClassInjector.DerivedConstructorBody(this); - _stream = stream; - } - - [HideFromIl2Cpp] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe Span GetSpan(Il2CppStructArray buffer, int offset, int count) - { - var rawBuffer = (byte*) buffer.Pointer + 4 * IntPtr.Size; - return new Span(rawBuffer + offset, count); - } - - public override int Read(Il2CppStructArray buffer, int offset, int count) - { - return _stream.Read(GetSpan(buffer, offset, count)); - } - - public override void Write(Il2CppStructArray 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 -{ - /// - /// Wraps a System Stream into an Il2Cpp Stream - /// - public static StreamWrapper AsIl2Cpp(this Stream stream) => new(stream); -} diff --git a/Reactor/CustomStringName.cs b/Reactor/Localization/CustomStringName.cs similarity index 97% rename from Reactor/CustomStringName.cs rename to Reactor/Localization/CustomStringName.cs index c424d48..404f5f5 100644 --- a/Reactor/CustomStringName.cs +++ b/Reactor/Localization/CustomStringName.cs @@ -3,7 +3,7 @@ using System.Linq; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; -namespace Reactor; +namespace Reactor.Localization; public class CustomStringName { diff --git a/Reactor/Networking/Serialization/MessageConverterAttribute.cs b/Reactor/Networking/Attributes/MessageConverterAttribute.cs similarity index 80% rename from Reactor/Networking/Serialization/MessageConverterAttribute.cs rename to Reactor/Networking/Attributes/MessageConverterAttribute.cs index a366e4e..513ca86 100644 --- a/Reactor/Networking/Serialization/MessageConverterAttribute.cs +++ b/Reactor/Networking/Attributes/MessageConverterAttribute.cs @@ -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 _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(); diff --git a/Reactor/Networking/MethodRpc/MethodRpcAttribute.cs b/Reactor/Networking/Attributes/MethodRpcAttribute.cs similarity index 83% rename from Reactor/Networking/MethodRpc/MethodRpcAttribute.cs rename to Reactor/Networking/Attributes/MethodRpcAttribute.cs index dd73f17..cf276a3 100644 --- a/Reactor/Networking/MethodRpc/MethodRpcAttribute.cs +++ b/Reactor/Networking/Attributes/MethodRpcAttribute.cs @@ -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 _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)); diff --git a/Reactor/ReactorModFlagsAttribute.cs b/Reactor/Networking/Attributes/ReactorModFlagsAttribute.cs similarity index 86% rename from Reactor/ReactorModFlagsAttribute.cs rename to Reactor/Networking/Attributes/ReactorModFlagsAttribute.cs index 1689c4c..bfc862e 100644 --- a/Reactor/ReactorModFlagsAttribute.cs +++ b/Reactor/Networking/Attributes/ReactorModFlagsAttribute.cs @@ -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().SingleOrDefault(x => x.Key == "Reactor.ModFlags"); - if (metadataAttribute != null && metadataAttribute.Value != null) + if (metadataAttribute is { Value: not null }) { return Enum.Parse(metadataAttribute.Value); } diff --git a/Reactor/RegisterCustomRpcAttribute.cs b/Reactor/Networking/Attributes/RegisterCustomRpcAttribute.cs similarity index 70% rename from Reactor/RegisterCustomRpcAttribute.cs rename to Reactor/Networking/Attributes/RegisterCustomRpcAttribute.cs index 307d923..37ccb96 100644 --- a/Reactor/RegisterCustomRpcAttribute.cs +++ b/Reactor/Networking/Attributes/RegisterCustomRpcAttribute.cs @@ -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; /// /// Utility attribute for automatically registering CustomRpc @@ -12,6 +14,8 @@ namespace Reactor; [AttributeUsage(AttributeTargets.Class)] public class RegisterCustomRpcAttribute : Attribute { + private static readonly HashSet _registeredAssemblies = new(); + public uint Id { get; } public RegisterCustomRpcAttribute(uint id) @@ -19,8 +23,16 @@ public class RegisterCustomRpcAttribute : Attribute Id = id; } + /// + /// Registers all types marked with from the specified assembly as custom rpcs + /// + /// + /// 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(); diff --git a/Reactor/Networking/Extensions.cs b/Reactor/Networking/Extensions/ClientExtensions.cs similarity index 92% rename from Reactor/Networking/Extensions.cs rename to Reactor/Networking/Extensions/ClientExtensions.cs index 9e44ecd..287dfd3 100644 --- a/Reactor/Networking/Extensions.cs +++ b/Reactor/Networking/Extensions/ClientExtensions.cs @@ -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) { diff --git a/Reactor/Extensions/ExtraMessageExtensions.cs b/Reactor/Networking/Extensions/ExtraMessageExtensions.cs similarity index 97% rename from Reactor/Extensions/ExtraMessageExtensions.cs rename to Reactor/Networking/Extensions/ExtraMessageExtensions.cs index 2d48010..7f35e88 100644 --- a/Reactor/Extensions/ExtraMessageExtensions.cs +++ b/Reactor/Networking/Extensions/ExtraMessageExtensions.cs @@ -3,7 +3,7 @@ using Hazel; using Hazel.Udp; using UnityEngine; -namespace Reactor.Extensions; +namespace Reactor.Networking.Extensions; public static class ExtraMessageExtensions { diff --git a/Reactor/Networking/ModdedHandshakeC2S.cs b/Reactor/Networking/Messages/ModdedHandshakeC2S.cs similarity index 96% rename from Reactor/Networking/ModdedHandshakeC2S.cs rename to Reactor/Networking/Messages/ModdedHandshakeC2S.cs index 6e9e4da..a90f4e4 100644 --- a/Reactor/Networking/ModdedHandshakeC2S.cs +++ b/Reactor/Networking/Messages/ModdedHandshakeC2S.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Hazel; -namespace Reactor.Networking; +namespace Reactor.Networking.Messages; internal static class ModdedHandshakeC2S { diff --git a/Reactor/Networking/ModdedHandshakeS2C.cs b/Reactor/Networking/Messages/ModdedHandshakeS2C.cs similarity index 93% rename from Reactor/Networking/ModdedHandshakeS2C.cs rename to Reactor/Networking/Messages/ModdedHandshakeS2C.cs index ab4e2a0..8e40990 100644 --- a/Reactor/Networking/ModdedHandshakeS2C.cs +++ b/Reactor/Networking/Messages/ModdedHandshakeS2C.cs @@ -1,6 +1,6 @@ using Hazel; -namespace Reactor.Networking; +namespace Reactor.Networking.Messages; internal static class ModdedHandshakeS2C { diff --git a/Reactor/Networking/ReactorHeader.cs b/Reactor/Networking/Messages/ReactorHeader.cs similarity index 90% rename from Reactor/Networking/ReactorHeader.cs rename to Reactor/Networking/Messages/ReactorHeader.cs index d495225..8872492 100644 --- a/Reactor/Networking/ReactorHeader.cs +++ b/Reactor/Networking/Messages/ReactorHeader.cs @@ -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 diff --git a/Reactor/Networking/ModList.cs b/Reactor/Networking/ModList.cs index d78d6ae..0b01ca9 100644 --- a/Reactor/Networking/ModList.cs +++ b/Reactor/Networking/ModList.cs @@ -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 Current { get; private set; } = null!; - private static readonly Dictionary _mapById = new(); - private static readonly Dictionary _mapByPluginType = new(); + private static readonly Dictionary _modById = new(); + private static readonly Dictionary _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 _mapByNetId = new(); - private static readonly Dictionary _netIdMap = new(); + private static readonly Dictionary _modByNetId = new(); + private static readonly Dictionary _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() diff --git a/Reactor/Networking/Patches/ClientPatches.cs b/Reactor/Networking/Patches/ClientPatches.cs index 3af5e3e..d28f764 100644 --- a/Reactor/Networking/Patches/ClientPatches.cs +++ b/Reactor/Networking/Patches/ClientPatches.cs @@ -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; diff --git a/Reactor/Networking/CustomRpc.cs b/Reactor/Networking/Rpc/CustomRpc.cs similarity index 98% rename from Reactor/Networking/CustomRpc.cs rename to Reactor/Networking/Rpc/CustomRpc.cs index 9419046..a310b83 100644 --- a/Reactor/Networking/CustomRpc.cs +++ b/Reactor/Networking/Rpc/CustomRpc.cs @@ -3,7 +3,7 @@ using BepInEx.Unity.IL2CPP; using Hazel; using InnerNet; -namespace Reactor.Networking; +namespace Reactor.Networking.Rpc; public abstract class CustomRpc : UnsafeCustomRpc where TPlugin : BasePlugin where TInnerNetObject : InnerNetObject { diff --git a/Reactor/Networking/CustomRpcManager.cs b/Reactor/Networking/Rpc/CustomRpcManager.cs similarity index 60% rename from Reactor/Networking/CustomRpcManager.cs rename to Reactor/Networking/Rpc/CustomRpcManager.cs index 852258d..a83cf16 100644 --- a/Reactor/Networking/CustomRpcManager.cs +++ b/Reactor/Networking/Rpc/CustomRpcManager.cs @@ -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 _list = new(); - private readonly Dictionary>> _map = new(); + internal readonly Dictionary>> _map = new(); public IReadOnlyList List => _list.AsReadOnly(); @@ -29,8 +23,8 @@ public class CustomRpcManager { customRpc.Manager = this; _list.Add(customRpc); - _map.GetOrCreate(customRpc.InnerNetObjectType, _ => new Dictionary>()) - .GetOrCreate(customRpc.Mod, _ => new Dictionary()) + _map.GetOrCreate(customRpc.InnerNetObjectType, static _ => new Dictionary>()) + .GetOrCreate(customRpc.Mod, static _ => new Dictionary()) .Add(customRpc.Id, customRpc); if (customRpc.IsSingleton) @@ -41,22 +35,6 @@ public class CustomRpcManager return customRpc; } - [HarmonyPatch] - private static class HandleRpcPatch - { - internal static List InnerNetObjectTypes { get; } = typeof(InnerNetObject).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(InnerNetObject)) && x != typeof(LobbyBehaviour)).ToList(); - - public static IEnumerable 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; } -} -public static class Rpc where T : UnsafeCustomRpc -{ - private static T? _instance; - - public static T Instance + [HarmonyPatch] + internal static class HandleRpcPatch { - get - { - if (_instance == null) - { - throw new Exception($"{typeof(T).FullName} isn't registered"); - } + private static List InnerNetObjectTypes { get; } = typeof(InnerNetObject).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(InnerNetObject)) && x != typeof(LobbyBehaviour)).ToList(); - return _instance; + public static IEnumerable TargetMethods() + { + return InnerNetObjectTypes.Select(x => x.GetMethod(nameof(InnerNetObject.HandleRpc), AccessTools.allDeclared)).Where(m => m != null)!; } - internal set + public static bool Prefix(InnerNetObject __instance, [HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader) { - if (_instance != null) - { - throw new Exception($"{typeof(T).FullName} is already registered"); - } - - _instance = value; + return !HandleRpc(__instance, callId, reader); } } } diff --git a/Reactor/Networking/MethodRpc/MethodRpc.cs b/Reactor/Networking/Rpc/MethodRpc.cs similarity index 99% rename from Reactor/Networking/MethodRpc/MethodRpc.cs rename to Reactor/Networking/Rpc/MethodRpc.cs index 39bb3a4..7322ff4 100644 --- a/Reactor/Networking/MethodRpc/MethodRpc.cs +++ b/Reactor/Networking/Rpc/MethodRpc.cs @@ -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 { diff --git a/Reactor/Networking/PlayerCustomRpc.cs b/Reactor/Networking/Rpc/PlayerCustomRpc.cs similarity index 96% rename from Reactor/Networking/PlayerCustomRpc.cs rename to Reactor/Networking/Rpc/PlayerCustomRpc.cs index dbda2d7..5468c3b 100644 --- a/Reactor/Networking/PlayerCustomRpc.cs +++ b/Reactor/Networking/Rpc/PlayerCustomRpc.cs @@ -1,7 +1,7 @@ using System; using BepInEx.Unity.IL2CPP; -namespace Reactor.Networking; +namespace Reactor.Networking.Rpc; public abstract class PlayerCustomRpc : CustomRpc where TPlugin : BasePlugin { diff --git a/Reactor/Networking/Rpc/Rpc.cs b/Reactor/Networking/Rpc/Rpc.cs new file mode 100644 index 0000000..c16451f --- /dev/null +++ b/Reactor/Networking/Rpc/Rpc.cs @@ -0,0 +1,31 @@ +using System; + +namespace Reactor.Networking.Rpc; + +public static class Rpc 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; + } + } +} diff --git a/Reactor/Networking/Rpc/RpcLocalHandling.cs b/Reactor/Networking/Rpc/RpcLocalHandling.cs new file mode 100644 index 0000000..8e1d3a9 --- /dev/null +++ b/Reactor/Networking/Rpc/RpcLocalHandling.cs @@ -0,0 +1,8 @@ +namespace Reactor.Networking.Rpc; + +public enum RpcLocalHandling +{ + None, + Before, + After, +} diff --git a/Reactor/Networking/UnsafeCustomRpc.cs b/Reactor/Networking/Rpc/UnsafeCustomRpc.cs similarity index 98% rename from Reactor/Networking/UnsafeCustomRpc.cs rename to Reactor/Networking/Rpc/UnsafeCustomRpc.cs index c1c0164..f210527 100644 --- a/Reactor/Networking/UnsafeCustomRpc.cs +++ b/Reactor/Networking/Rpc/UnsafeCustomRpc.cs @@ -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 { diff --git a/Reactor/Networking/Serialization/InnerNetObjectConverter.cs b/Reactor/Networking/Serialization/InnerNetObjectConverter.cs index c168a3b..93cf47c 100644 --- a/Reactor/Networking/Serialization/InnerNetObjectConverter.cs +++ b/Reactor/Networking/Serialization/InnerNetObjectConverter.cs @@ -4,6 +4,7 @@ using System.Reflection; using HarmonyLib; using Hazel; using InnerNet; +using Reactor.Networking.Attributes; namespace Reactor.Networking.Serialization; diff --git a/Reactor/Networking/Serialization/MessageSerializer.cs b/Reactor/Networking/Serialization/MessageSerializer.cs index 960d6ad..a11799c 100644 --- a/Reactor/Networking/Serialization/MessageSerializer.cs +++ b/Reactor/Networking/Serialization/MessageSerializer.cs @@ -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]; diff --git a/Reactor/Patches/BetterGuestPatch.cs b/Reactor/Patches/BetterGuestPatch.cs deleted file mode 100644 index 73cef30..0000000 --- a/Reactor/Patches/BetterGuestPatch.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Reactor/Patches/CursorPosPatch.cs b/Reactor/Patches/Fixes/CursorPosPatch.cs similarity index 95% rename from Reactor/Patches/CursorPosPatch.cs rename to Reactor/Patches/Fixes/CursorPosPatch.cs index 33f6739..01fc3f3 100644 --- a/Reactor/Patches/CursorPosPatch.cs +++ b/Reactor/Patches/Fixes/CursorPosPatch.cs @@ -3,7 +3,7 @@ using HarmonyLib; using TMPro; using UnityEngine; -namespace Reactor.Patches; +namespace Reactor.Patches.Fixes; /// /// "Fixes" an issue where empty TextBoxes have wrong cursor positions diff --git a/Reactor/Patches/ServerInfoFixes.cs b/Reactor/Patches/Fixes/ServerInfoFixesPatch.cs similarity index 69% rename from Reactor/Patches/ServerInfoFixes.cs rename to Reactor/Patches/Fixes/ServerInfoFixesPatch.cs index b164378..33244af 100644 --- a/Reactor/Patches/ServerInfoFixes.cs +++ b/Reactor/Patches/Fixes/ServerInfoFixesPatch.cs @@ -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}"); } -} -/// -/// Fixes hardcoded port and filters out IPv6 in DnsRegionInfo -/// -[HarmonyPatch(typeof(DnsRegionInfo), nameof(DnsRegionInfo.PopulateServers))] -public static class PopulateServersPatch -{ - public static bool Prefix(DnsRegionInfo __instance) + /// + /// Fixes hardcoded port and filters out IPv6 in DnsRegionInfo + /// + [HarmonyPatch(typeof(DnsRegionInfo), nameof(DnsRegionInfo.PopulateServers))] + [HarmonyPrefix] + public static bool FixPopulateServers(DnsRegionInfo __instance) { try { diff --git a/Reactor/Patches/CustomServersPatch.cs b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs similarity index 81% rename from Reactor/Patches/CustomServersPatch.cs rename to Reactor/Patches/Miscellaneous/CustomServersPatch.cs index 31eb84d..d053daf 100644 --- a/Reactor/Patches/CustomServersPatch.cs +++ b/Reactor/Patches/Miscellaneous/CustomServersPatch.cs @@ -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) diff --git a/Reactor/Patches/FreeNamePatch.cs b/Reactor/Patches/Miscellaneous/FreeNamePatch.cs similarity index 91% rename from Reactor/Patches/FreeNamePatch.cs rename to Reactor/Patches/Miscellaneous/FreeNamePatch.cs index a34c566..1c7046f 100644 --- a/Reactor/Patches/FreeNamePatch.cs +++ b/Reactor/Patches/Miscellaneous/FreeNamePatch.cs @@ -7,17 +7,16 @@ 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 + [HarmonyPrefix] + public static bool DontRegenerateNames() { - public static bool Prefix() - { - return false; - } + return false; } public static void Initialize() diff --git a/Reactor/Patches/SplashSkip.cs b/Reactor/Patches/Miscellaneous/SplashSkipPatch.cs similarity index 56% rename from Reactor/Patches/SplashSkip.cs rename to Reactor/Patches/Miscellaneous/SplashSkipPatch.cs index ce9bcd7..290af4b 100644 --- a/Reactor/Patches/SplashSkip.cs +++ b/Reactor/Patches/Miscellaneous/SplashSkipPatch.cs @@ -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 + [HarmonyPostfix] + private static void WaitForEpicAuth(bool overrideLoad) { - private static void Postfix(bool overrideLoad) - { - if (overrideLoad) _loginFinished = true; - } + if (overrideLoad) _loginFinished = true; } } diff --git a/Reactor/Patches/Miscellaneous/StandaloneSteamPatch.cs b/Reactor/Patches/Miscellaneous/StandaloneSteamPatch.cs new file mode 100644 index 0000000..b1b7dd1 --- /dev/null +++ b/Reactor/Patches/Miscellaneous/StandaloneSteamPatch.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Reflection; +using HarmonyLib; + +namespace Reactor.Patches.Miscellaneous; + +/// +/// Allows launching steam builds outside of steam +/// +[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; + } +} diff --git a/Reactor/Patches/PlayerIdPatch.cs b/Reactor/Patches/PlayerIdPatch.cs deleted file mode 100644 index 395acf8..0000000 --- a/Reactor/Patches/PlayerIdPatch.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Reactor/Patches/ReactorVersionShower.cs b/Reactor/Patches/ReactorVersionShower.cs index 81b2f6f..b564138 100644 --- a/Reactor/Patches/ReactorVersionShower.cs +++ b/Reactor/Patches/ReactorVersionShower.cs @@ -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; diff --git a/Reactor/Patches/SteamPatch.cs b/Reactor/Patches/SteamPatch.cs deleted file mode 100644 index 4d9a071..0000000 --- a/Reactor/Patches/SteamPatch.cs +++ /dev/null @@ -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; - } - } -} diff --git a/Reactor/Reactor.csproj b/Reactor/Reactor.csproj index 7106a28..cf0fca3 100644 --- a/Reactor/Reactor.csproj +++ b/Reactor/Reactor.csproj @@ -14,7 +14,7 @@ - + diff --git a/Reactor/ReactorPlugin.cs b/Reactor/ReactorPlugin.cs index 6637c5e..b9f1f0e 100644 --- a/Reactor/ReactorPlugin.cs +++ b/Reactor/ReactorPlugin.cs @@ -1,17 +1,18 @@ -global using static Reactor.Logger; +global using static Reactor.Utilities.Logger; 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(); } diff --git a/Reactor/Unstrip/IsUnmanagedAttribute.cs b/Reactor/Unstrip/IsUnmanagedAttribute.cs deleted file mode 100644 index 5ae4326..0000000 --- a/Reactor/Unstrip/IsUnmanagedAttribute.cs +++ /dev/null @@ -1,7 +0,0 @@ -// ReSharper disable CheckNamespace - -namespace System.Runtime.CompilerServices; - -public class IsUnmanagedAttribute : Attribute -{ -} diff --git a/Reactor/AssetBundleManager.cs b/Reactor/Utilities/AssetBundleManager.cs similarity index 98% rename from Reactor/AssetBundleManager.cs rename to Reactor/Utilities/AssetBundleManager.cs index 2c5823f..6507bca 100644 --- a/Reactor/AssetBundleManager.cs +++ b/Reactor/Utilities/AssetBundleManager.cs @@ -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 { diff --git a/Reactor/RegisterInIl2CppAttribute.cs b/Reactor/Utilities/Attributes/RegisterInIl2CppAttribute.cs similarity index 69% rename from Reactor/RegisterInIl2CppAttribute.cs rename to Reactor/Utilities/Attributes/RegisterInIl2CppAttribute.cs index b814f7d..95d3926 100644 --- a/Reactor/RegisterInIl2CppAttribute.cs +++ b/Reactor/Utilities/Attributes/RegisterInIl2CppAttribute.cs @@ -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; /// /// Utility attribute for automatically calling @@ -12,6 +13,8 @@ namespace Reactor; [AttributeUsage(AttributeTargets.Class)] public class RegisterInIl2CppAttribute : Attribute { + private static readonly HashSet _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(); 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 } } + /// + /// Registers in Il2Cpp all types marked with from the specified assembly.
+ ///
+ /// public static void Register(Assembly assembly) { + if (_registeredAssemblies.Contains(assembly)) return; + _registeredAssemblies.Add(assembly); + foreach (var type in assembly.GetTypes()) { var attribute = type.GetCustomAttribute(); if (attribute != null) { - Register(type, attribute.Interfaces); + RegisterType(type, attribute.Interfaces); } } } diff --git a/Reactor/Utilities/CanvasUtilities.cs b/Reactor/Utilities/CanvasUtilities.cs new file mode 100644 index 0000000..13a7bec --- /dev/null +++ b/Reactor/Utilities/CanvasUtilities.cs @@ -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.renderMode = RenderMode.ScreenSpaceOverlay; + + var canvasScaler = gameObject.AddComponent(); + + canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand; + canvasScaler.referencePixelsPerUnit = 100f; + + gameObject.AddComponent(); + + return gameObject; + } + + public static GameObject CreateEventSystem() + { + var gameObject = new GameObject("EventSystem"); + gameObject.AddComponent(); + gameObject.AddComponent(); + gameObject.AddComponent(); + + return gameObject; + } + + /// + /// Shortcut for empty texture + /// + public static Texture2D CreateEmptyTexture(int width = 0, int height = 0) + { + return new Texture2D(width, height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero); + } +} diff --git a/Reactor/Coroutines.cs b/Reactor/Utilities/Coroutines.cs similarity index 94% rename from Reactor/Coroutines.cs rename to Reactor/Utilities/Coroutines.cs index 5b1f7f4..b1531d5 100644 --- a/Reactor/Coroutines.cs +++ b/Reactor/Utilities/Coroutines.cs @@ -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 { diff --git a/Reactor/Unstrip/DefaultBundle.cs b/Reactor/Utilities/DefaultBundle.cs similarity index 77% rename from Reactor/Unstrip/DefaultBundle.cs rename to Reactor/Utilities/DefaultBundle.cs index 64573bc..3de1e0e 100644 --- a/Reactor/Unstrip/DefaultBundle.cs +++ b/Reactor/Utilities/DefaultBundle.cs @@ -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("Background")!.DontUnload(), checkmark = bundle.LoadAsset("Checkmark")!.DontUnload(), diff --git a/Reactor/Extensions/Dispatcher.cs b/Reactor/Utilities/Dispatcher.cs similarity index 92% rename from Reactor/Extensions/Dispatcher.cs rename to Reactor/Utilities/Dispatcher.cs index acbfda7..9ecc4ba 100644 --- a/Reactor/Extensions/Dispatcher.cs +++ b/Reactor/Utilities/Dispatcher.cs @@ -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 diff --git a/Reactor/Utilities/Extensions/AssetBundleExtensions.cs b/Reactor/Utilities/Extensions/AssetBundleExtensions.cs new file mode 100644 index 0000000..befd04f --- /dev/null +++ b/Reactor/Utilities/Extensions/AssetBundleExtensions.cs @@ -0,0 +1,12 @@ +using Il2CppInterop.Runtime; +using UnityEngine; + +namespace Reactor.Utilities.Extensions; + +public static class AssetBundleExtensions +{ + public static T? LoadAsset(this AssetBundle assetBundle, string name) where T : Object + { + return assetBundle.LoadAsset(name, Il2CppType.Of())?.Cast(); + } +} diff --git a/Reactor/Utilities/Extensions/GUIExtensions.cs b/Reactor/Utilities/Extensions/GUIExtensions.cs new file mode 100644 index 0000000..ff4a15e --- /dev/null +++ b/Reactor/Utilities/Extensions/GUIExtensions.cs @@ -0,0 +1,44 @@ +using UnityEngine; + +namespace Reactor.Utilities.Extensions; + +/// +/// GUI utilities +/// +public static class GUIExtensions +{ + /// + /// Clamp Rect to screen size + /// + 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; + } + + /// + /// Reset Rect size + /// + public static Rect ResetSize(this Rect rect) + { + rect.width = rect.height = 0; + + return rect; + } + + /// + /// Create from + /// + 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); + } +} diff --git a/Reactor/Utilities/Extensions/Il2CppInteropExtensions.cs b/Reactor/Utilities/Extensions/Il2CppInteropExtensions.cs new file mode 100644 index 0000000..d13e1f6 --- /dev/null +++ b/Reactor/Utilities/Extensions/Il2CppInteropExtensions.cs @@ -0,0 +1,12 @@ +using System; +using Il2CppInterop.Runtime.InteropTypes.Arrays; + +namespace Reactor.Utilities.Extensions; + +public static class Il2CppInteropExtensions +{ + public static unsafe Span ToSpan(this Il2CppStructArray array) where T : unmanaged + { + return new Span(IntPtr.Add(array.Pointer, IntPtr.Size * 4).ToPointer(), array.Length); + } +} diff --git a/Reactor/Extensions/RandomExtensions.cs b/Reactor/Utilities/Extensions/RandomExtensions.cs similarity index 66% rename from Reactor/Extensions/RandomExtensions.cs rename to Reactor/Utilities/Extensions/RandomExtensions.cs index fb2a587..a0c43bc 100644 --- a/Reactor/Extensions/RandomExtensions.cs +++ b/Reactor/Utilities/Extensions/RandomExtensions.cs @@ -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; } + + /// + /// Returns random from + /// + public static T? Random(this IEnumerable input) + { + var list = input as IList ?? input.ToList(); + return list.Count == 0 ? default : list[UnityEngine.Random.Range(0, list.Count)]; + } } diff --git a/Reactor/Extensions/ReflectionExtensions.cs b/Reactor/Utilities/Extensions/ReflectionExtensions.cs similarity index 79% rename from Reactor/Extensions/ReflectionExtensions.cs rename to Reactor/Utilities/Extensions/ReflectionExtensions.cs index 1f2eb91..3c3ccde 100644 --- a/Reactor/Extensions/ReflectionExtensions.cs +++ b/Reactor/Utilities/Extensions/ReflectionExtensions.cs @@ -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 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 GetMethods(this Type type, Type returnType, params Type[] parameterTypes) + { + return type.GetMethods(AccessTools.all, returnType, parameterTypes); + } } diff --git a/Reactor/Utilities/Extensions/StreamExtensions.cs b/Reactor/Utilities/Extensions/StreamExtensions.cs new file mode 100644 index 0000000..f17b668 --- /dev/null +++ b/Reactor/Utilities/Extensions/StreamExtensions.cs @@ -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()) + { + ClassInjector.DerivedConstructorBody(this); + _stream = stream; + } + + [HideFromIl2Cpp] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe Span GetSpan(Il2CppStructArray buffer, int offset, int count) + { + var rawBuffer = (byte*) buffer.Pointer + 4 * IntPtr.Size; + return new Span(rawBuffer + offset, count); + } + + public override int Read(Il2CppStructArray buffer, int offset, int count) + { + return _stream.Read(GetSpan(buffer, offset, count)); + } + + public override void Write(Il2CppStructArray 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; + } + } + + /// + /// Wraps a System Stream into an Il2Cpp Stream + /// + public static StreamWrapper AsIl2Cpp(this Stream stream) => new(stream); + + /// + /// Fully read stream, can be used as workaround for il2cpp streams. + /// + public static byte[] ReadFully(this Stream input) + { + using var ms = new MemoryStream(); + input.CopyTo(ms); + return ms.ToArray(); + } +} diff --git a/Reactor/Utilities/Extensions/UnityExtensions.cs b/Reactor/Utilities/Extensions/UnityExtensions.cs new file mode 100644 index 0000000..09a6b87 --- /dev/null +++ b/Reactor/Utilities/Extensions/UnityExtensions.cs @@ -0,0 +1,41 @@ +using UnityEngine; + +namespace Reactor.Utilities.Extensions; + +public static class UnityExtensions +{ + /// + /// Returns the color as a hexadecimal string in the format "RRGGBBAA". + /// + /// The color to be converted. + /// Hexadecimal string representing the color. + /// https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html + public static string ToHtmlStringRGBA(this Color32 color) + { + return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}"; + } + + /// + 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"); + + /// + /// Sets color outline for renderers using default Among Us shader + /// + 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); + } + } +} diff --git a/Reactor/Extensions/UnityObjectExtensions.cs b/Reactor/Utilities/Extensions/UnityObjectExtensions.cs similarity index 95% rename from Reactor/Extensions/UnityObjectExtensions.cs rename to Reactor/Utilities/Extensions/UnityObjectExtensions.cs index 2d7d186..81ffbd2 100644 --- a/Reactor/Extensions/UnityObjectExtensions.cs +++ b/Reactor/Utilities/Extensions/UnityObjectExtensions.cs @@ -1,6 +1,6 @@ using UnityEngine; -namespace Reactor.Extensions; +namespace Reactor.Utilities.Extensions; public static class UnityObjectExtensions { diff --git a/Reactor/Il2CppEqualityComparer.cs b/Reactor/Utilities/Il2CppEqualityComparer.cs similarity index 96% rename from Reactor/Il2CppEqualityComparer.cs rename to Reactor/Utilities/Il2CppEqualityComparer.cs index fbcad16..9500ea6 100644 --- a/Reactor/Il2CppEqualityComparer.cs +++ b/Reactor/Utilities/Il2CppEqualityComparer.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Il2CppSystem.Runtime.CompilerServices; -namespace Reactor; +namespace Reactor.Utilities; public sealed class Il2CppEqualityComparer : IEqualityComparer where T : Il2CppSystem.Object { diff --git a/Reactor/Utilities/ImGui/DragWindow.cs b/Reactor/Utilities/ImGui/DragWindow.cs new file mode 100644 index 0000000..ea1ca7f --- /dev/null +++ b/Reactor/Utilities/ImGui/DragWindow.cs @@ -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 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()) + { + } +} diff --git a/Reactor/Utilities/ImGui/Window.cs b/Reactor/Utilities/ImGui/Window.cs new file mode 100644 index 0000000..db88875 --- /dev/null +++ b/Reactor/Utilities/ImGui/Window.cs @@ -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 Func { get; set; } + public string Title { get; set; } + + public Window(Rect rect, string title, Action 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(); + } + } + } +} diff --git a/Reactor/Logger.cs b/Reactor/Utilities/Logger.cs similarity index 98% rename from Reactor/Logger.cs rename to Reactor/Utilities/Logger.cs index ef1786a..95d2e6e 100644 --- a/Reactor/Logger.cs +++ b/Reactor/Utilities/Logger.cs @@ -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 where T : BasePlugin { diff --git a/Reactor/PluginSingleton.cs b/Reactor/Utilities/PluginSingleton.cs similarity index 96% rename from Reactor/PluginSingleton.cs rename to Reactor/Utilities/PluginSingleton.cs index 55a7902..18937c0 100644 --- a/Reactor/PluginSingleton.cs +++ b/Reactor/Utilities/PluginSingleton.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Reflection; using BepInEx.Unity.IL2CPP; -namespace Reactor; +namespace Reactor.Utilities; public static class PluginSingleton where T : BasePlugin { diff --git a/Reactor/Patches/RegionInfoWatcher.cs b/Reactor/Utilities/RegionInfoWatcher.cs similarity index 95% rename from Reactor/Patches/RegionInfoWatcher.cs rename to Reactor/Utilities/RegionInfoWatcher.cs index aa2a043..461f1c7 100644 --- a/Reactor/Patches/RegionInfoWatcher.cs +++ b/Reactor/Utilities/RegionInfoWatcher.cs @@ -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 diff --git a/Reactor/Utilities/SceneManagerUtilities.cs b/Reactor/Utilities/SceneManagerUtilities.cs new file mode 100644 index 0000000..b0c51ce --- /dev/null +++ b/Reactor/Utilities/SceneManagerUtilities.cs @@ -0,0 +1,24 @@ +using System; +using UnityEngine.Events; +using UnityEngine.SceneManagement; + +namespace Reactor.Utilities; + +public static class SceneManagerUtilities +{ + /// + /// Adds a delegate to get one notification when a scene has loaded. + /// + public static void once_sceneLoaded(Action value) + { + UnityAction? unityAction = null; + + unityAction = (Action) ((scene, loadMode) => + { + SceneManager.remove_sceneLoaded(unityAction); + value.Invoke(scene, loadMode); + }); + + SceneManager.add_sceneLoaded(unityAction); + } +}