From 2060f7cf8ac9d857125fa1af84aed4027ee3df54 Mon Sep 17 00:00:00 2001 From: Alexejhero <32238504+Alexejhero@users.noreply.github.com> Date: Tue, 13 Aug 2024 22:37:17 +0300 Subject: [PATCH 01/13] Create reactor ping tracker --- .../Patches/Miscellaneous/PingTrackerPatch.cs | 16 ++++ Reactor/Patches/ReactorPingTracker.cs | 85 +++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 Reactor/Patches/Miscellaneous/PingTrackerPatch.cs create mode 100644 Reactor/Patches/ReactorPingTracker.cs diff --git a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs new file mode 100644 index 0000000..60235cb --- /dev/null +++ b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs @@ -0,0 +1,16 @@ +using System; +using HarmonyLib; + +namespace Reactor.Patches.Miscellaneous; + +[HarmonyPatch(typeof(PingTracker), nameof(PingTracker.Update))] +internal static class PingTrackerPatch +{ + [HarmonyPostfix] + [HarmonyPriority(-1000)] + public static void Postfix(PingTracker __instance) + { + if (!__instance.text.text.EndsWith("\n", StringComparison.InvariantCulture)) __instance.text.text += "\n"; + __instance.text.text += ReactorPingTracker.GetPingTrackerText(); + } +} diff --git a/Reactor/Patches/ReactorPingTracker.cs b/Reactor/Patches/ReactorPingTracker.cs new file mode 100644 index 0000000..0dc622b --- /dev/null +++ b/Reactor/Patches/ReactorPingTracker.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Reactor.Patches; + +/// +/// Controls the PingTracker. +/// +public static class ReactorPingTracker +{ + private struct ModIdentifier + { + private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff8"; + private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f008"; + + public string ModName; + public string Version; + public bool IsDevBuild; + public Func? ShouldShow; + + public readonly string Text => $"{ModName} {Version}"; + } + + private static readonly List _modIdentifiers = + [ + new ModIdentifier + { + ModName = ReactorPlugin.Name, + Version = ReactorPlugin.Version, +#if DEBUG + IsDevBuild = true, +#else + IsDevBuild = false, +#endif + ShouldShow = null//() => AmongUsClient.Instance.IsGameStarted, + } + ]; + + /// + /// Registers a mod with the PingTrackerManager, adding it to the list of mods that will be displayed in the PingTracker. + /// + /// The user-friendly name of the mod. Can contain spaces or special characters. + /// The version of the mod. + /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. + /// This function will be called every frame to determine if the mod should be displayed or not. This function should return false if your mod is currently disabled or has no effect on gameplay at the time. If you want the mod to be displayed at all times, set this parameter to null to avoid delegate calls. + public static void RegisterMod(string modName, string version, bool isDevOrBetaBuild, Func? shouldShow) + { + if (modName.Contains("", StringComparison.OrdinalIgnoreCase) || version.Contains("", StringComparison.OrdinalIgnoreCase)) + { + Error($"Not registering mod \"{modName}\" with version \"{version}\" in PingTrackerManager because it contains the string \"\" which is disallowed."); + return; + } + + if (_modIdentifiers.Any(m => m.ModName == modName)) + { + Error($"Mod \"{modName}\" is already registered in PingTrackerManager."); + return; + } + + _modIdentifiers.Add(new ModIdentifier + { + ModName = modName, + Version = version, + IsDevBuild = isDevOrBetaBuild, + ShouldShow = shouldShow, + }); + + _modIdentifiers.Sort((a, b) => string.Compare(a.ModName, b.ModName, StringComparison.Ordinal)); + + if (!isDevOrBetaBuild) + { + Info($"Mod \"{modName}\" registered in PingTrackerManager with version {version}."); + } + else + { + Warning($"Mod \"{modName}\" registered in PingTrackerManager with DEVELOPMENT/BETA version {version}."); + } + } + + internal static string GetPingTrackerText() + { + return " " + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow?.Invoke() ?? true).Select(m => m.Text)) + ""; + } +} From 524549fb298fa254e18c60b2c01a75945f144d8a Mon Sep 17 00:00:00 2001 From: Alexejhero <32238504+Alexejhero@users.noreply.github.com> Date: Tue, 13 Aug 2024 22:41:13 +0300 Subject: [PATCH 02/13] Fix warnings (i forgor) --- Reactor/Patches/ReactorPingTracker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Reactor/Patches/ReactorPingTracker.cs b/Reactor/Patches/ReactorPingTracker.cs index 0dc622b..0e2fc5e 100644 --- a/Reactor/Patches/ReactorPingTracker.cs +++ b/Reactor/Patches/ReactorPingTracker.cs @@ -33,7 +33,7 @@ private struct ModIdentifier #else IsDevBuild = false, #endif - ShouldShow = null//() => AmongUsClient.Instance.IsGameStarted, + ShouldShow = () => AmongUsClient.Instance.IsGameStarted, } ]; @@ -43,7 +43,7 @@ private struct ModIdentifier /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. - /// This function will be called every frame to determine if the mod should be displayed or not. This function should return false if your mod is currently disabled or has no effect on gameplay at the time. If you want the mod to be displayed at all times, set this parameter to null to avoid delegate calls. + /// This function will be called every frame to determine if the mod should be displayed or not. This function should return false if your mod is currently disabled or has no effect on gameplay at the time. If you want the mod to be displayed at all times, you can set this parameter to null to avoid delegate calls. public static void RegisterMod(string modName, string version, bool isDevOrBetaBuild, Func? shouldShow) { if (modName.Contains("", StringComparison.OrdinalIgnoreCase) || version.Contains("", StringComparison.OrdinalIgnoreCase)) From c422098d96455c0be525b6af4c2ab18086ff266c Mon Sep 17 00:00:00 2001 From: Alexejhero <32238504+Alexejhero@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:52:34 +0300 Subject: [PATCH 03/13] Improve ModIdentifier struct and address reviews --- Reactor/Patches/ReactorPingTracker.cs | 51 ++++++++++----------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/Reactor/Patches/ReactorPingTracker.cs b/Reactor/Patches/ReactorPingTracker.cs index 0e2fc5e..59b6d03 100644 --- a/Reactor/Patches/ReactorPingTracker.cs +++ b/Reactor/Patches/ReactorPingTracker.cs @@ -9,36 +9,21 @@ namespace Reactor.Patches; /// public static class ReactorPingTracker { - private struct ModIdentifier + private readonly struct ModIdentifier(string modName, string version, bool isDevBuild, Func? shouldShow) { private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff8"; private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f008"; - public string ModName; - public string Version; - public bool IsDevBuild; - public Func? ShouldShow; + public string ModName => modName; + public string Text => $"{ModName} {version}"; - public readonly string Text => $"{ModName} {Version}"; + public bool ShouldShow() => shouldShow?.Invoke() ?? true; } - private static readonly List _modIdentifiers = - [ - new ModIdentifier - { - ModName = ReactorPlugin.Name, - Version = ReactorPlugin.Version, -#if DEBUG - IsDevBuild = true, -#else - IsDevBuild = false, -#endif - ShouldShow = () => AmongUsClient.Instance.IsGameStarted, - } - ]; + private static readonly List _modIdentifiers = []; /// - /// Registers a mod with the PingTrackerManager, adding it to the list of mods that will be displayed in the PingTracker. + /// Registers a mod with the , adding it to the list of mods that will be displayed in the PingTracker. /// /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. @@ -46,40 +31,40 @@ private struct ModIdentifier /// This function will be called every frame to determine if the mod should be displayed or not. This function should return false if your mod is currently disabled or has no effect on gameplay at the time. If you want the mod to be displayed at all times, you can set this parameter to null to avoid delegate calls. public static void RegisterMod(string modName, string version, bool isDevOrBetaBuild, Func? shouldShow) { + if (modName.Length + version.Length > 60) + { + Error($"Not registering mod \"{modName}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because the combined length of the mod name and version is greater than 60 characters."); + return; + } + if (modName.Contains("", StringComparison.OrdinalIgnoreCase) || version.Contains("", StringComparison.OrdinalIgnoreCase)) { - Error($"Not registering mod \"{modName}\" with version \"{version}\" in PingTrackerManager because it contains the string \"\" which is disallowed."); + Error($"Not registering mod \"{modName}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because it contains the string \"\" which is disallowed."); return; } if (_modIdentifiers.Any(m => m.ModName == modName)) { - Error($"Mod \"{modName}\" is already registered in PingTrackerManager."); + Error($"Mod \"{modName}\" is already registered in {nameof(ReactorPingTracker)}."); return; } - _modIdentifiers.Add(new ModIdentifier - { - ModName = modName, - Version = version, - IsDevBuild = isDevOrBetaBuild, - ShouldShow = shouldShow, - }); + _modIdentifiers.Add(new ModIdentifier(modName, version, isDevOrBetaBuild, shouldShow)); _modIdentifiers.Sort((a, b) => string.Compare(a.ModName, b.ModName, StringComparison.Ordinal)); if (!isDevOrBetaBuild) { - Info($"Mod \"{modName}\" registered in PingTrackerManager with version {version}."); + Info($"Mod \"{modName}\" registered in {nameof(ReactorPingTracker)} with version {version}."); } else { - Warning($"Mod \"{modName}\" registered in PingTrackerManager with DEVELOPMENT/BETA version {version}."); + Warning($"Mod \"{modName}\" registered in {nameof(ReactorPingTracker)} with DEVELOPMENT/BETA version {version}."); } } internal static string GetPingTrackerText() { - return " " + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow?.Invoke() ?? true).Select(m => m.Text)) + ""; + return " " + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow()).Select(m => m.Text)) + ""; } } From 7abf16e33d8bf833dbea85e293932a2eb8a05ff6 Mon Sep 17 00:00:00 2001 From: Alexejhero <32238504+Alexejhero@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:59:02 +0300 Subject: [PATCH 04/13] Update pingtracker alpha in-game --- Reactor/Patches/ReactorPingTracker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Reactor/Patches/ReactorPingTracker.cs b/Reactor/Patches/ReactorPingTracker.cs index 59b6d03..d117cbd 100644 --- a/Reactor/Patches/ReactorPingTracker.cs +++ b/Reactor/Patches/ReactorPingTracker.cs @@ -11,8 +11,8 @@ public static class ReactorPingTracker { private readonly struct ModIdentifier(string modName, string version, bool isDevBuild, Func? shouldShow) { - private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff8"; - private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f008"; + private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff7"; + private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f007"; public string ModName => modName; public string Text => $"{ModName} {version}"; From bd2be1b7dead8362cf8354816382df593580e9fb Mon Sep 17 00:00:00 2001 From: Alexejhero <32238504+Alexejhero@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:16:38 +0300 Subject: [PATCH 05/13] Update PingTracker visuals --- Reactor/Patches/ReactorPingTracker.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Reactor/Patches/ReactorPingTracker.cs b/Reactor/Patches/ReactorPingTracker.cs index d117cbd..470111f 100644 --- a/Reactor/Patches/ReactorPingTracker.cs +++ b/Reactor/Patches/ReactorPingTracker.cs @@ -9,10 +9,10 @@ namespace Reactor.Patches; /// public static class ReactorPingTracker { - private readonly struct ModIdentifier(string modName, string version, bool isDevBuild, Func? shouldShow) + private readonly struct ModIdentifier(string modName, string version, Func? shouldShow, bool isDevBuild) { private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff7"; - private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f007"; + private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f447"; public string ModName => modName; public string Text => $"{ModName} {version}"; @@ -27,9 +27,9 @@ private readonly struct ModIdentifier(string modName, string version, bool isDev /// /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. - /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. /// This function will be called every frame to determine if the mod should be displayed or not. This function should return false if your mod is currently disabled or has no effect on gameplay at the time. If you want the mod to be displayed at all times, you can set this parameter to null to avoid delegate calls. - public static void RegisterMod(string modName, string version, bool isDevOrBetaBuild, Func? shouldShow) + /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. + public static void RegisterMod(string modName, string version, Func? shouldShow, bool isDevOrBetaBuild = false) { if (modName.Length + version.Length > 60) { @@ -49,7 +49,7 @@ public static void RegisterMod(string modName, string version, bool isDevOrBetaB return; } - _modIdentifiers.Add(new ModIdentifier(modName, version, isDevOrBetaBuild, shouldShow)); + _modIdentifiers.Add(new ModIdentifier(modName, version, shouldShow, isDevOrBetaBuild)); _modIdentifiers.Sort((a, b) => string.Compare(a.ModName, b.ModName, StringComparison.Ordinal)); From 267b7e6e74bfcae3b92bebedc05b86afe9eb3f9b Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 20:00:29 +0200 Subject: [PATCH 06/13] Change namespace --- Reactor/Patches/Miscellaneous/PingTrackerPatch.cs | 1 + Reactor/{Patches => Utilities}/ReactorPingTracker.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename Reactor/{Patches => Utilities}/ReactorPingTracker.cs (99%) diff --git a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs index 60235cb..1a6b52d 100644 --- a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs +++ b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs @@ -1,5 +1,6 @@ using System; using HarmonyLib; +using Reactor.Utilities; namespace Reactor.Patches.Miscellaneous; diff --git a/Reactor/Patches/ReactorPingTracker.cs b/Reactor/Utilities/ReactorPingTracker.cs similarity index 99% rename from Reactor/Patches/ReactorPingTracker.cs rename to Reactor/Utilities/ReactorPingTracker.cs index 470111f..fc527d7 100644 --- a/Reactor/Patches/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorPingTracker.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace Reactor.Patches; +namespace Reactor.Utilities; /// /// Controls the PingTracker. From 7d0a8b1a60be9eedcf4d8d9d81e3f94bba82f750 Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 16:52:35 +0200 Subject: [PATCH 07/13] Get rid of magic values --- .../Patches/Miscellaneous/PingTrackerPatch.cs | 2 +- Reactor/Utilities/ReactorPingTracker.cs | 21 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs index 1a6b52d..945aff7 100644 --- a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs +++ b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs @@ -8,7 +8,7 @@ namespace Reactor.Patches.Miscellaneous; internal static class PingTrackerPatch { [HarmonyPostfix] - [HarmonyPriority(-1000)] + [HarmonyPriority(Priority.Last)] public static void Postfix(PingTracker __instance) { if (!__instance.text.text.EndsWith("\n", StringComparison.InvariantCulture)) __instance.text.text += "\n"; diff --git a/Reactor/Utilities/ReactorPingTracker.cs b/Reactor/Utilities/ReactorPingTracker.cs index fc527d7..a53f3fe 100644 --- a/Reactor/Utilities/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorPingTracker.cs @@ -17,23 +17,34 @@ private readonly struct ModIdentifier(string modName, string version, Func public string ModName => modName; public string Text => $"{ModName} {version}"; - public bool ShouldShow() => shouldShow?.Invoke() ?? true; + public bool ShouldShow() => shouldShow == AlwaysShow || shouldShow(); } private static readonly List _modIdentifiers = []; + /// + /// A special value indicating a mod should always show. + /// + public const Func? AlwaysShow = null; + /// /// Registers a mod with the , adding it to the list of mods that will be displayed in the PingTracker. /// /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. - /// This function will be called every frame to determine if the mod should be displayed or not. This function should return false if your mod is currently disabled or has no effect on gameplay at the time. If you want the mod to be displayed at all times, you can set this parameter to null to avoid delegate calls. + /// + /// This function will be called every frame to determine if the mod should be displayed or not. + /// This function should return false if your mod is currently disabled or has no effect on gameplay at the time. + /// If you want the mod to be displayed at all times, you can set this parameter to . + /// /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. public static void RegisterMod(string modName, string version, Func? shouldShow, bool isDevOrBetaBuild = false) { - if (modName.Length + version.Length > 60) + const int MaxLength = 60; + + if (modName.Length + version.Length > MaxLength) { - Error($"Not registering mod \"{modName}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because the combined length of the mod name and version is greater than 60 characters."); + Error($"Not registering mod \"{modName}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because the combined length of the mod name and version is greater than {MaxLength} characters."); return; } @@ -65,6 +76,6 @@ public static void RegisterMod(string modName, string version, Func? shoul internal static string GetPingTrackerText() { - return " " + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow()).Select(m => m.Text)) + ""; + return "" + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow()).Select(m => m.Text)) + ""; } } From 7e90896f5b8d7b7e83895c04708444716a5be168 Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 17:02:02 +0200 Subject: [PATCH 08/13] RegisterMod -> Register This is more inline with other utilities --- Reactor/Utilities/ReactorPingTracker.cs | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Reactor/Utilities/ReactorPingTracker.cs b/Reactor/Utilities/ReactorPingTracker.cs index a53f3fe..719afa3 100644 --- a/Reactor/Utilities/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorPingTracker.cs @@ -9,15 +9,15 @@ namespace Reactor.Utilities; /// public static class ReactorPingTracker { - private readonly struct ModIdentifier(string modName, string version, Func? shouldShow, bool isDevBuild) + private readonly struct ModIdentifier(string name, string version, Func? shouldShow, bool isDevBuild) { private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff7"; private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f447"; - public string ModName => modName; - public string Text => $"{ModName} {version}"; + public string Name => name; - public bool ShouldShow() => shouldShow == AlwaysShow || shouldShow(); + public bool ShouldShow => shouldShow == AlwaysShow || shouldShow(); + public string Text => $"{Name} {version}"; } private static readonly List _modIdentifiers = []; @@ -30,7 +30,7 @@ private readonly struct ModIdentifier(string modName, string version, Func /// /// Registers a mod with the , adding it to the list of mods that will be displayed in the PingTracker. /// - /// The user-friendly name of the mod. Can contain spaces or special characters. + /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. /// /// This function will be called every frame to determine if the mod should be displayed or not. @@ -38,39 +38,39 @@ private readonly struct ModIdentifier(string modName, string version, Func /// If you want the mod to be displayed at all times, you can set this parameter to . /// /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. - public static void RegisterMod(string modName, string version, Func? shouldShow, bool isDevOrBetaBuild = false) + public static void Register(string name, string version, Func? shouldShow, bool isDevOrBetaBuild = false) { const int MaxLength = 60; - if (modName.Length + version.Length > MaxLength) + if (name.Length + version.Length > MaxLength) { - Error($"Not registering mod \"{modName}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because the combined length of the mod name and version is greater than {MaxLength} characters."); + Error($"Not registering mod \"{name}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because the combined length of the mod name and version is greater than {MaxLength} characters."); return; } - if (modName.Contains("", StringComparison.OrdinalIgnoreCase) || version.Contains("", StringComparison.OrdinalIgnoreCase)) + if (name.Contains("", StringComparison.OrdinalIgnoreCase) || version.Contains("", StringComparison.OrdinalIgnoreCase)) { - Error($"Not registering mod \"{modName}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because it contains the string \"\" which is disallowed."); + Error($"Not registering mod \"{name}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because it contains the string \"\" which is disallowed."); return; } - if (_modIdentifiers.Any(m => m.ModName == modName)) + if (_modIdentifiers.Any(m => m.Name == name)) { - Error($"Mod \"{modName}\" is already registered in {nameof(ReactorPingTracker)}."); + Error($"Mod \"{name}\" is already registered in {nameof(ReactorPingTracker)}."); return; } - _modIdentifiers.Add(new ModIdentifier(modName, version, shouldShow, isDevOrBetaBuild)); + _modIdentifiers.Add(new ModIdentifier(name, version, shouldShow, isDevOrBetaBuild)); - _modIdentifiers.Sort((a, b) => string.Compare(a.ModName, b.ModName, StringComparison.Ordinal)); + _modIdentifiers.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal)); if (!isDevOrBetaBuild) { - Info($"Mod \"{modName}\" registered in {nameof(ReactorPingTracker)} with version {version}."); + Info($"Mod \"{name}\" registered in {nameof(ReactorPingTracker)} with version {version}."); } else { - Warning($"Mod \"{modName}\" registered in {nameof(ReactorPingTracker)} with DEVELOPMENT/BETA version {version}."); + Warning($"Mod \"{name}\" registered in {nameof(ReactorPingTracker)} with DEVELOPMENT/BETA version {version}."); } } From 4036a59b8450f1c2fb421aa0f105d252e02791f6 Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 17:54:26 +0200 Subject: [PATCH 09/13] Add an overload for registering using plugin metadata --- Reactor/Utilities/ReactorPingTracker.cs | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/Reactor/Utilities/ReactorPingTracker.cs b/Reactor/Utilities/ReactorPingTracker.cs index 719afa3..3468303 100644 --- a/Reactor/Utilities/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorPingTracker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using BepInEx.Unity.IL2CPP; namespace Reactor.Utilities; @@ -9,7 +10,7 @@ namespace Reactor.Utilities; /// public static class ReactorPingTracker { - private readonly struct ModIdentifier(string name, string version, Func? shouldShow, bool isDevBuild) + private readonly struct ModIdentifier(string name, string version, Func? shouldShow, bool isPreRelease) { private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff7"; private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f447"; @@ -17,7 +18,7 @@ private readonly struct ModIdentifier(string name, string version, Func? s public string Name => name; public bool ShouldShow => shouldShow == AlwaysShow || shouldShow(); - public string Text => $"{Name} {version}"; + public string Text => $"{Name} {version}"; } private static readonly List _modIdentifiers = []; @@ -37,8 +38,8 @@ private readonly struct ModIdentifier(string name, string version, Func? s /// This function should return false if your mod is currently disabled or has no effect on gameplay at the time. /// If you want the mod to be displayed at all times, you can set this parameter to . /// - /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. - public static void Register(string name, string version, Func? shouldShow, bool isDevOrBetaBuild = false) + /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. + public static void Register(string name, string version, Func? shouldShow, bool isPreRelease = false) { const int MaxLength = 60; @@ -60,11 +61,11 @@ public static void Register(string name, string version, Func? shouldShow, return; } - _modIdentifiers.Add(new ModIdentifier(name, version, shouldShow, isDevOrBetaBuild)); + _modIdentifiers.Add(new ModIdentifier(name, version, shouldShow, isPreRelease)); _modIdentifiers.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal)); - if (!isDevOrBetaBuild) + if (!isPreRelease) { Info($"Mod \"{name}\" registered in {nameof(ReactorPingTracker)} with version {version}."); } @@ -74,8 +75,23 @@ public static void Register(string name, string version, Func? shouldShow, } } + /// + /// Registers a mod with the , adding it to the list of mods that will be displayed in the PingTracker. + /// + /// The BepInEx plugin type to get the name and version from. + /// + public static void Register(Func? shouldShow) where T : BasePlugin + { + var pluginInfo = IL2CPPChainloader.Instance.Plugins.Values.SingleOrDefault(p => p.TypeName == typeof(T).FullName) + ?? throw new ArgumentException("Couldn't find the metadata for the provided plugin type", nameof(T)); + + var metadata = pluginInfo.Metadata; + + Register(metadata.Name, metadata.Version.ToString(), shouldShow, metadata.Version.IsPreRelease); + } + internal static string GetPingTrackerText() { - return "" + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow()).Select(m => m.Text)) + ""; + return "" + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow).Select(m => m.Text)) + ""; } } From 09cea54ea7e787746df5220c60f1718160dae16a Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 20:00:37 +0200 Subject: [PATCH 10/13] Register in Reactor.Example --- Reactor.Example/ExamplePlugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Reactor.Example/ExamplePlugin.cs b/Reactor.Example/ExamplePlugin.cs index 4a768e8..8a2c23b 100644 --- a/Reactor.Example/ExamplePlugin.cs +++ b/Reactor.Example/ExamplePlugin.cs @@ -25,6 +25,8 @@ public partial class ExamplePlugin : BasePlugin public override void Load() { + ReactorPingTracker.Register(ReactorPingTracker.AlwaysShow); + this.AddComponent(); _helloStringName = CustomStringName.CreateAndRegister("Hello!"); From 03c6315004bbef566a0742ec1f93181da06b605f Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 19:57:40 +0200 Subject: [PATCH 11/13] Refactor --- .../Patches/Miscellaneous/PingTrackerPatch.cs | 8 +++- .../Extensions/RichTextExtensions.cs | 40 +++++++++++++++++++ Reactor/Utilities/ReactorPingTracker.cs | 29 +++++++------- 3 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 Reactor/Utilities/Extensions/RichTextExtensions.cs diff --git a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs index 945aff7..071dc3f 100644 --- a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs +++ b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs @@ -11,7 +11,11 @@ internal static class PingTrackerPatch [HarmonyPriority(Priority.Last)] public static void Postfix(PingTracker __instance) { - if (!__instance.text.text.EndsWith("\n", StringComparison.InvariantCulture)) __instance.text.text += "\n"; - __instance.text.text += ReactorPingTracker.GetPingTrackerText(); + var extraText = ReactorPingTracker.GetText(); + if (extraText != null) + { + if (!__instance.text.text.EndsWith("\n", StringComparison.InvariantCulture)) __instance.text.text += "\n"; + __instance.text.text += extraText; + } } } diff --git a/Reactor/Utilities/Extensions/RichTextExtensions.cs b/Reactor/Utilities/Extensions/RichTextExtensions.cs new file mode 100644 index 0000000..156a8c0 --- /dev/null +++ b/Reactor/Utilities/Extensions/RichTextExtensions.cs @@ -0,0 +1,40 @@ +namespace Reactor.Utilities.Extensions; + +/// +/// Provides extension methods for TestMeshPro's Rich Text. +/// +internal static class RichTextExtensions +{ + private static string Wrap(this string text, string tag) + { + return $"<{tag}>{text}"; + } + + private static string Wrap(this string text, string tag, string value) + { + return $"<{tag}={value}>{text}"; + } + + public static string Align(this string text, string value) + { + return text.Wrap("align", value); + } + + public static string Color(this string text, string value) + { + return text.Wrap("color", value); + } + + public static string Size(this string text, string value) + { + return text.Wrap("size", value); + } + + public static string EscapeRichText(this string text) + { + return text + .Replace("", string.Empty) + .Replace("", string.Empty) + .Wrap("noparse"); + } +} diff --git a/Reactor/Utilities/ReactorPingTracker.cs b/Reactor/Utilities/ReactorPingTracker.cs index 3468303..beb8325 100644 --- a/Reactor/Utilities/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorPingTracker.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using BepInEx.Unity.IL2CPP; +using Reactor.Utilities.Extensions; namespace Reactor.Utilities; @@ -12,13 +13,13 @@ public static class ReactorPingTracker { private readonly struct ModIdentifier(string name, string version, Func? shouldShow, bool isPreRelease) { - private static string NormalColor => !AmongUsClient.Instance.IsGameStarted ? "#fff" : "#fff7"; - private static string DevColor => !AmongUsClient.Instance.IsGameStarted ? "#f00" : "#f447"; + private const string NormalColor = "#fff"; + private const string PreReleaseColor = "#f00"; public string Name => name; public bool ShouldShow => shouldShow == AlwaysShow || shouldShow(); - public string Text => $"{Name} {version}"; + public string Text { get; } = $"{name} {version}".EscapeRichText().Color(isPreRelease ? PreReleaseColor : NormalColor); } private static readonly List _modIdentifiers = []; @@ -33,13 +34,13 @@ private readonly struct ModIdentifier(string name, string version, Func? s /// /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. + /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. /// /// This function will be called every frame to determine if the mod should be displayed or not. /// This function should return false if your mod is currently disabled or has no effect on gameplay at the time. /// If you want the mod to be displayed at all times, you can set this parameter to . /// - /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. - public static void Register(string name, string version, Func? shouldShow, bool isPreRelease = false) + public static void Register(string name, string version, bool isPreRelease, Func? shouldShow) { const int MaxLength = 60; @@ -49,12 +50,6 @@ public static void Register(string name, string version, Func? shouldShow, return; } - if (name.Contains("", StringComparison.OrdinalIgnoreCase) || version.Contains("", StringComparison.OrdinalIgnoreCase)) - { - Error($"Not registering mod \"{name}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because it contains the string \"\" which is disallowed."); - return; - } - if (_modIdentifiers.Any(m => m.Name == name)) { Error($"Mod \"{name}\" is already registered in {nameof(ReactorPingTracker)}."); @@ -87,11 +82,17 @@ public static void Register(Func? shouldShow) where T : BasePlugin var metadata = pluginInfo.Metadata; - Register(metadata.Name, metadata.Version.ToString(), shouldShow, metadata.Version.IsPreRelease); + Register(metadata.Name, metadata.Version.ToString(), metadata.Version.IsPreRelease, shouldShow); } - internal static string GetPingTrackerText() + internal static string? GetText() { - return "" + string.Join(", ", _modIdentifiers.Where(m => m.ShouldShow).Select(m => m.Text)) + ""; + var mods = _modIdentifiers.Where(m => m.ShouldShow).Select(m => m.Text).ToArray(); + if (mods.Length == 0) + { + return null; + } + + return ("" + string.Join(", ", mods)).Size("50%").Align("center"); } } From c4c243b548a22a2cb0db7fa5f7df4610b00fa38b Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 20:48:27 +0200 Subject: [PATCH 12/13] Strip build from versions --- Reactor/Patches/ReactorVersionShower.cs | 9 ++------- .../Utilities/Extensions/VersionExtensions.cs | 19 +++++++++++++++++++ Reactor/Utilities/ReactorPingTracker.cs | 2 +- 3 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 Reactor/Utilities/Extensions/VersionExtensions.cs diff --git a/Reactor/Patches/ReactorVersionShower.cs b/Reactor/Patches/ReactorVersionShower.cs index 6ed8f2a..16c47df 100644 --- a/Reactor/Patches/ReactorVersionShower.cs +++ b/Reactor/Patches/ReactorVersionShower.cs @@ -89,19 +89,14 @@ internal static void Initialize() })); } - private static string ToStringWithoutBuild(Version version) - { - return $"{version.Major}.{version.Minor}.{version.Patch}{(version.PreRelease == null ? string.Empty : $"-{version.PreRelease}")}"; - } - /// /// Updates with reactor version and fires . /// public static void UpdateText() { if (Text == null) return; - Text.text = "Reactor " + ReactorPlugin.Version; - Text.text += "\nBepInEx " + ToStringWithoutBuild(Paths.BepInExVersion); + Text.text = "Reactor " + Version.Parse(ReactorPlugin.Version).WithoutBuild(); + Text.text += "\nBepInEx " + Paths.BepInExVersion.WithoutBuild(); Text.text += "\nMods: " + IL2CPPChainloader.Instance.Plugins.Count; TextUpdated?.Invoke(Text); } diff --git a/Reactor/Utilities/Extensions/VersionExtensions.cs b/Reactor/Utilities/Extensions/VersionExtensions.cs new file mode 100644 index 0000000..947dd80 --- /dev/null +++ b/Reactor/Utilities/Extensions/VersionExtensions.cs @@ -0,0 +1,19 @@ +using SemanticVersioning; + +namespace Reactor.Utilities.Extensions; + +/// +/// Provides extension methods for . +/// +public static class VersionExtensions +{ + /// + /// Gets the provided without the build string (everything after the + symbol like the commit hash is stripped). + /// + /// The . + /// The without build. + public static Version WithoutBuild(this Version version) + { + return new Version(version.Major, version.Minor, version.Patch, version.PreRelease); + } +} diff --git a/Reactor/Utilities/ReactorPingTracker.cs b/Reactor/Utilities/ReactorPingTracker.cs index beb8325..0c6d29d 100644 --- a/Reactor/Utilities/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorPingTracker.cs @@ -82,7 +82,7 @@ public static void Register(Func? shouldShow) where T : BasePlugin var metadata = pluginInfo.Metadata; - Register(metadata.Name, metadata.Version.ToString(), metadata.Version.IsPreRelease, shouldShow); + Register(metadata.Name, metadata.Version.WithoutBuild().Clean(), metadata.Version.IsPreRelease, shouldShow); } internal static string? GetText() From b5b24a7c107c942f7f29ee4acc1b26a4b6e0aa36 Mon Sep 17 00:00:00 2001 From: js6pak Date: Tue, 20 Aug 2024 21:05:49 +0200 Subject: [PATCH 13/13] Add mainmenu support --- Reactor.Example/ExamplePlugin.cs | 2 +- .../Patches/Miscellaneous/PingTrackerPatch.cs | 2 +- Reactor/Patches/ReactorVersionShower.cs | 8 +++ ...eactorPingTracker.cs => ReactorCredits.cs} | 71 +++++++++++++------ 4 files changed, 58 insertions(+), 25 deletions(-) rename Reactor/Utilities/{ReactorPingTracker.cs => ReactorCredits.cs} (55%) diff --git a/Reactor.Example/ExamplePlugin.cs b/Reactor.Example/ExamplePlugin.cs index 8a2c23b..c14e8b9 100644 --- a/Reactor.Example/ExamplePlugin.cs +++ b/Reactor.Example/ExamplePlugin.cs @@ -25,7 +25,7 @@ public partial class ExamplePlugin : BasePlugin public override void Load() { - ReactorPingTracker.Register(ReactorPingTracker.AlwaysShow); + ReactorCredits.Register(ReactorCredits.AlwaysShow); this.AddComponent(); diff --git a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs index 071dc3f..0662758 100644 --- a/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs +++ b/Reactor/Patches/Miscellaneous/PingTrackerPatch.cs @@ -11,7 +11,7 @@ internal static class PingTrackerPatch [HarmonyPriority(Priority.Last)] public static void Postfix(PingTracker __instance) { - var extraText = ReactorPingTracker.GetText(); + var extraText = ReactorCredits.GetText(ReactorCredits.Location.PingTracker); if (extraText != null) { if (!__instance.text.text.EndsWith("\n", StringComparison.InvariantCulture)) __instance.text.text += "\n"; diff --git a/Reactor/Patches/ReactorVersionShower.cs b/Reactor/Patches/ReactorVersionShower.cs index 16c47df..d6b0559 100644 --- a/Reactor/Patches/ReactorVersionShower.cs +++ b/Reactor/Patches/ReactorVersionShower.cs @@ -2,6 +2,7 @@ using BepInEx; using BepInEx.Unity.IL2CPP; using HarmonyLib; +using Reactor.Utilities; using Reactor.Utilities.Extensions; using TMPro; using UnityEngine; @@ -98,6 +99,13 @@ public static void UpdateText() Text.text = "Reactor " + Version.Parse(ReactorPlugin.Version).WithoutBuild(); Text.text += "\nBepInEx " + Paths.BepInExVersion.WithoutBuild(); Text.text += "\nMods: " + IL2CPPChainloader.Instance.Plugins.Count; + + var creditsText = ReactorCredits.GetText(ReactorCredits.Location.MainMenu); + if (creditsText != null) + { + Text.text += "\n" + creditsText; + } + TextUpdated?.Invoke(Text); } diff --git a/Reactor/Utilities/ReactorPingTracker.cs b/Reactor/Utilities/ReactorCredits.cs similarity index 55% rename from Reactor/Utilities/ReactorPingTracker.cs rename to Reactor/Utilities/ReactorCredits.cs index 0c6d29d..7a574c8 100644 --- a/Reactor/Utilities/ReactorPingTracker.cs +++ b/Reactor/Utilities/ReactorCredits.cs @@ -2,57 +2,78 @@ using System.Collections.Generic; using System.Linq; using BepInEx.Unity.IL2CPP; +using Reactor.Patches; using Reactor.Utilities.Extensions; namespace Reactor.Utilities; /// -/// Controls the PingTracker. +/// Provides a way for mods to show their version information in-game. /// -public static class ReactorPingTracker +public static class ReactorCredits { - private readonly struct ModIdentifier(string name, string version, Func? shouldShow, bool isPreRelease) + private readonly struct ModIdentifier(string name, string version, Func? shouldShow, bool isPreRelease) { private const string NormalColor = "#fff"; private const string PreReleaseColor = "#f00"; public string Name => name; - public bool ShouldShow => shouldShow == AlwaysShow || shouldShow(); public string Text { get; } = $"{name} {version}".EscapeRichText().Color(isPreRelease ? PreReleaseColor : NormalColor); + + public bool ShouldShow(Location location) + { + return shouldShow == AlwaysShow || shouldShow(location); + } } private static readonly List _modIdentifiers = []; + /// + /// Represents the location of where the credit is shown. + /// + public enum Location + { + /// + /// In the main menu under Reactor/BepInEx versions. + /// + MainMenu, + + /// + /// During game under the ping tracker. + /// + PingTracker, + } + /// /// A special value indicating a mod should always show. /// - public const Func? AlwaysShow = null; + public const Func? AlwaysShow = null; /// - /// Registers a mod with the , adding it to the list of mods that will be displayed in the PingTracker. + /// Registers a mod with the , adding it to the list of mods that will be displayed. /// /// The user-friendly name of the mod. Can contain spaces or special characters. /// The version of the mod. - /// If this version is a development or beta version. If true, it will display the mod in red in the PingTracker. + /// If this version is a development or beta version. If true, it will display the mod in red. /// /// This function will be called every frame to determine if the mod should be displayed or not. /// This function should return false if your mod is currently disabled or has no effect on gameplay at the time. - /// If you want the mod to be displayed at all times, you can set this parameter to . + /// If you want the mod to be displayed at all times, you can set this parameter to . /// - public static void Register(string name, string version, bool isPreRelease, Func? shouldShow) + public static void Register(string name, string version, bool isPreRelease, Func? shouldShow) { const int MaxLength = 60; if (name.Length + version.Length > MaxLength) { - Error($"Not registering mod \"{name}\" with version \"{version}\" in {nameof(ReactorPingTracker)} because the combined length of the mod name and version is greater than {MaxLength} characters."); + Error($"Not registering mod \"{name}\" with version \"{version}\" in {nameof(ReactorCredits)} because the combined length of the mod name and version is greater than {MaxLength} characters."); return; } if (_modIdentifiers.Any(m => m.Name == name)) { - Error($"Mod \"{name}\" is already registered in {nameof(ReactorPingTracker)}."); + Error($"Mod \"{name}\" is already registered in {nameof(ReactorCredits)}."); return; } @@ -62,20 +83,22 @@ public static void Register(string name, string version, bool isPreRelease, Func if (!isPreRelease) { - Info($"Mod \"{name}\" registered in {nameof(ReactorPingTracker)} with version {version}."); + Info($"Mod \"{name}\" registered in {nameof(ReactorCredits)} with version {version}."); } else { - Warning($"Mod \"{name}\" registered in {nameof(ReactorPingTracker)} with DEVELOPMENT/BETA version {version}."); + Warning($"Mod \"{name}\" registered in {nameof(ReactorCredits)} with DEVELOPMENT/BETA version {version}."); } + + ReactorVersionShower.UpdateText(); } /// - /// Registers a mod with the , adding it to the list of mods that will be displayed in the PingTracker. + /// Registers a mod with the , adding it to the list of mods that will be displayed. /// /// The BepInEx plugin type to get the name and version from. - /// - public static void Register(Func? shouldShow) where T : BasePlugin + /// + public static void Register(Func? shouldShow) where T : BasePlugin { var pluginInfo = IL2CPPChainloader.Instance.Plugins.Values.SingleOrDefault(p => p.TypeName == typeof(T).FullName) ?? throw new ArgumentException("Couldn't find the metadata for the provided plugin type", nameof(T)); @@ -85,14 +108,16 @@ public static void Register(Func? shouldShow) where T : BasePlugin Register(metadata.Name, metadata.Version.WithoutBuild().Clean(), metadata.Version.IsPreRelease, shouldShow); } - internal static string? GetText() + internal static string? GetText(Location location) { - var mods = _modIdentifiers.Where(m => m.ShouldShow).Select(m => m.Text).ToArray(); - if (mods.Length == 0) - { - return null; - } + var modTexts = _modIdentifiers.Where(m => m.ShouldShow(location)).Select(m => m.Text).ToArray(); + if (modTexts.Length == 0) return null; - return ("" + string.Join(", ", mods)).Size("50%").Align("center"); + return location switch + { + Location.MainMenu => string.Join('\n', modTexts), + Location.PingTracker => ("" + string.Join(", ", modTexts)).Size("50%").Align("center"), + _ => throw new ArgumentOutOfRangeException(nameof(location), location, null), + }; } }