From aae9613fcadace6171daf7fdd257f4bcf3fefdd5 Mon Sep 17 00:00:00 2001 From: Misfiy <85962933+obvEve@users.noreply.github.com> Date: Tue, 6 Jan 2026 14:53:37 +0100 Subject: [PATCH 01/37] 3.0 --- SecretAPI.Examples/ExampleEntry.cs | 1 - SecretAPI/Extensions/CollectionExtensions.cs | 51 ++++---- SecretAPI/Extensions/HarmonyExtensions.cs | 85 ++++++------- SecretAPI/Extensions/MirrorExtensions.cs | 20 ---- SecretAPI/Extensions/PlayerExtensions.cs | 120 +++++++++---------- SecretAPI/Extensions/RoleExtensions.cs | 57 --------- SecretAPI/SecretAPI.csproj | 3 + 7 files changed, 130 insertions(+), 207 deletions(-) delete mode 100644 SecretAPI/Extensions/RoleExtensions.cs diff --git a/SecretAPI.Examples/ExampleEntry.cs b/SecretAPI.Examples/ExampleEntry.cs index 7381b08..32ee205 100644 --- a/SecretAPI.Examples/ExampleEntry.cs +++ b/SecretAPI.Examples/ExampleEntry.cs @@ -5,7 +5,6 @@ using HarmonyLib; using LabApi.Loader.Features.Plugins; using SecretAPI.Examples.Settings; - using SecretAPI.Extensions; using SecretAPI.Features.UserSettings; /// diff --git a/SecretAPI/Extensions/CollectionExtensions.cs b/SecretAPI/Extensions/CollectionExtensions.cs index 28e1887..83af57e 100644 --- a/SecretAPI/Extensions/CollectionExtensions.cs +++ b/SecretAPI/Extensions/CollectionExtensions.cs @@ -11,37 +11,38 @@ /// public static class CollectionExtensions { - /// - /// Gets a random value from the collection. - /// /// The collection to pull from. /// The Type contained by the collection. - /// A random value, default value when empty collection. - /// Will occur if the collection is empty. - public static T GetRandomValue(this IEnumerable collection) + extension(IEnumerable collection) { - TryGetRandomValue(collection, out T? value); - return value!; - } - - /// - /// Tries to get a random value from . - /// - /// The to try and get a random value from. - /// The value that was found. Default if none could be found. - /// The type contained within the . - /// Whether a non-null value was found. - public static bool TryGetRandomValue(this IEnumerable collection, [NotNullWhen(true)] out T? value) - { - IList list = collection as IList ?? collection.ToList(); - if (list.Count == 0) + /// + /// Gets a random value from the collection. + /// + /// A random value, default value when empty collection. + /// Will occur if the collection is empty. + public T GetRandomValue() { - value = default; - return false; + TryGetRandomValue(collection, out T? value); + return value!; } - value = list[Random.Range(0, list.Count)]; - return value != null; + /// + /// Tries to get a random value from . + /// + /// The value that was found. Default if none could be found. + /// Whether a non-null value was found. + public bool TryGetRandomValue([NotNullWhen(true)] out T? value) + { + IList list = collection as IList ?? collection.ToList(); + if (list.Count == 0) + { + value = default; + return false; + } + + value = list[Random.Range(0, list.Count)]; + return value != null; + } } } } \ No newline at end of file diff --git a/SecretAPI/Extensions/HarmonyExtensions.cs b/SecretAPI/Extensions/HarmonyExtensions.cs index 2f623b1..29d7451 100644 --- a/SecretAPI/Extensions/HarmonyExtensions.cs +++ b/SecretAPI/Extensions/HarmonyExtensions.cs @@ -13,55 +13,56 @@ /// public static class HarmonyExtensions { - /// - /// Patches all methods with the proper . - /// /// The harmony to use for the patch. - /// The category to patch. - /// The assembly to find patches in. - public static void PatchCategory(this Harmony harmony, string category, Assembly? assembly = null) + extension(Harmony harmony) { - assembly ??= Assembly.GetCallingAssembly(); - - assembly.GetTypes().Where(type => - { - IEnumerable categories = type.GetCustomAttributes(); - return categories.Any(c => c.Category == category); - }) - .Do(type => SafePatch(harmony, type)); - } - - /// - /// Patches all patches that don't have a . - /// - /// The harmony to use for the patch. - /// The assembly to look for patches. - public static void PatchAllNoCategory(this Harmony harmony, Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); + /// + /// Patches all methods with the proper . + /// + /// The category to patch. + /// The assembly to find patches in. + public void PatchCategory(string category, Assembly? assembly = null) + { + assembly ??= Assembly.GetCallingAssembly(); - assembly.GetTypes().Where(type => - { - IEnumerable categories = type.GetCustomAttributes(); - return !categories.Any(); - }) - .Do(type => SafePatch(harmony, type)); - } + assembly.GetTypes().Where(type => + { + IEnumerable categories = type.GetCustomAttributes(); + return categories.Any(c => c.Category == category); + }) + .Do(type => SafePatch(harmony, type)); + } - /// - /// Attempts to safely patch a , logging any errors. - /// - /// The harmony to use for the patch. - /// The to attempt to patch. - public static void SafePatch(this Harmony harmony, Type type) - { - try + /// + /// Patches all patches that don't have a . + /// + /// The assembly to look for patches. + public void PatchAllNoCategory(Assembly? assembly = null) { - harmony.CreateClassProcessor(type).Patch(); + assembly ??= Assembly.GetCallingAssembly(); + + assembly.GetTypes().Where(type => + { + IEnumerable categories = type.GetCustomAttributes(); + return !categories.Any(); + }) + .Do(type => SafePatch(harmony, type)); } - catch (Exception ex) + + /// + /// Attempts to safely patch a , logging any errors. + /// + /// The to attempt to patch. + public void SafePatch(Type type) { - Logger.Error($"[HarmonyExtensions] failed to safely patch {harmony.Id} ({type.FullName}): {ex}"); + try + { + harmony.CreateClassProcessor(type).Patch(); + } + catch (Exception ex) + { + Logger.Error($"[HarmonyExtensions] failed to safely patch {harmony.Id} ({type.FullName}): {ex}"); + } } } } diff --git a/SecretAPI/Extensions/MirrorExtensions.cs b/SecretAPI/Extensions/MirrorExtensions.cs index 126321e..30aea4e 100644 --- a/SecretAPI/Extensions/MirrorExtensions.cs +++ b/SecretAPI/Extensions/MirrorExtensions.cs @@ -11,26 +11,6 @@ /// public static class MirrorExtensions { - /// - /// Sends a fake cassie message to a player. - /// - /// The target to send the cassie message to. - /// The message to send. - /// Whether the cassie is held. - /// Whether the cassie is noisy. - /// Whether there is subtitles on the cassie. - /// The custom subtitles to use for the cassie. - [Obsolete("Due to NW changes to Cassie, this is no longer functional.")] - public static void SendFakeCassieMessage( - this Player target, - string message, - bool isHeld = false, - bool isNoisy = true, - bool isSubtitles = true, - string customSubtitles = "") - { - } - /// /// Send a fake rpc message to a player. /// diff --git a/SecretAPI/Extensions/PlayerExtensions.cs b/SecretAPI/Extensions/PlayerExtensions.cs index 032b3bf..d2a6737 100644 --- a/SecretAPI/Extensions/PlayerExtensions.cs +++ b/SecretAPI/Extensions/PlayerExtensions.cs @@ -2,9 +2,6 @@ { using CustomPlayerEffects; using Interactables.Interobjects.DoorUtils; - using InventorySystem; - using InventorySystem.Items; - using InventorySystem.Items.Usables.Scp330; using LabApi.Features.Wrappers; using SecretAPI.Enums; @@ -13,74 +10,73 @@ /// public static class PlayerExtensions { - /// - /// Gets an effect of a player based on the effect name. - /// /// The player to get effect from. - /// Name of the effect to find. - /// The effect. - public static StatusEffectBase GetEffect(this Player player, string name) - => player.ReferenceHub.playerEffectsController.TryGetEffect(name, out StatusEffectBase? effect) ? effect : null!; - - /// - /// Checks whether a player has permission to access a . - /// - /// The player to check. - /// The requester to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public static bool HasDoorPermission(this Player player, IDoorPermissionRequester requester, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + extension(Player player) { - if (checkFlags.HasFlag(DoorPermissionCheck.Bypass) && player.IsBypassEnabled) - return true; - - if (checkFlags.HasFlag(DoorPermissionCheck.Role) && player.RoleBase is IDoorPermissionProvider roleProvider && requester.PermissionsPolicy.CheckPermissions(roleProvider.GetPermissions(requester))) - return true; + /// + /// Gets an effect of a player based on the effect name. + /// + /// Name of the effect to find. + /// The effect. + public StatusEffectBase GetEffect(string name) + => player.ReferenceHub.playerEffectsController.TryGetEffect(name, out StatusEffectBase? effect) ? effect : null!; - foreach (Item item in player.Items) + /// + /// Checks whether a player has permission to access a . + /// + /// The requester to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasDoorPermission(IDoorPermissionRequester requester, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) { - bool isCurrent = item == player.CurrentItem; - if (!checkFlags.HasFlag(DoorPermissionCheck.CurrentItem) && isCurrent) - continue; - - if (!checkFlags.HasFlag(DoorPermissionCheck.InventoryExcludingCurrent) && !isCurrent) - continue; + if (checkFlags.HasFlag(DoorPermissionCheck.Bypass) && player.IsBypassEnabled) + return true; - if (item.Base is IDoorPermissionProvider itemProvider && requester.PermissionsPolicy.CheckPermissions(itemProvider.GetPermissions(requester))) + if (checkFlags.HasFlag(DoorPermissionCheck.Role) && player.RoleBase is IDoorPermissionProvider roleProvider && requester.PermissionsPolicy.CheckPermissions(roleProvider.GetPermissions(requester))) return true; - } - return false; - } + foreach (Item item in player.Items) + { + bool isCurrent = item == player.CurrentItem; + if (!checkFlags.HasFlag(DoorPermissionCheck.CurrentItem) && isCurrent) + continue; - /// - /// Checks whether a player has permission to access a . - /// - /// The player to check. - /// The door to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public static bool HasDoorPermission(this Player player, Door door, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - => player.HasDoorPermission(door.Base, checkFlags); + if (!checkFlags.HasFlag(DoorPermissionCheck.InventoryExcludingCurrent) && !isCurrent) + continue; - /// - /// Checks whether a player has permission to access a . - /// - /// The player to check. - /// The locker chamber to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public static bool HasLockerChamberPermission(this Player player, LockerChamber chamber, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - => player.HasDoorPermission(chamber.Base, checkFlags); + if (item.Base is IDoorPermissionProvider itemProvider && requester.PermissionsPolicy.CheckPermissions(itemProvider.GetPermissions(requester))) + return true; + } - /// - /// Checks whether a player has permission to access a . - /// - /// The player to check. - /// The generator to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public static bool HasGeneratorPermission(this Player player, Generator generator, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - => player.HasDoorPermission(generator.Base, checkFlags); + return false; + } + + /// + /// Checks whether a player has permission to access a . + /// + /// The door to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasDoorPermission(Door door, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + => player.HasDoorPermission(door.Base, checkFlags); + + /// + /// Checks whether a player has permission to access a . + /// + /// The locker chamber to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasLockerChamberPermission(LockerChamber chamber, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + => player.HasDoorPermission(chamber.Base, checkFlags); + + /// + /// Checks whether a player has permission to access a . + /// + /// The generator to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasGeneratorPermission(Generator generator, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + => player.HasDoorPermission(generator.Base, checkFlags); + } } } \ No newline at end of file diff --git a/SecretAPI/Extensions/RoleExtensions.cs b/SecretAPI/Extensions/RoleExtensions.cs deleted file mode 100644 index e436281..0000000 --- a/SecretAPI/Extensions/RoleExtensions.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace SecretAPI.Extensions -{ - using System; - using System.Diagnostics.CodeAnalysis; - using InventorySystem; - using LabApi.Features.Extensions; - using PlayerRoles; - using Respawning.Objectives; - using UnityEngine; - - /// - /// Extensions related to . - /// - [Obsolete("This no longer provides anything that basegame/LabAPI does not")] - public static class RoleExtensions - { - /// - /// Tries to get a role base from a . - /// - /// The to get base of. - /// The found. - /// The . - /// The role base found, else null. - [Obsolete("Use LabApi.Features.Extensions.RoleExtensions.TryGetRoleBase")] - public static bool TryGetRoleBase(this RoleTypeId roleTypeId, [NotNullWhen(true)] out T? role) - => LabApi.Features.Extensions.RoleExtensions.TryGetRoleBase(roleTypeId, out role); - - /// - /// Gets the color of a . - /// - /// The role to get color of. - /// The color found, if not found then white. - [Obsolete("Use Respawning.Objectives.GetRoleColor")] - public static Color GetColor(this RoleTypeId roleTypeId) - => roleTypeId.GetRoleColor(); - - /// - /// Tries to get a random spawn point from a . - /// - /// The role to get spawn from. - /// The position found. - /// The rotation found. - /// Whether a spawnpoint was found. - [Obsolete("Use LabApi.Features.Extensions.RoleExtensions.TryGetRandomSpawnPoint")] - public static bool GetRandomSpawnPosition(this RoleTypeId role, out Vector3 position, out float horizontalRot) - => role.TryGetRandomSpawnPoint(out position, out horizontalRot); - - /// - /// Gets the inventory of the specified . - /// - /// The . - /// The found. - [Obsolete("Use LabApi.Features.Extensions.RoleExtensions.GetInventory")] - public static InventoryRoleInfo GetInventory(this RoleTypeId role) - => LabApi.Features.Extensions.RoleExtensions.GetInventory(role); - } -} \ No newline at end of file diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index ba3d4b3..c4335e7 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -29,13 +29,16 @@ + + From 22ec81f241ae790e5a1ae8ed20945880e7f41a21 Mon Sep 17 00:00:00 2001 From: evelyn <85962933+Misfiy@users.noreply.github.com> Date: Wed, 7 Jan 2026 09:27:51 +0100 Subject: [PATCH 02/37] Update Attribute to Attributes namespace --- SecretAPI.Examples/Patches/ExamplePatch.cs | 2 +- SecretAPI/{Attribute => Attributes}/CallOnLoadAttribute.cs | 2 +- SecretAPI/{Attribute => Attributes}/CallOnUnloadAttribute.cs | 2 +- SecretAPI/{Attribute => Attributes}/HarmonyPatchCategory.cs | 2 +- SecretAPI/Extensions/HarmonyExtensions.cs | 2 +- SecretAPI/Patches/Features/SendSettingsPlayerSync.cs | 2 +- SecretAPI/Patches/Features/SendSettingsServerSync.cs | 2 +- SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs | 2 +- SecretAPI/Patches/Features/SettingsSyncValidateFix.cs | 2 +- SecretAPI/SecretApi.cs | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) rename SecretAPI/{Attribute => Attributes}/CallOnLoadAttribute.cs (98%) rename SecretAPI/{Attribute => Attributes}/CallOnUnloadAttribute.cs (97%) rename SecretAPI/{Attribute => Attributes}/HarmonyPatchCategory.cs (95%) diff --git a/SecretAPI.Examples/Patches/ExamplePatch.cs b/SecretAPI.Examples/Patches/ExamplePatch.cs index fde590e..167939b 100644 --- a/SecretAPI.Examples/Patches/ExamplePatch.cs +++ b/SecretAPI.Examples/Patches/ExamplePatch.cs @@ -1,6 +1,6 @@ namespace SecretAPI.Examples.Patches { - using SecretAPI.Attribute; + using SecretAPI.Attributes; /// /// An example harmony patch. diff --git a/SecretAPI/Attribute/CallOnLoadAttribute.cs b/SecretAPI/Attributes/CallOnLoadAttribute.cs similarity index 98% rename from SecretAPI/Attribute/CallOnLoadAttribute.cs rename to SecretAPI/Attributes/CallOnLoadAttribute.cs index cb55356..32bcd34 100644 --- a/SecretAPI/Attribute/CallOnLoadAttribute.cs +++ b/SecretAPI/Attributes/CallOnLoadAttribute.cs @@ -1,4 +1,4 @@ -namespace SecretAPI.Attribute +namespace SecretAPI.Attributes { using System; using System.Collections.Generic; diff --git a/SecretAPI/Attribute/CallOnUnloadAttribute.cs b/SecretAPI/Attributes/CallOnUnloadAttribute.cs similarity index 97% rename from SecretAPI/Attribute/CallOnUnloadAttribute.cs rename to SecretAPI/Attributes/CallOnUnloadAttribute.cs index b886d55..65da120 100644 --- a/SecretAPI/Attribute/CallOnUnloadAttribute.cs +++ b/SecretAPI/Attributes/CallOnUnloadAttribute.cs @@ -1,4 +1,4 @@ -namespace SecretAPI.Attribute +namespace SecretAPI.Attributes { using System; using System.Reflection; diff --git a/SecretAPI/Attribute/HarmonyPatchCategory.cs b/SecretAPI/Attributes/HarmonyPatchCategory.cs similarity index 95% rename from SecretAPI/Attribute/HarmonyPatchCategory.cs rename to SecretAPI/Attributes/HarmonyPatchCategory.cs index a27b953..853129d 100644 --- a/SecretAPI/Attribute/HarmonyPatchCategory.cs +++ b/SecretAPI/Attributes/HarmonyPatchCategory.cs @@ -1,4 +1,4 @@ -namespace SecretAPI.Attribute +namespace SecretAPI.Attributes { using System; using SecretAPI.Extensions; diff --git a/SecretAPI/Extensions/HarmonyExtensions.cs b/SecretAPI/Extensions/HarmonyExtensions.cs index 29d7451..f26b436 100644 --- a/SecretAPI/Extensions/HarmonyExtensions.cs +++ b/SecretAPI/Extensions/HarmonyExtensions.cs @@ -6,7 +6,7 @@ using System.Reflection; using HarmonyLib; using LabApi.Features.Console; - using SecretAPI.Attribute; + using SecretAPI.Attributes; /// /// Handles patching. diff --git a/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs b/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs index dcc3c58..d7b94cb 100644 --- a/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs +++ b/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs @@ -2,7 +2,7 @@ { using HarmonyLib; using LabApi.Features.Wrappers; - using SecretAPI.Attribute; + using SecretAPI.Attributes; using SecretAPI.Features.UserSettings; using UserSettings.ServerSpecific; diff --git a/SecretAPI/Patches/Features/SendSettingsServerSync.cs b/SecretAPI/Patches/Features/SendSettingsServerSync.cs index cb2fd52..e84d1ff 100644 --- a/SecretAPI/Patches/Features/SendSettingsServerSync.cs +++ b/SecretAPI/Patches/Features/SendSettingsServerSync.cs @@ -1,7 +1,7 @@ namespace SecretAPI.Patches.Features { using HarmonyLib; - using SecretAPI.Attribute; + using SecretAPI.Attributes; using SecretAPI.Features.UserSettings; using UserSettings.ServerSpecific; diff --git a/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs b/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs index f17b81c..a1b1e74 100644 --- a/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs +++ b/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs @@ -1,7 +1,7 @@ namespace SecretAPI.Patches.Features { using HarmonyLib; - using SecretAPI.Attribute; + using SecretAPI.Attributes; using SecretAPI.Features.UserSettings; using UserSettings.ServerSpecific; diff --git a/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs b/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs index 89e08a9..39cf781 100644 --- a/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs +++ b/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs @@ -1,7 +1,7 @@ namespace SecretAPI.Patches.Features { using HarmonyLib; - using SecretAPI.Attribute; + using SecretAPI.Attributes; using SecretAPI.Features.UserSettings; using UserSettings.ServerSpecific; diff --git a/SecretAPI/SecretApi.cs b/SecretAPI/SecretApi.cs index 6272099..72634a7 100644 --- a/SecretAPI/SecretApi.cs +++ b/SecretAPI/SecretApi.cs @@ -6,7 +6,7 @@ using LabApi.Features; using LabApi.Loader.Features.Plugins; using LabApi.Loader.Features.Plugins.Enums; - using SecretAPI.Attribute; + using SecretAPI.Attributes; /// /// Main class handling loading API. From b544af7cb45f62b0faaceb0957bf2490b7dd7879 Mon Sep 17 00:00:00 2001 From: evelyn <85962933+Misfiy@users.noreply.github.com> Date: Wed, 7 Jan 2026 09:31:11 +0100 Subject: [PATCH 03/37] Namespace fix --- SecretAPI/Features/Effects/CustomPlayerEffect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecretAPI/Features/Effects/CustomPlayerEffect.cs b/SecretAPI/Features/Effects/CustomPlayerEffect.cs index 6231060..8f9b080 100644 --- a/SecretAPI/Features/Effects/CustomPlayerEffect.cs +++ b/SecretAPI/Features/Effects/CustomPlayerEffect.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using CustomPlayerEffects; using LabApi.Features.Wrappers; - using SecretAPI.Attribute; + using SecretAPI.Attributes; using SecretAPI.Extensions; using UnityEngine; using UnityEngine.SceneManagement; From 6c765bd48303869a68f01e506094ff03c997587a Mon Sep 17 00:00:00 2001 From: Misfiy <85962933+obvEve@users.noreply.github.com> Date: Sun, 18 Jan 2026 20:42:41 +0100 Subject: [PATCH 04/37] Change DoorPermissionCheck to use bitwise --- SecretAPI/Enums/DoorPermissionCheck.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SecretAPI/Enums/DoorPermissionCheck.cs b/SecretAPI/Enums/DoorPermissionCheck.cs index 0070b60..ad78cac 100644 --- a/SecretAPI/Enums/DoorPermissionCheck.cs +++ b/SecretAPI/Enums/DoorPermissionCheck.cs @@ -21,22 +21,22 @@ public enum DoorPermissionCheck /// /// Used to consider . /// - Bypass = 1, + Bypass = 1 << 0, /// /// Used to consider the player's . /// - Role = 2, + Role = 1 << 1, /// /// Used to consider the player's . /// - CurrentItem = 4, + CurrentItem = 1 << 2, /// /// Used to consider the player's inventory, not including the item they are holding. /// - InventoryExcludingCurrent = 8, + InventoryExcludingCurrent = 1 << 3, /// /// Used to consider the player's ENTIRE inventory. From cc8060499d8795d2e2a2c4f1748ecf3c94182110 Mon Sep 17 00:00:00 2001 From: Evelyn <85962933+obvEve@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:33:09 +0100 Subject: [PATCH 05/37] SSSS additions (#85) * SSSS additions for sending values * SSSS additions!! * Do all settings properly * SSSS stuff * Fix: Harmony is not created early enough, causing patches to fail * Improve & Fix docs * feat: SSSS Improvements/Additions (#87) * feat: SettingResponseType enum | CustomSetting:LastUpdateType * CustomDropdownSetting & CustomTwoButtonSetting -> HasValueChanged * Improve documentation --- .../UserSettings/CustomButtonSetting.cs | 29 +++++- .../UserSettings/CustomDropdownSetting.cs | 53 +++++++++- .../Features/UserSettings/CustomHeader.cs | 2 + .../UserSettings/CustomKeybindSetting.cs | 6 +- .../UserSettings/CustomPlainTextSetting.cs | 64 +++++++++++-- .../Features/UserSettings/CustomSetting.cs | 67 +++++++++++-- .../UserSettings/CustomSliderSetting.cs | 96 +++++++++++++++++-- .../UserSettings/CustomTextAreaSetting.cs | 9 ++ .../UserSettings/CustomTwoButtonSetting.cs | 66 ++++++++++++- .../UserSettings/SettingResponseType.cs | 23 +++++ SecretAPI/SecretApi.cs | 3 +- 11 files changed, 383 insertions(+), 35 deletions(-) create mode 100644 SecretAPI/Features/UserSettings/SettingResponseType.cs diff --git a/SecretAPI/Features/UserSettings/CustomButtonSetting.cs b/SecretAPI/Features/UserSettings/CustomButtonSetting.cs index 3076fb3..7ef676c 100644 --- a/SecretAPI/Features/UserSettings/CustomButtonSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomButtonSetting.cs @@ -40,13 +40,34 @@ protected CustomButtonSetting(int? id, string label, string buttonText, float? h public TimeSpan LastPress => Base.SyncLastPress.Elapsed; /// - /// Gets the text of the button. + /// Gets or sets the text of the button. /// - public string Text => Base.ButtonText; + public string Text + { + get => Base.ButtonText; + set + { + Base.ButtonText = value; + SendButtonUpdate(); + } + } + + /// + /// Gets or sets the amount of time to hold the button in seconds. + /// + public float RequiredHoldTime + { + get => Base.HoldTimeSeconds; + set + { + Base.HoldTimeSeconds = value; + SendButtonUpdate(); + } + } /// - /// Gets the amount of time to hold the button in seconds. + /// Sends an update to that or has updated. /// - public float HoldTime => Base.HoldTimeSeconds; + private void SendButtonUpdate() => Base.SendButtonUpdate(Text, RequiredHoldTime, false, IsKnownOwnerHub); } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs b/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs index 00626a1..ec18e97 100644 --- a/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs @@ -27,20 +27,27 @@ protected CustomDropdownSetting(SSDropdownSetting setting) /// The default option (int index). /// The entry type. /// The hint to show. + /// The . + /// See . protected CustomDropdownSetting( int? id, string label, string[] options, int defaultOptionIndex = 0, SSDropdownSetting.DropdownEntryType entryType = SSDropdownSetting.DropdownEntryType.Regular, - string? hint = null) - : this(new SSDropdownSetting(id, label, options, defaultOptionIndex, entryType, hint)) + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSDropdownSetting(id, label, options, defaultOptionIndex, entryType, hint, collectionId, isServerSetting)) { } /// public new SSDropdownSetting Base { get; } + /// + public override bool HasValueChanged => LastSelectedIndex != SyncedIndex; + /// /// Gets the index the client is claiming to have selected. /// @@ -58,12 +65,52 @@ protected CustomDropdownSetting( public string[] Options { get => Base.Options; - set => Base.Options = value; + set + { + Base.Options = value; + SendDropdownUpdate(); + } } /// /// Gets the selected option as string. /// public string SelectedOption => Options[ValidatedSelectedIndex]; + + /// + /// Gets the . + /// + /// Refer to https://github.com/HubertMoszka/Server-Specific-Settings-System/blob/main/SSDropdownSetting.cs#L151 for proper documentation. + public SSDropdownSetting.DropdownEntryType EntryType => Base.EntryType; + + /// + /// Gets the last selected index, or -1 if none was selected previously. + /// + public int LastSelectedIndex { get; private set; } = -1; + + /// + /// Gets the selected option prior to the most recent call as a string, or null if none was selected previously. + /// + public string? LastSelectedOption => LastSelectedIndex < 0 || LastSelectedIndex >= Options.Length ? null : Options[LastSelectedIndex]; + + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// The new ID selected. + public void SendServerUpdate(int selectionId) => Base.SendValueUpdate(selectionId, false, IsKnownOwnerHub); + + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + + if (LastUpdateType != SettingResponseType.Initial) + LastSelectedIndex = SyncedIndex; + } + + /// + /// Sends an update to that has been updated. + /// + private void SendDropdownUpdate() => Base.SendDropdownUpdate(Options, false, IsKnownOwnerHub); } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomHeader.cs b/SecretAPI/Features/UserSettings/CustomHeader.cs index bc7c7b5..d638121 100644 --- a/SecretAPI/Features/UserSettings/CustomHeader.cs +++ b/SecretAPI/Features/UserSettings/CustomHeader.cs @@ -1,5 +1,6 @@ namespace SecretAPI.Features.UserSettings { + using System; using global::UserSettings.ServerSpecific; /// @@ -21,6 +22,7 @@ public CustomHeader(string label, bool reducedPadding = false, string? hint = nu /// /// Gets a for Gameplay purposes. /// + [Obsolete("3.0 will remove this - Please handle your setting header yourself!")] public static CustomHeader Gameplay { get; } = new("Gameplay", hint: "Features that affect gameplay"); /// diff --git a/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs b/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs index 1b8d094..32ae2aa 100644 --- a/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs @@ -27,14 +27,16 @@ protected CustomKeybindSetting(SSKeybindSetting setting) /// Whether to prevent interaction in a GUI. /// Whether to allow spectators to trigger. /// The hint to show. + /// The . protected CustomKeybindSetting( int? id, string label, KeyCode suggestedKey = KeyCode.None, bool preventInteractionOnGui = true, bool allowSpectatorTrigger = true, - string? hint = null) - : this(new SSKeybindSetting(id, label, suggestedKey, preventInteractionOnGui, allowSpectatorTrigger, hint)) + string? hint = null, + byte collectionId = byte.MaxValue) + : this(new SSKeybindSetting(id, label, suggestedKey, preventInteractionOnGui, allowSpectatorTrigger, hint, collectionId)) { } diff --git a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs index 963f93d..2c6b2b5 100644 --- a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs @@ -1,5 +1,6 @@ namespace SecretAPI.Features.UserSettings { + using System; using global::UserSettings.ServerSpecific; using TMPro; @@ -41,24 +42,75 @@ protected CustomPlainTextSetting( /// public new SSPlaintextSetting Base { get; } + /// + /// Gets the input text prior to the most recent call. + /// + public string LastInputText + { + get => field ??= string.Empty; + private set; + } + /// /// Gets the synced input text. /// public string InputText => Base.SyncInputText; /// - /// Gets the content type. + /// Gets or sets the content type. /// - public TMP_InputField.ContentType ContentType => Base.ContentType; + public TMP_InputField.ContentType ContentType + { + get => Base.ContentType; + set + { + Base.ContentType = value; + SendPlaintextUpdate(); + } + } /// - /// Gets the placeholder. + /// Gets or sets the placeholder. /// - public string Placeholder => Base.Placeholder; + public string Placeholder + { + get => Base.Placeholder; + set + { + Base.Placeholder = value; + SendPlaintextUpdate(); + } + } + + /// + /// Gets or sets the character limit. + /// + public int CharacterLimit + { + get => Base.CharacterLimit; + set + { + Base.CharacterLimit = value; + SendPlaintextUpdate(); + } + } + + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// The new text. + public void SendServerUpdate(string text) => Base.SendValueUpdate(text, false, IsKnownOwnerHub); + + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + LastInputText = InputText; + } /// - /// Gets the character limit. + /// Sends an update to the that or has changed values. /// - public int CharacterLimit => Base.CharacterLimit; + private void SendPlaintextUpdate() => Base.SendPlaintextUpdate(Placeholder, (ushort)Math.Clamp(CharacterLimit, ushort.MinValue, ushort.MaxValue), ContentType, false, IsKnownOwnerHub); } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomSetting.cs b/SecretAPI/Features/UserSettings/CustomSetting.cs index 9f65019..2c97fad 100644 --- a/SecretAPI/Features/UserSettings/CustomSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSetting.cs @@ -21,7 +21,7 @@ public abstract class CustomSetting : ISetting static CustomSetting() { - SecretApi.Harmony?.PatchCategory(nameof(CustomSetting)); + SecretApi.Harmony?.PatchCategory(nameof(CustomSetting), SecretApi.Assembly); ServerSpecificSettingsSync.SendOnJoinFilter = null; ServerSpecificSettingsSync.DefinedSettings ??= []; // fix null ref @@ -66,22 +66,42 @@ protected CustomSetting(ServerSpecificSettingBase setting) public abstract CustomHeader Header { get; } /// - /// Gets or sets a value indicating whether the setting is server side only. + /// Gets an enum indicating the type of the last update. /// - /// The setting value cannot be updated from client side and can be used to indicate server features being toggled. - public bool IsServerOnly + /// When used inside of it will indicate the current status. + public SettingResponseType LastUpdateType { get; private set; } = SettingResponseType.None; + + /// + /// Gets a value indicating whether the current value received is different to that prior to the most recent call. + /// + public virtual bool HasValueChanged { get; } + + /// + /// Gets or sets a value indicating whether the setting is server side. + /// + /// This will result in client not saving the setting values and allows the server to change the setting . + public bool IsServerSetting { get => Base.IsServerOnly; set => Base.IsServerOnly = value; } + /// + /// Gets a value indicating whether the setting is the default and not tied to a . + /// + public bool IsDefaultSetting => KnownOwner == null; + /// /// Gets or sets the current label. /// public string Label { get => Base.Label; - set => Base.Label = value; + set + { + Base.Label = value; + SendSettingUpdate(); + } } /// @@ -90,7 +110,11 @@ public string Label public string DescriptionHint { get => Base.HintDescription; - set => Base.HintDescription = value; + set + { + Base.HintDescription = value; + SendSettingUpdate(); + } } /// @@ -138,13 +162,13 @@ public bool IsShared /// Unregisters collection of settings. /// /// The settings to unregister. - public static void UnRegister(params CustomSetting[] settings) => CustomSettings.RemoveAll(s => settings.Contains(s)); + public static void UnRegister(params CustomSetting[] settings) => CustomSettings.RemoveAll(settings.Contains); /// /// Unregisters a collection of settings. /// /// The settings to unregister. - public static void UnRegister(IEnumerable settings) => CustomSettings.RemoveAll(s => settings.Contains(s)); + public static void UnRegister(IEnumerable settings) => CustomSettings.RemoveAll(settings.Contains); /// /// Tries to get player specific setting. @@ -252,6 +276,24 @@ public static void SendSettingsToPlayer(Player player, int? version = null) ListPool.Shared.Return(ordered); } + /// + /// Checks whether a is equal to . + /// + /// The to check. + /// Whether is equal to Owner . + internal bool IsKnownOwnerHub(ReferenceHub? hub) => hub && KnownOwner?.ReferenceHub == hub; + + /// + /// Called before , adding & . + /// + /// This will not have the current status. + protected internal virtual void HandleBeforeSettingUpdate() + { + LastUpdateType = LastUpdateType == SettingResponseType.None + ? SettingResponseType.Initial + : SettingResponseType.Update; + } + /// /// Resyncs the setting to its owner. /// @@ -287,6 +329,7 @@ protected virtual void PersonalizeSetting() /// /// Called when client sends a new value on the setting. /// + /// You can use to get the current update type. protected abstract void HandleSettingUpdate(); private static void RemoveStoredPlayer(Player player) => ReceivedPlayerSettings.Remove(player); @@ -304,10 +347,11 @@ private static void OnSettingsUpdated(ReferenceHub hub, ServerSpecificSettingBas // validate setting existence and then write data from client CustomSetting newSettingPlayer = EnsurePlayerSpecificSetting(player, setting); + newSettingPlayer.HandleBeforeSettingUpdate(); + NetworkWriterPooled valueWriter = NetworkWriterPool.Get(); settingBase.SerializeValue(valueWriter); newSettingPlayer.Base.DeserializeValue(new NetworkReader(valueWriter.buffer)); - NetworkWriterPool.Return(valueWriter); newSettingPlayer.HandleSettingUpdate(); @@ -326,5 +370,10 @@ private static CustomSetting EnsurePlayerSpecificSetting(Player player, CustomSe return currentSetting; } + + /// + /// Sends an update to that or has changed. + /// + private void SendSettingUpdate() => Base.SendUpdate(Label, DescriptionHint, false, IsKnownOwnerHub); } } diff --git a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs index 98b7e4e..c64031d 100644 --- a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs @@ -1,6 +1,7 @@ namespace SecretAPI.Features.UserSettings { using global::UserSettings.ServerSpecific; + using UnityEngine; /// /// Wrapper for . @@ -29,6 +30,8 @@ protected CustomSliderSetting(SSSliderSetting setting) /// Value to string format. /// The final display format. /// The hint to display. + /// The . + /// See . protected CustomSliderSetting( int? id, string label, @@ -38,7 +41,9 @@ protected CustomSliderSetting( bool integer = false, string valueToStringFormat = "0.##", string finalDisplayFormat = "{0}", - string? hint = null) + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) : this(new SSSliderSetting(id, label, minValue, maxValue, defaultValue, integer, valueToStringFormat, finalDisplayFormat, hint)) { } @@ -46,6 +51,19 @@ protected CustomSliderSetting( /// public new SSSliderSetting Base { get; } + /// + public override bool HasValueChanged => !Mathf.Approximately(LastSelectedValueFloat, SelectedValueFloat); + + /// + /// Gets the selected value prior to the most recent call as a float. + /// + public float LastSelectedValueFloat { get; private set; } + + /// + /// Gets the selected value prior to the most recent call as an int. + /// + public int LastSelectedValueInt => Mathf.RoundToInt(LastSelectedValueFloat); + /// /// Gets the synced value selected as a float. /// @@ -56,13 +74,43 @@ protected CustomSliderSetting( /// public int SelectedValueInt => Base.SyncIntValue; + /// + /// Gets or sets the value to string format. + /// + public string ValueToStringFormat + { + get => Base.ValueToStringFormat; + set + { + Base.ValueToStringFormat = value; + SendSliderUpdate(); + } + } + + /// + /// Gets or sets the final display format. + /// + public string FinalDisplayFormat + { + get => Base.FinalDisplayFormat; + set + { + Base.FinalDisplayFormat = value; + SendSliderUpdate(); + } + } + /// /// Gets or sets the minimum value of the setting. /// public float MinimumValue { get => Base.MinValue; - set => Base.MinValue = value; + set + { + Base.MinValue = value; + SendSliderUpdate(); + } } /// @@ -71,17 +119,51 @@ public float MinimumValue public float MaximumValue { get => Base.MaxValue; - set => Base.MaxValue = value; + set + { + Base.MaxValue = value; + SendSliderUpdate(); + } + } + + /// + /// Gets or sets the default value of the setting. + /// + public float DefaultValue + { + get => Base.DefaultValue; + set => Base.DefaultValue = value; } /// - /// Gets the default value of the setting. + /// Gets or sets a value indicating whether to use integer. False will use float. /// - public float DefaultValue => Base.DefaultValue; + public bool UseInteger + { + get => Base.Integer; + set + { + Base.Integer = value; + SendSliderUpdate(); + } + } + + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// The new value that this is set to. + public void SendServerUpdate(float value) => Base.SendValueUpdate(value, false, IsKnownOwnerHub); + + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + LastSelectedValueFloat = SelectedValueFloat; + } /// - /// Gets a value indicating whether to use integer. False will use float. + /// Sends an update that any of the slider values have been updated. /// - public bool UseInteger => Base.Integer; + private void SendSliderUpdate() => Base.SendSliderUpdate(MinimumValue, MaximumValue, UseInteger, ValueToStringFormat, FinalDisplayFormat, false, IsKnownOwnerHub); } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs b/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs index ea4b4d8..e70c67c 100644 --- a/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs @@ -39,6 +39,15 @@ protected CustomTextAreaSetting( /// public new SSTextArea Base { get; } + /// + /// Gets or sets the current content. This is equal to . + /// + public string Content + { + get => Label; + set => Label = value; + } + /// /// Gets the foldout mode. /// diff --git a/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs b/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs index 4a48a2a..d73cc61 100644 --- a/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs @@ -26,14 +26,58 @@ protected CustomTwoButtonSetting(SSTwoButtonsSetting button) /// The second option. /// Whether the second option should be default. Default: false. /// The hint to show. - protected CustomTwoButtonSetting(int? id, string label, string optionA, string optionB, bool defaultIsB = false, string? hint = null) - : this(new SSTwoButtonsSetting(id, label, optionA, optionB, defaultIsB, hint)) + /// The . + /// See . + protected CustomTwoButtonSetting( + int? id, + string label, + string optionA, + string optionB, + bool defaultIsB = false, + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSTwoButtonsSetting(id, label, optionA, optionB, defaultIsB, hint, collectionId, isServerSetting)) { } /// public new SSTwoButtonsSetting Base { get; } + /// + /// Gets or sets the current text for the first option. + /// + public string OptionA + { + get => Base.OptionA; + set + { + Base.OptionA = value; + SendOptionsUpdate(); + } + } + + /// + /// Gets or sets the current text for the second option. + /// + public string OptionB + { + get => Base.OptionB; + set + { + Base.OptionB = value; + SendOptionsUpdate(); + } + } + + /// + public override bool HasValueChanged => WasLastOptionB != IsOptionB; + + /// + /// Gets a value indicating whether the value prior to the most recent call was Option B. + /// + public bool WasLastOptionB { get; private set; } + /// /// Gets a value indicating whether the selected option is currently the first. /// @@ -48,5 +92,23 @@ protected CustomTwoButtonSetting(int? id, string label, string optionA, string o /// Gets a value indicating whether the selected option is currently set to the default. /// public bool IsDefault => Base.DefaultIsB ? IsOptionB : IsOptionA; + + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// Whether the setting is set to B value now. + public void SendServerUpdate(bool isB) => Base.SendValueUpdate(isB, false, IsKnownOwnerHub); + + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + WasLastOptionB = IsOptionB; + } + + /// + /// Sends an update to the that or has changed values. + /// + private void SendOptionsUpdate() => Base.SendTwoButtonUpdate(OptionA, OptionB, false, IsKnownOwnerHub); } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/SettingResponseType.cs b/SecretAPI/Features/UserSettings/SettingResponseType.cs new file mode 100644 index 0000000..ea3f5db --- /dev/null +++ b/SecretAPI/Features/UserSettings/SettingResponseType.cs @@ -0,0 +1,23 @@ +namespace SecretAPI.Features.UserSettings +{ + /// + /// The type of response. + /// + public enum SettingResponseType + { + /// + /// Indicates that no response has been recorded. + /// + None, + + /// + /// Indicates that this is the initial response. + /// + Initial, + + /// + /// Indicates that this is an update, changing the value. + /// + Update, + } +} \ No newline at end of file diff --git a/SecretAPI/SecretApi.cs b/SecretAPI/SecretApi.cs index 72634a7..7f58deb 100644 --- a/SecretAPI/SecretApi.cs +++ b/SecretAPI/SecretApi.cs @@ -38,7 +38,7 @@ public class SecretApi : Plugin /// /// Gets the harmony to use for the API. /// - internal static Harmony? Harmony { get; private set; } + internal static Harmony? Harmony { get; } = new("SecretAPI" + DateTime.Now); /// /// Gets the Assembly of the API. @@ -48,7 +48,6 @@ public class SecretApi : Plugin /// public override void Enable() { - Harmony = new Harmony("SecretAPI" + DateTime.Now); CallOnLoadAttribute.Load(Assembly); } From d0692146668f5dda7f9c0b353e3b8573119658a3 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:35:05 +0100 Subject: [PATCH 06/37] Fix build (Please Exiled stop changing it) --- .github/workflows/nuget.yml | 6 +++--- .github/workflows/pull_request.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 468425b..6c30cde 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -26,9 +26,9 @@ jobs: Invoke-WebRequest -Uri ${{ env.REFERENCES_URL }} -OutFile "${{ github.workspace }}/References.zip" Expand-Archive -Path "${{ github.workspace }}/References.zip" -DestinationPath ${{ env.REFERENCES_PATH }} - - name: Rename Assembly-CSharp-Publicized to Assembly-CSharp - shell: pwsh - run: Rename-Item -Path "${{ env.REFERENCES_PATH }}\Assembly-CSharp-Publicized.dll" -NewName "Assembly-CSharp.dll" +# - name: Rename Assembly-CSharp-Publicized to Assembly-CSharp +# shell: pwsh +# run: Rename-Item -Path "${{ env.REFERENCES_PATH }}\Assembly-CSharp-Publicized.dll" -NewName "Assembly-CSharp.dll" - name: Build and Pack NuGet env: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 526d686..2de576e 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -26,9 +26,9 @@ jobs: Invoke-WebRequest -Uri ${{ env.REFERENCES_URL }} -OutFile "${{ github.workspace }}/References.zip" Expand-Archive -Path "${{ github.workspace }}/References.zip" -DestinationPath ${{ env.REFERENCES_PATH }} - - name: Rename Assembly-CSharp-Publicized to Assembly-CSharp - shell: pwsh - run: Rename-Item -Path "${{ env.REFERENCES_PATH }}\Assembly-CSharp-Publicized.dll" -NewName "Assembly-CSharp.dll" +# - name: Rename Assembly-CSharp-Publicized to Assembly-CSharp +# shell: pwsh +# run: Rename-Item -Path "${{ env.REFERENCES_PATH }}\Assembly-CSharp-Publicized.dll" -NewName "Assembly-CSharp.dll" - name: Build env: From a771508db6795b5c529d1c889164f8535f4b54eb Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:38:38 +0100 Subject: [PATCH 07/37] docs: Change & symbol to and --- SecretAPI/Features/UserSettings/CustomSetting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecretAPI/Features/UserSettings/CustomSetting.cs b/SecretAPI/Features/UserSettings/CustomSetting.cs index 2c97fad..ef52e5a 100644 --- a/SecretAPI/Features/UserSettings/CustomSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSetting.cs @@ -284,7 +284,7 @@ public static void SendSettingsToPlayer(Player player, int? version = null) internal bool IsKnownOwnerHub(ReferenceHub? hub) => hub && KnownOwner?.ReferenceHub == hub; /// - /// Called before , adding & . + /// Called before , adding and . /// /// This will not have the current status. protected internal virtual void HandleBeforeSettingUpdate() From 2284ecd2b1ee8ca91b2b5aacb1396645661e7ea4 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:40:57 +0100 Subject: [PATCH 08/37] Update csproj to obvEve instead of Misfiy & Bump version to 3.0.0 --- SecretAPI/SecretAPI.csproj | 8 ++++---- SecretAPI/SecretApi.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index c4335e7..d1740d4 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -4,18 +4,18 @@ net48 latest enable - 2.0.3 + 3.0.0 true - + true true - Misfiy + obvEve SecretAPI API to extend SCP:SL LabAPI git - https://github.com/Misfiy/SecretAPI + https://github.com/obvEve/SecretAPI README.md MIT diff --git a/SecretAPI/SecretApi.cs b/SecretAPI/SecretApi.cs index 7f58deb..40abb82 100644 --- a/SecretAPI/SecretApi.cs +++ b/SecretAPI/SecretApi.cs @@ -20,7 +20,7 @@ public class SecretApi : Plugin public override string Description => "API for SCP:SL"; /// - public override string Author => "@misfiy / @obvEvelyn"; + public override string Author => "@obvEve"; /// public override LoadPriority Priority => LoadPriority.Highest; From f57caf868bdcc48136199e3fa3ded71276581bc0 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Fri, 27 Mar 2026 17:48:08 +0100 Subject: [PATCH 09/37] Minor code improvements --- SecretAPI/Features/Effects/CustomPlayerEffect.cs | 11 ++--------- .../Features/UserSettings/CustomPlainTextSetting.cs | 8 ++++++-- SecretAPI/Features/UserSettings/CustomSetting.cs | 2 +- .../Features/UserSettings/CustomSliderSetting.cs | 2 +- SecretAPI/Features/UserSettings/ISetting.cs | 2 +- SecretAPI/SecretApi.cs | 4 ++-- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/SecretAPI/Features/Effects/CustomPlayerEffect.cs b/SecretAPI/Features/Effects/CustomPlayerEffect.cs index 8f9b080..a74b743 100644 --- a/SecretAPI/Features/Effects/CustomPlayerEffect.cs +++ b/SecretAPI/Features/Effects/CustomPlayerEffect.cs @@ -27,14 +27,7 @@ public abstract class CustomPlayerEffect : StatusEffectBase /// /// Gets the with this effect. /// - public Player Owner { get; private set; } = null!; - - /// - public override void Start() - { - Owner = Player.Get(Hub); - base.Start(); - } + public Player Owner => field ??= Player.Get(Hub); /// public override string ToString() => $"{GetType().Name}: Owner ({Owner}) - Intensity ({Intensity}) - Duration {Duration}"; @@ -45,7 +38,7 @@ public override void Start() [CallOnLoad] internal static void Initialize() { - SecretApi.Harmony?.PatchCategory(nameof(CustomPlayerEffect), SecretApi.Assembly); + SecretApi.Harmony.PatchCategory(nameof(CustomPlayerEffect), SecretApi.Assembly); EffectsToRegister.Add(typeof(TemporaryDamageImmunity)); EffectsToRegister.Add(typeof(StaminaUsageDisablerEffect)); EffectsToRegister.Add(typeof(SprintDisablerEffect)); diff --git a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs index 2c6b2b5..a5ba7cc 100644 --- a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs @@ -28,14 +28,18 @@ protected CustomPlainTextSetting(SSPlaintextSetting setting) /// The max allowed characters. /// The content type. /// The hint to display for the setting. + /// The . + /// See . protected CustomPlainTextSetting( int? id, string label, string placeholder = "...", int characterLimit = 64, TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, - string? hint = null) - : this(new SSPlaintextSetting(id, label, placeholder, characterLimit, contentType, hint)) + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSPlaintextSetting(id, label, placeholder, characterLimit, contentType, hint, collectionId, isServerSetting)) { } diff --git a/SecretAPI/Features/UserSettings/CustomSetting.cs b/SecretAPI/Features/UserSettings/CustomSetting.cs index ef52e5a..1190e7d 100644 --- a/SecretAPI/Features/UserSettings/CustomSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSetting.cs @@ -21,7 +21,7 @@ public abstract class CustomSetting : ISetting static CustomSetting() { - SecretApi.Harmony?.PatchCategory(nameof(CustomSetting), SecretApi.Assembly); + SecretApi.Harmony.PatchCategory(nameof(CustomSetting), SecretApi.Assembly); ServerSpecificSettingsSync.SendOnJoinFilter = null; ServerSpecificSettingsSync.DefinedSettings ??= []; // fix null ref diff --git a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs index c64031d..1831934 100644 --- a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs @@ -44,7 +44,7 @@ protected CustomSliderSetting( string? hint = null, byte collectionId = byte.MaxValue, bool isServerSetting = false) - : this(new SSSliderSetting(id, label, minValue, maxValue, defaultValue, integer, valueToStringFormat, finalDisplayFormat, hint)) + : this(new SSSliderSetting(id, label, minValue, maxValue, defaultValue, integer, valueToStringFormat, finalDisplayFormat, hint, collectionId, isServerSetting)) { } diff --git a/SecretAPI/Features/UserSettings/ISetting.cs b/SecretAPI/Features/UserSettings/ISetting.cs index 629936d..6c35959 100644 --- a/SecretAPI/Features/UserSettings/ISetting.cs +++ b/SecretAPI/Features/UserSettings/ISetting.cs @@ -6,7 +6,7 @@ /// Interface for to handle the Base. /// /// The setting being wrapped. - public interface ISetting + public interface ISetting where T : ServerSpecificSettingBase { /// diff --git a/SecretAPI/SecretApi.cs b/SecretAPI/SecretApi.cs index 40abb82..f41521c 100644 --- a/SecretAPI/SecretApi.cs +++ b/SecretAPI/SecretApi.cs @@ -38,7 +38,7 @@ public class SecretApi : Plugin /// /// Gets the harmony to use for the API. /// - internal static Harmony? Harmony { get; } = new("SecretAPI" + DateTime.Now); + internal static Harmony Harmony { get; } = new("SecretAPI" + DateTime.Now); /// /// Gets the Assembly of the API. @@ -54,7 +54,7 @@ public override void Enable() /// public override void Disable() { - Harmony?.UnpatchAll(Harmony.Id); + Harmony.UnpatchAll(Harmony.Id); } } } \ No newline at end of file From 2afdab894ec6190fe1d585456977fe73278d8014 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:08:31 +0100 Subject: [PATCH 10/37] CustomPlainTextSetting::LastInputText -> Remove validation as basegame already does it --- SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs index a5ba7cc..2c75305 100644 --- a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs @@ -49,11 +49,7 @@ protected CustomPlainTextSetting( /// /// Gets the input text prior to the most recent call. /// - public string LastInputText - { - get => field ??= string.Empty; - private set; - } + public string LastInputText { get; private set; } = string.Empty; /// /// Gets the synced input text. From 92cd31cf5195716b523244f3a12b840947328172 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:20:15 +0200 Subject: [PATCH 11/37] Change version to 3.0.0-beta1 --- SecretAPI/SecretAPI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index d1740d4..48282bb 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -4,7 +4,7 @@ net48 latest enable - 3.0.0 + 3.0.0-beta1 true From 83c2c49867fd28f48e90416f880b0cc7fb0c6a73 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:37:14 +0200 Subject: [PATCH 12/37] Fix? --output on nuget.yml dotnet pack --- .github/workflows/nuget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 6c30cde..1be9c83 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -33,7 +33,7 @@ jobs: - name: Build and Pack NuGet env: SL_REFERENCES: ${{ env.REFERENCES_PATH }} - run: dotnet pack -c Release --output ${NUGET_PACKAGED_PATH} + run: dotnet pack -c Release --output ${{ NUGET_PACKAGED_PATH }} - name: Push NuGet package run: | From 21eacc04fc3e415ff7573536e27c6bb3a89265b1 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:37:48 +0200 Subject: [PATCH 13/37] Remove CustomHeader::Gameplay --- SecretAPI/Features/UserSettings/CustomHeader.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/SecretAPI/Features/UserSettings/CustomHeader.cs b/SecretAPI/Features/UserSettings/CustomHeader.cs index d638121..8822806 100644 --- a/SecretAPI/Features/UserSettings/CustomHeader.cs +++ b/SecretAPI/Features/UserSettings/CustomHeader.cs @@ -19,12 +19,6 @@ public CustomHeader(string label, bool reducedPadding = false, string? hint = nu Base = new SSGroupHeader(label, reducedPadding, hint); } - /// - /// Gets a for Gameplay purposes. - /// - [Obsolete("3.0 will remove this - Please handle your setting header yourself!")] - public static CustomHeader Gameplay { get; } = new("Gameplay", hint: "Features that affect gameplay"); - /// /// Gets a for Example purposes. /// From d9656967806801316c13149de519a497bf5f2a8b Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:44:35 +0200 Subject: [PATCH 14/37] File-scoped namespaces --- .editorconfig | 3 +- SecretAPI.Examples/ExampleEntry.cs | 61 +- SecretAPI.Examples/Patches/ExamplePatch.cs | 37 +- .../Settings/ExampleDropdownSetting.cs | 73 +- .../Settings/ExampleKeybindSetting.cs | 53 +- SecretAPI/Attributes/CallOnLoadAttribute.cs | 115 ++- SecretAPI/Attributes/CallOnUnloadAttribute.cs | 67 +- SecretAPI/Attributes/HarmonyPatchCategory.cs | 39 +- SecretAPI/Enums/DoorPermissionCheck.cs | 107 ++- SecretAPI/Extensions/CollectionExtensions.cs | 77 +-- SecretAPI/Extensions/HarmonyExtensions.cs | 113 ++- SecretAPI/Extensions/MirrorExtensions.cs | 105 ++- SecretAPI/Extensions/PlayerExtensions.cs | 137 ++-- SecretAPI/Extensions/ReflectionExtensions.cs | 81 ++- SecretAPI/Extensions/RoomExtensions.cs | 61 +- SecretAPI/Extensions/Scp914Extensions.cs | 69 +- .../Features/Effects/CustomPlayerEffect.cs | 105 ++- .../Effects/CustomTickingPlayerEffect.cs | 79 ++- .../Features/Effects/SprintDisablerEffect.cs | 45 +- .../Effects/StaminaUsageDisablerEffect.cs | 29 +- .../Effects/TemporaryDamageImmunity.cs | 31 +- SecretAPI/Features/IPriority.cs | 17 +- SecretAPI/Features/IRegister.cs | 105 ++- SecretAPI/Features/PrefabManager.cs | 83 ++- SecretAPI/Features/PrefabStore.cs | 87 ++- SecretAPI/Features/Registry.cs | 33 +- .../UserSettings/CustomButtonSetting.cs | 115 ++- .../UserSettings/CustomDropdownSetting.cs | 197 +++--- .../Features/UserSettings/CustomHeader.cs | 44 +- .../UserSettings/CustomKeybindSetting.cs | 85 ++- .../UserSettings/CustomPlainTextSetting.cs | 189 +++-- .../Features/UserSettings/CustomSetting.cs | 653 +++++++++--------- .../UserSettings/CustomSliderSetting.cs | 275 ++++---- .../UserSettings/CustomTextAreaSetting.cs | 101 ++- .../UserSettings/CustomTwoButtonSetting.cs | 185 +++-- SecretAPI/Features/UserSettings/ISetting.cs | 25 +- .../UserSettings/SettingResponseType.cs | 33 +- .../Features/SendSettingsPlayerSync.cs | 35 +- .../Features/SendSettingsServerSync.cs | 33 +- .../Features/SettingsOriginalDefinitionFix.cs | 39 +- .../Features/SettingsSyncValidateFix.cs | 39 +- SecretAPI/SecretApi.cs | 93 ++- 42 files changed, 1955 insertions(+), 1998 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5bb1630..5ab906b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,4 +4,5 @@ csharp_style_var_when_type_is_apparent = false:error csharp_style_var_elsewhere = false:error csharp_using_directive_placement = inside_namespace:error csharp_prefer_braces = false -dotnet_code_quality_unused_parameters = non_public:none \ No newline at end of file +dotnet_code_quality_unused_parameters = non_public:none +csharp_style_namespace_declarations = file_scoped:error \ No newline at end of file diff --git a/SecretAPI.Examples/ExampleEntry.cs b/SecretAPI.Examples/ExampleEntry.cs index 32ee205..6dd27b0 100644 --- a/SecretAPI.Examples/ExampleEntry.cs +++ b/SecretAPI.Examples/ExampleEntry.cs @@ -1,41 +1,38 @@ -namespace SecretAPI.Examples +namespace SecretAPI.Examples; + +using System; +using LabApi.Loader.Features.Plugins; +using SecretAPI.Examples.Settings; +using SecretAPI.Features.UserSettings; + +/// +/// Defines the entry for the plugin. +/// +public class ExampleEntry : Plugin { - using System; - using System.Reflection; - using HarmonyLib; - using LabApi.Loader.Features.Plugins; - using SecretAPI.Examples.Settings; - using SecretAPI.Features.UserSettings; - - /// - /// Defines the entry for the plugin. - /// - public class ExampleEntry : Plugin - { - /// - public override string Name { get; } = "SecretAPI.Examples"; + /// + public override string Name { get; } = "SecretAPI.Examples"; - /// - public override string Description { get; } = "An example plugin"; + /// + public override string Description { get; } = "An example plugin"; - /// - public override string Author { get; } = "@misfiy / @obvEvelyn"; + /// + public override string Author { get; } = "@misfiy / @obvEvelyn"; - /// - public override Version Version { get; } = typeof(SecretApi).Assembly.GetName().Version; + /// + public override Version Version { get; } = typeof(SecretApi).Assembly.GetName().Version; - /// - public override Version RequiredApiVersion { get; } = new(LabApi.Features.LabApiProperties.CompiledVersion); + /// + public override Version RequiredApiVersion { get; } = new(LabApi.Features.LabApiProperties.CompiledVersion); - /// - public override void Enable() - { - CustomSetting.Register(new ExampleKeybindSetting(), new ExampleDropdownSetting()); - } + /// + public override void Enable() + { + CustomSetting.Register(new ExampleKeybindSetting(), new ExampleDropdownSetting()); + } - /// - public override void Disable() - { - } + /// + public override void Disable() + { } } \ No newline at end of file diff --git a/SecretAPI.Examples/Patches/ExamplePatch.cs b/SecretAPI.Examples/Patches/ExamplePatch.cs index 167939b..29cd386 100644 --- a/SecretAPI.Examples/Patches/ExamplePatch.cs +++ b/SecretAPI.Examples/Patches/ExamplePatch.cs @@ -1,24 +1,23 @@ -namespace SecretAPI.Examples.Patches -{ - using SecretAPI.Attributes; +namespace SecretAPI.Examples.Patches; + +using SecretAPI.Attributes; - /// - /// An example harmony patch. - /// - [HarmonyPatchCategory(nameof(ExampleEntry))] - /*[HarmonyPatch]*/ - public static class ExamplePatch +/// +/// An example harmony patch. +/// +[HarmonyPatchCategory(nameof(ExampleEntry))] +/*[HarmonyPatch]*/ +public static class ExamplePatch +{ + // gets called before the original method is called + private static bool Prefix() { - // gets called before the original method is called - private static bool Prefix() - { - // prevent original method from running - return false; - } + // prevent original method from running + return false; + } - // gets called after the original method is called - private static void Postfix() - { - } + // gets called after the original method is called + private static void Postfix() + { } } \ No newline at end of file diff --git a/SecretAPI.Examples/Settings/ExampleDropdownSetting.cs b/SecretAPI.Examples/Settings/ExampleDropdownSetting.cs index c47c8d6..f3a17a0 100644 --- a/SecretAPI.Examples/Settings/ExampleDropdownSetting.cs +++ b/SecretAPI.Examples/Settings/ExampleDropdownSetting.cs @@ -1,44 +1,43 @@ -namespace SecretAPI.Examples.Settings +namespace SecretAPI.Examples.Settings; + +using LabApi.Features.Console; +using LabApi.Features.Permissions; +using SecretAPI.Features.UserSettings; + +/// +/// Example version of . +/// +public class ExampleDropdownSetting : CustomDropdownSetting { - using LabApi.Features.Console; - using LabApi.Features.Permissions; - using SecretAPI.Features.UserSettings; + private static readonly string[] ExampleOptions = ["hi", "test", "yum", "fish", "nugget"]; + private static readonly string[] ExampleSupporterOptions = ["bucket", "lava", "wanted", "globe"]; /// - /// Example version of . + /// Initializes a new instance of the class. /// - public class ExampleDropdownSetting : CustomDropdownSetting + public ExampleDropdownSetting() + : base(901, "Example dropdown", ExampleOptions) + { + } + + /// + public override CustomHeader Header => CustomHeader.Examples; + + /// + protected override CustomSetting CreateDuplicate() => new ExampleDropdownSetting(); + + /// + protected override void PersonalizeSetting() + { + if (KnownOwner == null || !KnownOwner.HasAnyPermission("example.supporter")) + return; + + Options = ExampleSupporterOptions; + } + + /// + protected override void HandleSettingUpdate() { - private static readonly string[] ExampleOptions = ["hi", "test", "yum", "fish", "nugget"]; - private static readonly string[] ExampleSupporterOptions = ["bucket", "lava", "wanted", "globe"]; - - /// - /// Initializes a new instance of the class. - /// - public ExampleDropdownSetting() - : base(901, "Example dropdown", ExampleOptions) - { - } - - /// - public override CustomHeader Header => CustomHeader.Examples; - - /// - protected override CustomSetting CreateDuplicate() => new ExampleDropdownSetting(); - - /// - protected override void PersonalizeSetting() - { - if (KnownOwner == null || !KnownOwner.HasAnyPermission("example.supporter")) - return; - - Options = ExampleSupporterOptions; - } - - /// - protected override void HandleSettingUpdate() - { - Logger.Info($"{KnownOwner?.DisplayName ?? "(Null Owner - What went wrong?)"} selected {SelectedOption} (Index {ValidatedSelectedIndex}/{Options.Length - 1})"); - } + Logger.Info($"{KnownOwner?.DisplayName ?? "(Null Owner - What went wrong?)"} selected {SelectedOption} (Index {ValidatedSelectedIndex}/{Options.Length - 1})"); } } \ No newline at end of file diff --git a/SecretAPI.Examples/Settings/ExampleKeybindSetting.cs b/SecretAPI.Examples/Settings/ExampleKeybindSetting.cs index a51f541..df726cb 100644 --- a/SecretAPI.Examples/Settings/ExampleKeybindSetting.cs +++ b/SecretAPI.Examples/Settings/ExampleKeybindSetting.cs @@ -1,38 +1,37 @@ -namespace SecretAPI.Examples.Settings -{ - using LabApi.Features.Wrappers; - using SecretAPI.Features.UserSettings; - using UnityEngine; +namespace SecretAPI.Examples.Settings; + +using LabApi.Features.Wrappers; +using SecretAPI.Features.UserSettings; +using UnityEngine; +/// +/// Example setting for keybinds. +/// +public class ExampleKeybindSetting : CustomKeybindSetting +{ /// - /// Example setting for keybinds. + /// Initializes a new instance of the class. /// - public class ExampleKeybindSetting : CustomKeybindSetting + public ExampleKeybindSetting() + : base(900, "Example Kill Button", KeyCode.G, allowSpectatorTrigger: false) { - /// - /// Initializes a new instance of the class. - /// - public ExampleKeybindSetting() - : base(900, "Example Kill Button", KeyCode.G, allowSpectatorTrigger: false) - { - } + } - /// - public override CustomHeader Header => CustomHeader.Examples; + /// + public override CustomHeader Header => CustomHeader.Examples; - /// - protected override bool CanView(Player player) => player.RemoteAdminAccess; + /// + protected override bool CanView(Player player) => player.RemoteAdminAccess; - /// - protected override CustomSetting CreateDuplicate() => new ExampleKeybindSetting(); + /// + protected override CustomSetting CreateDuplicate() => new ExampleKeybindSetting(); - /// - protected override void HandleSettingUpdate() - { - if (!IsPressed) - return; + /// + protected override void HandleSettingUpdate() + { + if (!IsPressed) + return; - KnownOwner?.Kill(); - } + KnownOwner?.Kill(); } } \ No newline at end of file diff --git a/SecretAPI/Attributes/CallOnLoadAttribute.cs b/SecretAPI/Attributes/CallOnLoadAttribute.cs index 32bcd34..4ece8ce 100644 --- a/SecretAPI/Attributes/CallOnLoadAttribute.cs +++ b/SecretAPI/Attributes/CallOnLoadAttribute.cs @@ -1,74 +1,73 @@ -namespace SecretAPI.Attributes -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using SecretAPI.Features; +namespace SecretAPI.Attributes; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using SecretAPI.Features; +/// +/// Defines the attribute for methods to call on load. +/// +[AttributeUsage(AttributeTargets.Method)] +public class CallOnLoadAttribute : Attribute, IPriority +{ /// - /// Defines the attribute for methods to call on load. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.Method)] - public class CallOnLoadAttribute : Attribute, IPriority + /// The priority of the load. + public CallOnLoadAttribute(int priority = 0) { - /// - /// Initializes a new instance of the class. - /// - /// The priority of the load. - public CallOnLoadAttribute(int priority = 0) - { - Priority = priority; - } + Priority = priority; + } - /// - /// Gets the priority of the loading. - /// - public int Priority { get; } + /// + /// Gets the priority of the loading. + /// + public int Priority { get; } - /// - /// Loads and calls all . - /// - /// The assembly to begin this on. Null will attempt to get calling, but may fail. - public static void Load(Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); - CallAttributeMethodPriority(assembly); - } + /// + /// Loads and calls all . + /// + /// The assembly to begin this on. Null will attempt to get calling, but may fail. + public static void Load(Assembly? assembly = null) + { + assembly ??= Assembly.GetCallingAssembly(); + CallAttributeMethodPriority(assembly); + } - /// - public override bool Equals(object? obj) => obj == this; + /// + public override bool Equals(object? obj) => obj == this; - /// - public override int GetHashCode() => base.GetHashCode(); + /// + public override int GetHashCode() => base.GetHashCode(); - /// - /// Calls the method associated with a Attribute implementing . - /// - /// The assembly to handle calls from. - /// The attribute required for this to be called. - internal static void CallAttributeMethodPriority(Assembly assembly) - where TAttribute : Attribute, IPriority - { - const BindingFlags methodFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - Dictionary methods = new(); + /// + /// Calls the method associated with a Attribute implementing . + /// + /// The assembly to handle calls from. + /// The attribute required for this to be called. + internal static void CallAttributeMethodPriority(Assembly assembly) + where TAttribute : Attribute, IPriority + { + const BindingFlags methodFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + Dictionary methods = new(); - // get all types - foreach (Type type in assembly.GetTypes()) + // get all types + foreach (Type type in assembly.GetTypes()) + { + // get all static methods + foreach (MethodInfo method in type.GetMethods(methodFlags)) { - // get all static methods - foreach (MethodInfo method in type.GetMethods(methodFlags)) - { - TAttribute? attribute = method.GetCustomAttribute(); - if (attribute == null) - continue; + TAttribute? attribute = method.GetCustomAttribute(); + if (attribute == null) + continue; - methods.Add(attribute, method); - } + methods.Add(attribute, method); } - - foreach (KeyValuePair method in methods.OrderBy(static v => v.Key.Priority)) - method.Value.Invoke(null, null); } + + foreach (KeyValuePair method in methods.OrderBy(static v => v.Key.Priority)) + method.Value.Invoke(null, null); } } \ No newline at end of file diff --git a/SecretAPI/Attributes/CallOnUnloadAttribute.cs b/SecretAPI/Attributes/CallOnUnloadAttribute.cs index 65da120..86eb13f 100644 --- a/SecretAPI/Attributes/CallOnUnloadAttribute.cs +++ b/SecretAPI/Attributes/CallOnUnloadAttribute.cs @@ -1,43 +1,42 @@ -namespace SecretAPI.Attributes -{ - using System; - using System.Reflection; - using SecretAPI.Features; +namespace SecretAPI.Attributes; + +using System; +using System.Reflection; +using SecretAPI.Features; +/// +/// Defines the attribute for methods to call on unload. +/// +[AttributeUsage(AttributeTargets.Method)] +public class CallOnUnloadAttribute : Attribute, IPriority +{ /// - /// Defines the attribute for methods to call on unload. + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.Method)] - public class CallOnUnloadAttribute : Attribute, IPriority + /// The priority of the load. + public CallOnUnloadAttribute(int priority = 0) { - /// - /// Initializes a new instance of the class. - /// - /// The priority of the load. - public CallOnUnloadAttribute(int priority = 0) - { - Priority = priority; - } + Priority = priority; + } - /// - /// Gets the priority of the loading. - /// - public int Priority { get; } + /// + /// Gets the priority of the loading. + /// + public int Priority { get; } - /// - /// Unloads and calls all . - /// - /// The assembly to begin this on. Null will attempt to get calling, but may fail. - public static void Unload(Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); - CallOnLoadAttribute.CallAttributeMethodPriority(assembly); - } + /// + /// Unloads and calls all . + /// + /// The assembly to begin this on. Null will attempt to get calling, but may fail. + public static void Unload(Assembly? assembly = null) + { + assembly ??= Assembly.GetCallingAssembly(); + CallOnLoadAttribute.CallAttributeMethodPriority(assembly); + } - /// - public override bool Equals(object? obj) => obj == this; + /// + public override bool Equals(object? obj) => obj == this; - /// - public override int GetHashCode() => base.GetHashCode(); - } + /// + public override int GetHashCode() => base.GetHashCode(); } \ No newline at end of file diff --git a/SecretAPI/Attributes/HarmonyPatchCategory.cs b/SecretAPI/Attributes/HarmonyPatchCategory.cs index 853129d..4257803 100644 --- a/SecretAPI/Attributes/HarmonyPatchCategory.cs +++ b/SecretAPI/Attributes/HarmonyPatchCategory.cs @@ -1,26 +1,25 @@ -namespace SecretAPI.Attributes -{ - using System; - using SecretAPI.Extensions; +namespace SecretAPI.Attributes; + +using System; +using SecretAPI.Extensions; +/// +/// Category handling for . +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +public class HarmonyPatchCategory : Attribute +{ /// - /// Category handling for . + /// Initializes a new instance of the class. /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class HarmonyPatchCategory : Attribute + /// The category of the patch. + public HarmonyPatchCategory(string category) { - /// - /// Initializes a new instance of the class. - /// - /// The category of the patch. - public HarmonyPatchCategory(string category) - { - Category = category; - } - - /// - /// Gets the patch category. - /// - public string Category { get; } + Category = category; } + + /// + /// Gets the patch category. + /// + public string Category { get; } } \ No newline at end of file diff --git a/SecretAPI/Enums/DoorPermissionCheck.cs b/SecretAPI/Enums/DoorPermissionCheck.cs index ad78cac..c1c38af 100644 --- a/SecretAPI/Enums/DoorPermissionCheck.cs +++ b/SecretAPI/Enums/DoorPermissionCheck.cs @@ -1,56 +1,55 @@ -namespace SecretAPI.Enums +namespace SecretAPI.Enums; + +using System; +using Interactables.Interobjects.DoorUtils; +using LabApi.Features.Wrappers; +using PlayerRoles; +using PlayerStatsSystem; +using SecretAPI.Extensions; + +/// +/// Flags to use for . +/// +[Flags] +public enum DoorPermissionCheck { - using System; - using Interactables.Interobjects.DoorUtils; - using LabApi.Features.Wrappers; - using PlayerRoles; - using PlayerStatsSystem; - using SecretAPI.Extensions; - - /// - /// Flags to use for . - /// - [Flags] - public enum DoorPermissionCheck - { - /// - /// None. Will not check anything. - /// - None = 0, - - /// - /// Used to consider . - /// - Bypass = 1 << 0, - - /// - /// Used to consider the player's . - /// - Role = 1 << 1, - - /// - /// Used to consider the player's . - /// - CurrentItem = 1 << 2, - - /// - /// Used to consider the player's inventory, not including the item they are holding. - /// - InventoryExcludingCurrent = 1 << 3, - - /// - /// Used to consider the player's ENTIRE inventory. - /// - FullInventory = CurrentItem | InventoryExcludingCurrent, - - /// - /// Used to consider all. - /// - All = -1, - - /// - /// Used to mirror default base-game checks (bypass mode, role and held item). - /// - Default = Bypass | Role | CurrentItem, - } + /// + /// None. Will not check anything. + /// + None = 0, + + /// + /// Used to consider . + /// + Bypass = 1 << 0, + + /// + /// Used to consider the player's . + /// + Role = 1 << 1, + + /// + /// Used to consider the player's . + /// + CurrentItem = 1 << 2, + + /// + /// Used to consider the player's inventory, not including the item they are holding. + /// + InventoryExcludingCurrent = 1 << 3, + + /// + /// Used to consider the player's ENTIRE inventory. + /// + FullInventory = CurrentItem | InventoryExcludingCurrent, + + /// + /// Used to consider all. + /// + All = -1, + + /// + /// Used to mirror default base-game checks (bypass mode, role and held item). + /// + Default = Bypass | Role | CurrentItem, } \ No newline at end of file diff --git a/SecretAPI/Extensions/CollectionExtensions.cs b/SecretAPI/Extensions/CollectionExtensions.cs index 83af57e..969a9d2 100644 --- a/SecretAPI/Extensions/CollectionExtensions.cs +++ b/SecretAPI/Extensions/CollectionExtensions.cs @@ -1,48 +1,47 @@ -namespace SecretAPI.Extensions -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using Random = UnityEngine.Random; +namespace SecretAPI.Extensions; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Random = UnityEngine.Random; - /// - /// Extensions for collections. - /// - public static class CollectionExtensions +/// +/// Extensions for collections. +/// +public static class CollectionExtensions +{ + /// The collection to pull from. + /// The Type contained by the collection. + extension(IEnumerable collection) { - /// The collection to pull from. - /// The Type contained by the collection. - extension(IEnumerable collection) + /// + /// Gets a random value from the collection. + /// + /// A random value, default value when empty collection. + /// Will occur if the collection is empty. + public T GetRandomValue() { - /// - /// Gets a random value from the collection. - /// - /// A random value, default value when empty collection. - /// Will occur if the collection is empty. - public T GetRandomValue() - { - TryGetRandomValue(collection, out T? value); - return value!; - } + TryGetRandomValue(collection, out T? value); + return value!; + } - /// - /// Tries to get a random value from . - /// - /// The value that was found. Default if none could be found. - /// Whether a non-null value was found. - public bool TryGetRandomValue([NotNullWhen(true)] out T? value) + /// + /// Tries to get a random value from . + /// + /// The value that was found. Default if none could be found. + /// Whether a non-null value was found. + public bool TryGetRandomValue([NotNullWhen(true)] out T? value) + { + IList list = collection as IList ?? collection.ToList(); + if (list.Count == 0) { - IList list = collection as IList ?? collection.ToList(); - if (list.Count == 0) - { - value = default; - return false; - } - - value = list[Random.Range(0, list.Count)]; - return value != null; + value = default; + return false; } + + value = list[Random.Range(0, list.Count)]; + return value != null; } } } \ No newline at end of file diff --git a/SecretAPI/Extensions/HarmonyExtensions.cs b/SecretAPI/Extensions/HarmonyExtensions.cs index f26b436..1eee37b 100644 --- a/SecretAPI/Extensions/HarmonyExtensions.cs +++ b/SecretAPI/Extensions/HarmonyExtensions.cs @@ -1,68 +1,67 @@ -namespace SecretAPI.Extensions -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using HarmonyLib; - using LabApi.Features.Console; - using SecretAPI.Attributes; +namespace SecretAPI.Extensions; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using HarmonyLib; +using LabApi.Features.Console; +using SecretAPI.Attributes; - /// - /// Handles patching. - /// - public static class HarmonyExtensions +/// +/// Handles patching. +/// +public static class HarmonyExtensions +{ + /// The harmony to use for the patch. + extension(Harmony harmony) { - /// The harmony to use for the patch. - extension(Harmony harmony) + /// + /// Patches all methods with the proper . + /// + /// The category to patch. + /// The assembly to find patches in. + public void PatchCategory(string category, Assembly? assembly = null) { - /// - /// Patches all methods with the proper . - /// - /// The category to patch. - /// The assembly to find patches in. - public void PatchCategory(string category, Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); + assembly ??= Assembly.GetCallingAssembly(); - assembly.GetTypes().Where(type => - { - IEnumerable categories = type.GetCustomAttributes(); - return categories.Any(c => c.Category == category); - }) - .Do(type => SafePatch(harmony, type)); - } + assembly.GetTypes().Where(type => + { + IEnumerable categories = type.GetCustomAttributes(); + return categories.Any(c => c.Category == category); + }) + .Do(type => SafePatch(harmony, type)); + } - /// - /// Patches all patches that don't have a . - /// - /// The assembly to look for patches. - public void PatchAllNoCategory(Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); + /// + /// Patches all patches that don't have a . + /// + /// The assembly to look for patches. + public void PatchAllNoCategory(Assembly? assembly = null) + { + assembly ??= Assembly.GetCallingAssembly(); - assembly.GetTypes().Where(type => - { - IEnumerable categories = type.GetCustomAttributes(); - return !categories.Any(); - }) - .Do(type => SafePatch(harmony, type)); - } + assembly.GetTypes().Where(type => + { + IEnumerable categories = type.GetCustomAttributes(); + return !categories.Any(); + }) + .Do(type => SafePatch(harmony, type)); + } - /// - /// Attempts to safely patch a , logging any errors. - /// - /// The to attempt to patch. - public void SafePatch(Type type) + /// + /// Attempts to safely patch a , logging any errors. + /// + /// The to attempt to patch. + public void SafePatch(Type type) + { + try { - try - { - harmony.CreateClassProcessor(type).Patch(); - } - catch (Exception ex) - { - Logger.Error($"[HarmonyExtensions] failed to safely patch {harmony.Id} ({type.FullName}): {ex}"); - } + harmony.CreateClassProcessor(type).Patch(); + } + catch (Exception ex) + { + Logger.Error($"[HarmonyExtensions] failed to safely patch {harmony.Id} ({type.FullName}): {ex}"); } } } diff --git a/SecretAPI/Extensions/MirrorExtensions.cs b/SecretAPI/Extensions/MirrorExtensions.cs index 30aea4e..4b2dc81 100644 --- a/SecretAPI/Extensions/MirrorExtensions.cs +++ b/SecretAPI/Extensions/MirrorExtensions.cs @@ -1,66 +1,65 @@ -namespace SecretAPI.Extensions -{ - using System; - using System.Reflection; - using LabApi.Features.Console; - using LabApi.Features.Wrappers; - using Mirror; +namespace SecretAPI.Extensions; + +using System; +using System.Reflection; +using LabApi.Features.Console; +using LabApi.Features.Wrappers; +using Mirror; +/// +/// Extensions related to Mirror. +/// +public static class MirrorExtensions +{ /// - /// Extensions related to Mirror. + /// Send a fake rpc message to a player. /// - public static class MirrorExtensions + /// The target to send the rpc to. + /// The network behaviour containing the rpc. + /// The type containing the rpc. + /// The name of the rpc to call. + /// The values to write to the writer. + public static void SendFakeRpcMessage(this Player target, NetworkBehaviour behaviour, Type type, string rpcName, params object[] values) { - /// - /// Send a fake rpc message to a player. - /// - /// The target to send the rpc to. - /// The network behaviour containing the rpc. - /// The type containing the rpc. - /// The name of the rpc to call. - /// The values to write to the writer. - public static void SendFakeRpcMessage(this Player target, NetworkBehaviour behaviour, Type type, string rpcName, params object[] values) - { - NetworkWriterPooled pooledWriter = NetworkWriterPool.Get(); + NetworkWriterPooled pooledWriter = NetworkWriterPool.Get(); - foreach (object obj in values) - ProperWrite(pooledWriter, obj); + foreach (object obj in values) + ProperWrite(pooledWriter, obj); - RpcMessage rpcMessage = new() - { - netId = behaviour.netId, - componentIndex = behaviour.ComponentIndex, - functionHash = (ushort)ReflectionExtensions.GetLongFuncName(type, rpcName).GetStableHashCode(), - payload = pooledWriter.ToArraySegment(), - }; + RpcMessage rpcMessage = new() + { + netId = behaviour.netId, + componentIndex = behaviour.ComponentIndex, + functionHash = (ushort)ReflectionExtensions.GetLongFuncName(type, rpcName).GetStableHashCode(), + payload = pooledWriter.ToArraySegment(), + }; - target.Connection.Send(rpcMessage); - NetworkWriterPool.Return(pooledWriter); - } + target.Connection.Send(rpcMessage); + NetworkWriterPool.Return(pooledWriter); + } - /// - /// Handles writing into a . - /// - /// The writer to write the object to. - /// The object to write. - public static void ProperWrite(this NetworkWriter writer, object obj) + /// + /// Handles writing into a . + /// + /// The writer to write the object to. + /// The object to write. + public static void ProperWrite(this NetworkWriter writer, object obj) + { + Type genericType = typeof(Writer<>).MakeGenericType(obj.GetType()); + FieldInfo? writeField = genericType.GetField("write", BindingFlags.Static | BindingFlags.Public); + if (writeField == null) { - Type genericType = typeof(Writer<>).MakeGenericType(obj.GetType()); - FieldInfo? writeField = genericType.GetField("write", BindingFlags.Static | BindingFlags.Public); - if (writeField == null) - { - Logger.Warn($"Tried to write type: {obj.GetType()} but has no NetworkWriter!"); - return; - } - - object? writeDelegate = writeField.GetValue(null); - if (writeDelegate is not Delegate del) - { - Logger.Warn($"Writer<{obj.GetType()}>.write is not a delegate!"); - return; - } + Logger.Warn($"Tried to write type: {obj.GetType()} but has no NetworkWriter!"); + return; + } - del.DynamicInvoke(writer, obj); + object? writeDelegate = writeField.GetValue(null); + if (writeDelegate is not Delegate del) + { + Logger.Warn($"Writer<{obj.GetType()}>.write is not a delegate!"); + return; } + + del.DynamicInvoke(writer, obj); } } \ No newline at end of file diff --git a/SecretAPI/Extensions/PlayerExtensions.cs b/SecretAPI/Extensions/PlayerExtensions.cs index d2a6737..b4a8a18 100644 --- a/SecretAPI/Extensions/PlayerExtensions.cs +++ b/SecretAPI/Extensions/PlayerExtensions.cs @@ -1,82 +1,81 @@ -namespace SecretAPI.Extensions -{ - using CustomPlayerEffects; - using Interactables.Interobjects.DoorUtils; - using LabApi.Features.Wrappers; - using SecretAPI.Enums; +namespace SecretAPI.Extensions; - /// - /// Extensions related to the player. - /// - public static class PlayerExtensions - { - /// The player to get effect from. - extension(Player player) - { - /// - /// Gets an effect of a player based on the effect name. - /// - /// Name of the effect to find. - /// The effect. - public StatusEffectBase GetEffect(string name) - => player.ReferenceHub.playerEffectsController.TryGetEffect(name, out StatusEffectBase? effect) ? effect : null!; +using CustomPlayerEffects; +using Interactables.Interobjects.DoorUtils; +using LabApi.Features.Wrappers; +using SecretAPI.Enums; - /// - /// Checks whether a player has permission to access a . - /// - /// The requester to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public bool HasDoorPermission(IDoorPermissionRequester requester, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - { - if (checkFlags.HasFlag(DoorPermissionCheck.Bypass) && player.IsBypassEnabled) - return true; +/// +/// Extensions related to the player. +/// +public static class PlayerExtensions +{ + /// The player to get effect from. + extension(Player player) + { + /// + /// Gets an effect of a player based on the effect name. + /// + /// Name of the effect to find. + /// The effect. + public StatusEffectBase GetEffect(string name) + => player.ReferenceHub.playerEffectsController.TryGetEffect(name, out StatusEffectBase? effect) ? effect : null!; - if (checkFlags.HasFlag(DoorPermissionCheck.Role) && player.RoleBase is IDoorPermissionProvider roleProvider && requester.PermissionsPolicy.CheckPermissions(roleProvider.GetPermissions(requester))) - return true; + /// + /// Checks whether a player has permission to access a . + /// + /// The requester to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasDoorPermission(IDoorPermissionRequester requester, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + { + if (checkFlags.HasFlag(DoorPermissionCheck.Bypass) && player.IsBypassEnabled) + return true; - foreach (Item item in player.Items) - { - bool isCurrent = item == player.CurrentItem; - if (!checkFlags.HasFlag(DoorPermissionCheck.CurrentItem) && isCurrent) - continue; + if (checkFlags.HasFlag(DoorPermissionCheck.Role) && player.RoleBase is IDoorPermissionProvider roleProvider && requester.PermissionsPolicy.CheckPermissions(roleProvider.GetPermissions(requester))) + return true; - if (!checkFlags.HasFlag(DoorPermissionCheck.InventoryExcludingCurrent) && !isCurrent) - continue; + foreach (Item item in player.Items) + { + bool isCurrent = item == player.CurrentItem; + if (!checkFlags.HasFlag(DoorPermissionCheck.CurrentItem) && isCurrent) + continue; - if (item.Base is IDoorPermissionProvider itemProvider && requester.PermissionsPolicy.CheckPermissions(itemProvider.GetPermissions(requester))) - return true; - } + if (!checkFlags.HasFlag(DoorPermissionCheck.InventoryExcludingCurrent) && !isCurrent) + continue; - return false; + if (item.Base is IDoorPermissionProvider itemProvider && requester.PermissionsPolicy.CheckPermissions(itemProvider.GetPermissions(requester))) + return true; } - /// - /// Checks whether a player has permission to access a . - /// - /// The door to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public bool HasDoorPermission(Door door, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - => player.HasDoorPermission(door.Base, checkFlags); + return false; + } - /// - /// Checks whether a player has permission to access a . - /// - /// The locker chamber to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public bool HasLockerChamberPermission(LockerChamber chamber, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - => player.HasDoorPermission(chamber.Base, checkFlags); + /// + /// Checks whether a player has permission to access a . + /// + /// The door to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasDoorPermission(Door door, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + => player.HasDoorPermission(door.Base, checkFlags); - /// - /// Checks whether a player has permission to access a . - /// - /// The generator to check for permissions. - /// The to use for checking if a player has it. - /// Whether a valid permission was found. - public bool HasGeneratorPermission(Generator generator, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) - => player.HasDoorPermission(generator.Base, checkFlags); - } + /// + /// Checks whether a player has permission to access a . + /// + /// The locker chamber to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasLockerChamberPermission(LockerChamber chamber, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + => player.HasDoorPermission(chamber.Base, checkFlags); + + /// + /// Checks whether a player has permission to access a . + /// + /// The generator to check for permissions. + /// The to use for checking if a player has it. + /// Whether a valid permission was found. + public bool HasGeneratorPermission(Generator generator, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) + => player.HasDoorPermission(generator.Base, checkFlags); } } \ No newline at end of file diff --git a/SecretAPI/Extensions/ReflectionExtensions.cs b/SecretAPI/Extensions/ReflectionExtensions.cs index 987ca0b..f367c6e 100644 --- a/SecretAPI/Extensions/ReflectionExtensions.cs +++ b/SecretAPI/Extensions/ReflectionExtensions.cs @@ -1,52 +1,51 @@ -namespace SecretAPI.Extensions -{ - using System; - using System.Linq; - using System.Reflection; +namespace SecretAPI.Extensions; + +using System; +using System.Linq; +using System.Reflection; +/// +/// Extensions for reflection. +/// +public static class ReflectionExtensions +{ /// - /// Extensions for reflection. + /// Gets the long name of a function. /// - public static class ReflectionExtensions + /// The type containing the method. + /// The method name. + /// The long function name. + /// When the method could not be found. + public static string GetLongFuncName(Type type, string methodName) { - /// - /// Gets the long name of a function. - /// - /// The type containing the method. - /// The method name. - /// The long function name. - /// When the method could not be found. - public static string GetLongFuncName(Type type, string methodName) - { - const BindingFlags methodFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + const BindingFlags methodFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - MethodInfo method = type.GetMethod(methodName, methodFlags) ?? throw new InvalidOperationException($"[ReflectionExtensions.GetLongFuncName] {type.FullName}.{methodName} could not be found."); - return GetLongFuncName(type, method); - } + MethodInfo method = type.GetMethod(methodName, methodFlags) ?? throw new InvalidOperationException($"[ReflectionExtensions.GetLongFuncName] {type.FullName}.{methodName} could not be found."); + return GetLongFuncName(type, method); + } - /// - /// Gets the long name of a function. - /// - /// The type containing the method. - /// The method to use. - /// The long function name. - public static string GetLongFuncName(Type type, MethodInfo method) - { - return $"{method.ReturnType.FullName} {type.FullName}::{method.Name}({string.Join(",", method.GetParameters().Select(x => x.ParameterType.FullName))})"; - } + /// + /// Gets the long name of a function. + /// + /// The type containing the method. + /// The method to use. + /// The long function name. + public static string GetLongFuncName(Type type, MethodInfo method) + { + return $"{method.ReturnType.FullName} {type.FullName}::{method.Name}({string.Join(",", method.GetParameters().Select(x => x.ParameterType.FullName))})"; + } - /// - /// Copies the properties. - /// - /// The source of the properties to copy. - /// Where to copy to. - public static void CopyProperties(this object source, object destination) + /// + /// Copies the properties. + /// + /// The source of the properties to copy. + /// Where to copy to. + public static void CopyProperties(this object source, object destination) + { + Type destinationType = destination.GetType(); + foreach (PropertyInfo property in source.GetType().GetProperties()) { - Type destinationType = destination.GetType(); - foreach (PropertyInfo property in source.GetType().GetProperties()) - { - destinationType.GetProperty(property.Name)?.SetValue(destination, property.GetValue(source)); - } + destinationType.GetProperty(property.Name)?.SetValue(destination, property.GetValue(source)); } } } \ No newline at end of file diff --git a/SecretAPI/Extensions/RoomExtensions.cs b/SecretAPI/Extensions/RoomExtensions.cs index 41629af..5225af3 100644 --- a/SecretAPI/Extensions/RoomExtensions.cs +++ b/SecretAPI/Extensions/RoomExtensions.cs @@ -1,41 +1,40 @@ -namespace SecretAPI.Extensions +namespace SecretAPI.Extensions; + +using System.Collections.Generic; +using LabApi.Features.Wrappers; +using MapGeneration; +using UnityEngine; + +/// +/// Extensions related to rooms. +/// +public static class RoomExtensions { - using System.Collections.Generic; - using LabApi.Features.Wrappers; - using MapGeneration; - using UnityEngine; + private static readonly List KnownUnsafeRooms = + [ + RoomName.HczTesla, // Instant death + RoomName.EzEvacShelter, // Stuck permanently + RoomName.EzCollapsedTunnel, // Stuck permanently + RoomName.HczWaysideIncinerator, // Death + RoomName.Hcz096, // Void + ]; /// - /// Extensions related to rooms. + /// Gets whether a room is safe to teleport to. Will consider decontamination, warhead, teslas and void rooms. /// - public static class RoomExtensions + /// The room to check. + /// Whether the room is safe to teleport to. + public static bool IsSafeToTeleport(this Room room) { - private static readonly List KnownUnsafeRooms = - [ - RoomName.HczTesla, // Instant death - RoomName.EzEvacShelter, // Stuck permanently - RoomName.EzCollapsedTunnel, // Stuck permanently - RoomName.HczWaysideIncinerator, // Death - RoomName.Hcz096, // Void - ]; - - /// - /// Gets whether a room is safe to teleport to. Will consider decontamination, warhead, teslas and void rooms. - /// - /// The room to check. - /// Whether the room is safe to teleport to. - public static bool IsSafeToTeleport(this Room room) - { - if (Warhead.IsDetonated && room.Zone != FacilityZone.Surface) - return false; + if (Warhead.IsDetonated && room.Zone != FacilityZone.Surface) + return false; - if (Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) - return false; + if (Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) + return false; - if (KnownUnsafeRooms.Contains(room.Name)) - return false; + if (KnownUnsafeRooms.Contains(room.Name)) + return false; - return Physics.Raycast(room.Position, Vector3.down, out _, 2); - } + return Physics.Raycast(room.Position, Vector3.down, out _, 2); } } \ No newline at end of file diff --git a/SecretAPI/Extensions/Scp914Extensions.cs b/SecretAPI/Extensions/Scp914Extensions.cs index ad13efb..cab6b07 100644 --- a/SecretAPI/Extensions/Scp914Extensions.cs +++ b/SecretAPI/Extensions/Scp914Extensions.cs @@ -1,45 +1,44 @@ -namespace SecretAPI.Extensions -{ - using System.Linq; - using LabApi.Features.Interfaces; - using LabApi.Features.Wrappers; - using Scp914; +namespace SecretAPI.Extensions; + +using System.Linq; +using LabApi.Features.Interfaces; +using LabApi.Features.Wrappers; +using Scp914; +/// +/// Extensions related to SCP-914. +/// +public static class Scp914Extensions +{ /// - /// Extensions related to SCP-914. + /// Process Player like 914 Without Being in 914. /// - public static class Scp914Extensions + /// The player. + /// If it should upgrade only the held item. + /// The knob setting. + public static void Process914Player(this Player player, bool heldOnly, Scp914KnobSetting setting) { - /// - /// Process Player like 914 Without Being in 914. - /// - /// The player. - /// If it should upgrade only the held item. - /// The knob setting. - public static void Process914Player(this Player player, bool heldOnly, Scp914KnobSetting setting) + if (heldOnly) { - if (heldOnly) - { - Process914Item(player.CurrentItem, setting); - return; - } - - foreach (Item item in player.Items.ToList()) - Process914Item(item, setting); + Process914Item(player.CurrentItem, setting); + return; } - /// - /// Processes an item in 914. - /// - /// The item to process. - /// The setting to process the item on. - public static void Process914Item(this Item? item, Scp914KnobSetting setting) - { - if (item == null) - return; + foreach (Item item in player.Items.ToList()) + Process914Item(item, setting); + } - IScp914ItemProcessor? processor = Scp914.GetItemProcessor(item.Type); - processor?.UpgradeItem(setting, item); - } + /// + /// Processes an item in 914. + /// + /// The item to process. + /// The setting to process the item on. + public static void Process914Item(this Item? item, Scp914KnobSetting setting) + { + if (item == null) + return; + + IScp914ItemProcessor? processor = Scp914.GetItemProcessor(item.Type); + processor?.UpgradeItem(setting, item); } } \ No newline at end of file diff --git a/SecretAPI/Features/Effects/CustomPlayerEffect.cs b/SecretAPI/Features/Effects/CustomPlayerEffect.cs index a74b743..6fedb8d 100644 --- a/SecretAPI/Features/Effects/CustomPlayerEffect.cs +++ b/SecretAPI/Features/Effects/CustomPlayerEffect.cs @@ -1,68 +1,67 @@ -namespace SecretAPI.Features.Effects +namespace SecretAPI.Features.Effects; + +using System; +using System.Collections.Generic; +using CustomPlayerEffects; +using LabApi.Features.Wrappers; +using SecretAPI.Attributes; +using SecretAPI.Extensions; +using UnityEngine; +using UnityEngine.SceneManagement; +using Logger = LabApi.Features.Console.Logger; + +/// +/// Handles custom player effects. +/// Must register to to work. +/// +public abstract class CustomPlayerEffect : StatusEffectBase { - using System; - using System.Collections.Generic; - using CustomPlayerEffects; - using LabApi.Features.Wrappers; - using SecretAPI.Attributes; - using SecretAPI.Extensions; - using UnityEngine; - using UnityEngine.SceneManagement; - using Logger = LabApi.Features.Console.Logger; + private static bool isLoaded; /// - /// Handles custom player effects. - /// Must register to to work. + /// Gets a list of types to register (Must inherit ). + /// Must be , can be gotten through typeof(Scp207) /// - public abstract class CustomPlayerEffect : StatusEffectBase - { - private static bool isLoaded; + public static List EffectsToRegister { get; } = []; - /// - /// Gets a list of types to register (Must inherit ). - /// Must be , can be gotten through typeof(Scp207) - /// - public static List EffectsToRegister { get; } = []; + /// + /// Gets the with this effect. + /// + public Player Owner => field ??= Player.Get(Hub); - /// - /// Gets the with this effect. - /// - public Player Owner => field ??= Player.Get(Hub); + /// + public override string ToString() => $"{GetType().Name}: Owner ({Owner}) - Intensity ({Intensity}) - Duration {Duration}"; - /// - public override string ToString() => $"{GetType().Name}: Owner ({Owner}) - Intensity ({Intensity}) - Duration {Duration}"; + /// + /// Initializes the to implement . + /// + [CallOnLoad] + internal static void Initialize() + { + SecretApi.Harmony.PatchCategory(nameof(CustomPlayerEffect), SecretApi.Assembly); + EffectsToRegister.Add(typeof(TemporaryDamageImmunity)); + EffectsToRegister.Add(typeof(StaminaUsageDisablerEffect)); + EffectsToRegister.Add(typeof(SprintDisablerEffect)); - /// - /// Initializes the to implement . - /// - [CallOnLoad] - internal static void Initialize() + SceneManager.sceneLoaded += (_, _) => { - SecretApi.Harmony.PatchCategory(nameof(CustomPlayerEffect), SecretApi.Assembly); - EffectsToRegister.Add(typeof(TemporaryDamageImmunity)); - EffectsToRegister.Add(typeof(StaminaUsageDisablerEffect)); - EffectsToRegister.Add(typeof(SprintDisablerEffect)); - - SceneManager.sceneLoaded += (_, _) => - { - if (isLoaded) - return; + if (isLoaded) + return; - isLoaded = true; + isLoaded = true; - Transform playerEffects = PrefabStore.Prefab.playerEffectsController.effectsGameObject.transform; - foreach (Type type in EffectsToRegister) + Transform playerEffects = PrefabStore.Prefab.playerEffectsController.effectsGameObject.transform; + foreach (Type type in EffectsToRegister) + { + if (!typeof(StatusEffectBase).IsAssignableFrom(type)) { - if (!typeof(StatusEffectBase).IsAssignableFrom(type)) - { - Logger.Error($"[CustomPlayerEffect.Initialize] {type.FullName} is not a valid StatusEffectBase and thus could not be registered!"); - continue; - } - - // register effect into prefab - new GameObject(type.Name, type).transform.parent = playerEffects; + Logger.Error($"[CustomPlayerEffect.Initialize] {type.FullName} is not a valid StatusEffectBase and thus could not be registered!"); + continue; } - }; - } + + // register effect into prefab + new GameObject(type.Name, type).transform.parent = playerEffects; + } + }; } } \ No newline at end of file diff --git a/SecretAPI/Features/Effects/CustomTickingPlayerEffect.cs b/SecretAPI/Features/Effects/CustomTickingPlayerEffect.cs index 7cd15b8..b948220 100644 --- a/SecretAPI/Features/Effects/CustomTickingPlayerEffect.cs +++ b/SecretAPI/Features/Effects/CustomTickingPlayerEffect.cs @@ -1,47 +1,46 @@ -namespace SecretAPI.Features.Effects +namespace SecretAPI.Features.Effects; + +using CustomPlayerEffects; +using UnityEngine; + +/// +/// Custom Effect for . +/// +public abstract class CustomTickingPlayerEffect : CustomPlayerEffect { - using CustomPlayerEffects; - using UnityEngine; + private float timeTillTick; + + /// + /// Gets the time that should pass between each tick. + /// + protected virtual float TimePerTick => 1; + + /// + public override void Enabled() + { + timeTillTick = TimePerTick; + } + + /// + public override void OnEffectUpdate() + { + base.OnEffectUpdate(); + + if (TimePerTick == 0) + return; + + timeTillTick -= Time.deltaTime; + if (timeTillTick > 0) + return; + + timeTillTick += TimePerTick; + OnTick(); + } /// - /// Custom Effect for . + /// Called everytime passes. /// - public abstract class CustomTickingPlayerEffect : CustomPlayerEffect + public virtual void OnTick() { - private float timeTillTick; - - /// - /// Gets the time that should pass between each tick. - /// - protected virtual float TimePerTick => 1; - - /// - public override void Enabled() - { - timeTillTick = TimePerTick; - } - - /// - public override void OnEffectUpdate() - { - base.OnEffectUpdate(); - - if (TimePerTick == 0) - return; - - timeTillTick -= Time.deltaTime; - if (timeTillTick > 0) - return; - - timeTillTick += TimePerTick; - OnTick(); - } - - /// - /// Called everytime passes. - /// - public virtual void OnTick() - { - } } } \ No newline at end of file diff --git a/SecretAPI/Features/Effects/SprintDisablerEffect.cs b/SecretAPI/Features/Effects/SprintDisablerEffect.cs index de3a15b..44f197d 100644 --- a/SecretAPI/Features/Effects/SprintDisablerEffect.cs +++ b/SecretAPI/Features/Effects/SprintDisablerEffect.cs @@ -1,30 +1,29 @@ -namespace SecretAPI.Features.Effects -{ - using PlayerRoles.FirstPersonControl; +namespace SecretAPI.Features.Effects; - /// - /// Effect that disables sprinting for a player. Sets stamina to 0 and disables regen. - /// - public class SprintDisablerEffect : CustomPlayerEffect, IStaminaModifier - { - /// - public bool StaminaModifierActive => IsEnabled; +using PlayerRoles.FirstPersonControl; - /// - public float StaminaRegenMultiplier => 0; +/// +/// Effect that disables sprinting for a player. Sets stamina to 0 and disables regen. +/// +public class SprintDisablerEffect : CustomPlayerEffect, IStaminaModifier +{ + /// + public bool StaminaModifierActive => IsEnabled; - /// - public override EffectClassification Classification => EffectClassification.Negative; + /// + public float StaminaRegenMultiplier => 0; - /// - public override void Enabled() - { - Owner.StaminaRemaining = 0; - } + /// + public override EffectClassification Classification => EffectClassification.Negative; - /// - public override void Disabled() - { - } + /// + public override void Enabled() + { + Owner.StaminaRemaining = 0; + } + + /// + public override void Disabled() + { } } \ No newline at end of file diff --git a/SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs b/SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs index 8e28c6e..d20d6a3 100644 --- a/SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs +++ b/SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs @@ -1,19 +1,18 @@ -namespace SecretAPI.Features.Effects -{ - using PlayerRoles.FirstPersonControl; +namespace SecretAPI.Features.Effects; + +using PlayerRoles.FirstPersonControl; - /// - /// Effect that disables stamina usage. - /// - public class StaminaUsageDisablerEffect : CustomPlayerEffect, IStaminaModifier - { - /// - public bool StaminaModifierActive => IsEnabled; +/// +/// Effect that disables stamina usage. +/// +public class StaminaUsageDisablerEffect : CustomPlayerEffect, IStaminaModifier +{ + /// + public bool StaminaModifierActive => IsEnabled; - /// - public float StaminaUsageMultiplier => 0; + /// + public float StaminaUsageMultiplier => 0; - /// - public override EffectClassification Classification => EffectClassification.Positive; - } + /// + public override EffectClassification Classification => EffectClassification.Positive; } \ No newline at end of file diff --git a/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs b/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs index 1001f9e..25daa52 100644 --- a/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs +++ b/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs @@ -1,20 +1,19 @@ -namespace SecretAPI.Features.Effects -{ - using CustomPlayerEffects; - using PlayerStatsSystem; +namespace SecretAPI.Features.Effects; + +using CustomPlayerEffects; +using PlayerStatsSystem; - /// - /// Grants a player temporary damage immunity. - /// - public class TemporaryDamageImmunity : CustomPlayerEffect, IDamageModifierEffect - { - /// - public bool DamageModifierActive => IsEnabled; +/// +/// Grants a player temporary damage immunity. +/// +public class TemporaryDamageImmunity : CustomPlayerEffect, IDamageModifierEffect +{ + /// + public bool DamageModifierActive => IsEnabled; - /// - public override EffectClassification Classification => EffectClassification.Technical; + /// + public override EffectClassification Classification => EffectClassification.Technical; - /// - public float GetDamageModifier(float baseDamage, DamageHandlerBase handler, HitboxType hitboxType) => 0; - } + /// + public float GetDamageModifier(float baseDamage, DamageHandlerBase handler, HitboxType hitboxType) => 0; } \ No newline at end of file diff --git a/SecretAPI/Features/IPriority.cs b/SecretAPI/Features/IPriority.cs index f337724..6f0666b 100644 --- a/SecretAPI/Features/IPriority.cs +++ b/SecretAPI/Features/IPriority.cs @@ -1,13 +1,12 @@ -namespace SecretAPI.Features +namespace SecretAPI.Features; + +/// +/// Handles IPriority. +/// +public interface IPriority { /// - /// Handles IPriority. + /// Gets the current priority. /// - public interface IPriority - { - /// - /// Gets the current priority. - /// - public int Priority { get; } - } + public int Priority { get; } } \ No newline at end of file diff --git a/SecretAPI/Features/IRegister.cs b/SecretAPI/Features/IRegister.cs index 11ba45b..11d04b4 100644 --- a/SecretAPI/Features/IRegister.cs +++ b/SecretAPI/Features/IRegister.cs @@ -1,71 +1,70 @@ -namespace SecretAPI.Features +namespace SecretAPI.Features; + +using System; +using System.Collections.Generic; +using System.Reflection; + +/// +/// Interface used to define a type that should auto register. +/// +public interface IRegister { - using System; - using System.Collections.Generic; - using System.Reflection; + /// + /// A list of all registered items for an assembly. + /// + private static Dictionary> registerables = new(); /// - /// Interface used to define a type that should auto register. + /// Attempts to register the object. /// - public interface IRegister + public void TryRegister(); + + /// + /// Attempts to unregister the object. + /// + public void TryUnregister() { - /// - /// A list of all registered items for an assembly. - /// - private static Dictionary> registerables = new(); + // default empty to prevent breaking change + } - /// - /// Attempts to register the object. - /// - public void TryRegister(); + /// + /// Registers all . + /// + /// The assembly to register from. + public static void RegisterAll(Assembly? assembly = null) + { + assembly ??= Assembly.GetCallingAssembly(); - /// - /// Attempts to unregister the object. - /// - public void TryUnregister() - { - // default empty to prevent breaking change - } + registerables.TryAdd(assembly, new()); - /// - /// Registers all . - /// - /// The assembly to register from. - public static void RegisterAll(Assembly? assembly = null) + foreach (Type type in assembly.GetTypes()) { - assembly ??= Assembly.GetCallingAssembly(); - - registerables.TryAdd(assembly, new()); + if (type.IsAbstract || type.IsInterface) + continue; - foreach (Type type in assembly.GetTypes()) - { - if (type.IsAbstract || type.IsInterface) - continue; + if (!typeof(IRegister).IsAssignableFrom(type)) + continue; - if (!typeof(IRegister).IsAssignableFrom(type)) - continue; + object obj = Activator.CreateInstance(type); + if (obj is not IRegister register) + continue; - object obj = Activator.CreateInstance(type); - if (obj is not IRegister register) - continue; - - registerables[assembly].Add(register); - register.TryRegister(); - } + registerables[assembly].Add(register); + register.TryRegister(); } + } - /// - /// Unregisters all from an . - /// - /// The assembly to unregister from. - public static void UnRegisterAll(Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); + /// + /// Unregisters all from an . + /// + /// The assembly to unregister from. + public static void UnRegisterAll(Assembly? assembly = null) + { + assembly ??= Assembly.GetCallingAssembly(); - foreach (IRegister register in registerables[assembly]) - register.TryUnregister(); + foreach (IRegister register in registerables[assembly]) + register.TryUnregister(); - registerables.Remove(assembly); - } + registerables.Remove(assembly); } } \ No newline at end of file diff --git a/SecretAPI/Features/PrefabManager.cs b/SecretAPI/Features/PrefabManager.cs index a5d3c7a..80250c3 100644 --- a/SecretAPI/Features/PrefabManager.cs +++ b/SecretAPI/Features/PrefabManager.cs @@ -1,47 +1,46 @@ -namespace SecretAPI.Features +namespace SecretAPI.Features; + +using System; +using System.Linq; +using Interactables.Interobjects; +using MapGeneration; + +/// +/// Manages prefabs that have variants and cannot be easily used within . +/// +public static class PrefabManager { - using System; - using System.Linq; - using Interactables.Interobjects; - using MapGeneration; + private const string LczDoorName = "LCZ BreakableDoor"; + private const string HczDoorName = "HCZ BreakableDoor"; + private const string HczBulkDoorName = "HCZ BulkDoor"; + private const string EzDoorName = "EZ BreakableDoor"; + + /// + /// Gets the prefab. + /// + public static ReferenceHub PlayerPrefab => PrefabStore.Prefab; + + /// + /// Gets the found in . + /// + public static BasicDoor LczDoorPrefab => field ??= GetDoor(LczDoorName); /// - /// Manages prefabs that have variants and cannot be easily used within . + /// Gets the found in . /// - public static class PrefabManager - { - private const string LczDoorName = "LCZ BreakableDoor"; - private const string HczDoorName = "HCZ BreakableDoor"; - private const string HczBulkDoorName = "HCZ BulkDoor"; - private const string EzDoorName = "EZ BreakableDoor"; - - /// - /// Gets the prefab. - /// - public static ReferenceHub PlayerPrefab => PrefabStore.Prefab; - - /// - /// Gets the found in . - /// - public static BasicDoor LczDoorPrefab => field ??= GetDoor(LczDoorName); - - /// - /// Gets the found in . - /// - public static BasicDoor HczDoorPrefab => field ??= GetDoor(HczDoorName); - - /// - /// Gets the found in . - /// - public static BasicDoor HczBulkDoorPrefab => field ??= GetDoor(HczBulkDoorName); - - /// - /// Gets the found in . - /// - public static BasicDoor EzDoorPrefab => field ??= GetDoor(EzDoorName); - - private static BasicDoor GetDoor(string name) - => PrefabStore.AllComponentPrefabs.FirstOrDefault(d => d.name == name) - ?? throw new InvalidOperationException($"[PrefabManager] Failed to get door named {name} | Report this as a bug!"); - } + public static BasicDoor HczDoorPrefab => field ??= GetDoor(HczDoorName); + + /// + /// Gets the found in . + /// + public static BasicDoor HczBulkDoorPrefab => field ??= GetDoor(HczBulkDoorName); + + /// + /// Gets the found in . + /// + public static BasicDoor EzDoorPrefab => field ??= GetDoor(EzDoorName); + + private static BasicDoor GetDoor(string name) + => PrefabStore.AllComponentPrefabs.FirstOrDefault(d => d.name == name) + ?? throw new InvalidOperationException($"[PrefabManager] Failed to get door named {name} | Report this as a bug!"); } \ No newline at end of file diff --git a/SecretAPI/Features/PrefabStore.cs b/SecretAPI/Features/PrefabStore.cs index 3c0b675..b60c89e 100644 --- a/SecretAPI/Features/PrefabStore.cs +++ b/SecretAPI/Features/PrefabStore.cs @@ -1,61 +1,60 @@ -namespace SecretAPI.Features +namespace SecretAPI.Features; + +using System.Collections.Generic; +using System.Linq; +using Interactables.Interobjects; +using Mirror; +using NorthwoodLib.Pools; +using UnityEngine; + +/// +/// Handles the storing of a prefab. +/// +/// The prefab to use. +/// For Doors use . +public static class PrefabStore + where TPrefab : NetworkBehaviour { - using System.Collections.Generic; - using System.Linq; - using Interactables.Interobjects; - using Mirror; - using NorthwoodLib.Pools; - using UnityEngine; - /// - /// Handles the storing of a prefab. + /// Gets the first prefab found of the specified type. /// - /// The prefab to use. - /// For Doors use . - public static class PrefabStore - where TPrefab : NetworkBehaviour + public static TPrefab Prefab { - /// - /// Gets the first prefab found of the specified type. - /// - public static TPrefab Prefab + get { - get - { - if (field) - return field; + if (field) + return field; - if (typeof(TPrefab) == typeof(ReferenceHub)) - return field = NetworkManager.singleton.playerPrefab.GetComponent(); + if (typeof(TPrefab) == typeof(ReferenceHub)) + return field = NetworkManager.singleton.playerPrefab.GetComponent(); - return field = AllComponentPrefabs.FirstOrDefault()!; - } + return field = AllComponentPrefabs.FirstOrDefault()!; } + } - /// - /// Gets every single prefab associated with this component. - /// - /// Used to find all of a base type (such as ). - public static TPrefab[] AllComponentPrefabs + /// + /// Gets every single prefab associated with this component. + /// + /// Used to find all of a base type (such as ). + public static TPrefab[] AllComponentPrefabs + { + get { - get - { - if (field != null) - return field; + if (field != null) + return field; - List allPrefabs = ListPool.Shared.Rent(); + List allPrefabs = ListPool.Shared.Rent(); - foreach (GameObject gameObject in NetworkClient.prefabs.Values) - { - if (gameObject.TryGetComponent(out TPrefab prefab)) - allPrefabs.Add(prefab); - } + foreach (GameObject gameObject in NetworkClient.prefabs.Values) + { + if (gameObject.TryGetComponent(out TPrefab prefab)) + allPrefabs.Add(prefab); + } - field = allPrefabs.ToArray(); - ListPool.Shared.Return(allPrefabs); + field = allPrefabs.ToArray(); + ListPool.Shared.Return(allPrefabs); - return field; - } + return field; } } } \ No newline at end of file diff --git a/SecretAPI/Features/Registry.cs b/SecretAPI/Features/Registry.cs index ea3b88d..2aae151 100644 --- a/SecretAPI/Features/Registry.cs +++ b/SecretAPI/Features/Registry.cs @@ -1,22 +1,21 @@ -namespace SecretAPI.Features -{ - using System.Collections.Generic; +namespace SecretAPI.Features; + +using System.Collections.Generic; +/// +/// Handles the registration of objects and ids. +/// +/// The type of the registry. +public static class Registry +{ /// - /// Handles the registration of objects and ids. + /// Gets the registered objects. /// - /// The type of the registry. - public static class Registry - { - /// - /// Gets the registered objects. - /// - public static List Registered { get; } = []; + public static List Registered { get; } = []; - /// - /// Gets or sets the current id iteration. - /// - /// You should increment this with ++CurrentId when using it. - public static int CurrentId { get; set; } - } + /// + /// Gets or sets the current id iteration. + /// + /// You should increment this with ++CurrentId when using it. + public static int CurrentId { get; set; } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomButtonSetting.cs b/SecretAPI/Features/UserSettings/CustomButtonSetting.cs index 7ef676c..dfff551 100644 --- a/SecretAPI/Features/UserSettings/CustomButtonSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomButtonSetting.cs @@ -1,73 +1,72 @@ -namespace SecretAPI.Features.UserSettings -{ - using System; - using global::UserSettings.ServerSpecific; +namespace SecretAPI.Features.UserSettings; + +using System; +using global::UserSettings.ServerSpecific; +/// +/// Wraps . +/// +public abstract class CustomButtonSetting : CustomSetting, ISetting +{ /// - /// Wraps . + /// Initializes a new instance of the class. /// - public abstract class CustomButtonSetting : CustomSetting, ISetting + /// The button base. + protected CustomButtonSetting(SSButton button) + : base(button) { - /// - /// Initializes a new instance of the class. - /// - /// The button base. - protected CustomButtonSetting(SSButton button) - : base(button) - { - Base = button; - } + Base = button; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the button. - /// The setting's label. - /// The button text. - /// The time to hold. - /// The hint to show. - protected CustomButtonSetting(int? id, string label, string buttonText, float? holdTimeSeconds = null, string? hint = null) - : this(new SSButton(id, label, buttonText, holdTimeSeconds, hint)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the button. + /// The setting's label. + /// The button text. + /// The time to hold. + /// The hint to show. + protected CustomButtonSetting(int? id, string label, string buttonText, float? holdTimeSeconds = null, string? hint = null) + : this(new SSButton(id, label, buttonText, holdTimeSeconds, hint)) + { + } - /// - public new SSButton Base { get; } + /// + public new SSButton Base { get; } - /// - /// Gets the of the last press. - /// - public TimeSpan LastPress => Base.SyncLastPress.Elapsed; + /// + /// Gets the of the last press. + /// + public TimeSpan LastPress => Base.SyncLastPress.Elapsed; - /// - /// Gets or sets the text of the button. - /// - public string Text + /// + /// Gets or sets the text of the button. + /// + public string Text + { + get => Base.ButtonText; + set { - get => Base.ButtonText; - set - { - Base.ButtonText = value; - SendButtonUpdate(); - } + Base.ButtonText = value; + SendButtonUpdate(); } + } - /// - /// Gets or sets the amount of time to hold the button in seconds. - /// - public float RequiredHoldTime + /// + /// Gets or sets the amount of time to hold the button in seconds. + /// + public float RequiredHoldTime + { + get => Base.HoldTimeSeconds; + set { - get => Base.HoldTimeSeconds; - set - { - Base.HoldTimeSeconds = value; - SendButtonUpdate(); - } + Base.HoldTimeSeconds = value; + SendButtonUpdate(); } - - /// - /// Sends an update to that or has updated. - /// - private void SendButtonUpdate() => Base.SendButtonUpdate(Text, RequiredHoldTime, false, IsKnownOwnerHub); } + + /// + /// Sends an update to that or has updated. + /// + private void SendButtonUpdate() => Base.SendButtonUpdate(Text, RequiredHoldTime, false, IsKnownOwnerHub); } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs b/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs index ec18e97..2bb966a 100644 --- a/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs @@ -1,116 +1,115 @@ -namespace SecretAPI.Features.UserSettings -{ - using System; - using global::UserSettings.ServerSpecific; +namespace SecretAPI.Features.UserSettings; + +using System; +using global::UserSettings.ServerSpecific; +/// +/// Custom wrapper. +/// +public abstract class CustomDropdownSetting : CustomSetting, ISetting +{ /// - /// Custom wrapper. + /// Initializes a new instance of the class. /// - public abstract class CustomDropdownSetting : CustomSetting, ISetting + /// The base setting to create the wrapper with. + protected CustomDropdownSetting(SSDropdownSetting setting) + : base(setting) { - /// - /// Initializes a new instance of the class. - /// - /// The base setting to create the wrapper with. - protected CustomDropdownSetting(SSDropdownSetting setting) - : base(setting) - { - Base = setting; - } + Base = setting; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the setting. - /// The setting's label. - /// The array of string options to give. - /// The default option (int index). - /// The entry type. - /// The hint to show. - /// The . - /// See . - protected CustomDropdownSetting( - int? id, - string label, - string[] options, - int defaultOptionIndex = 0, - SSDropdownSetting.DropdownEntryType entryType = SSDropdownSetting.DropdownEntryType.Regular, - string? hint = null, - byte collectionId = byte.MaxValue, - bool isServerSetting = false) - : this(new SSDropdownSetting(id, label, options, defaultOptionIndex, entryType, hint, collectionId, isServerSetting)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the setting. + /// The setting's label. + /// The array of string options to give. + /// The default option (int index). + /// The entry type. + /// The hint to show. + /// The . + /// See . + protected CustomDropdownSetting( + int? id, + string label, + string[] options, + int defaultOptionIndex = 0, + SSDropdownSetting.DropdownEntryType entryType = SSDropdownSetting.DropdownEntryType.Regular, + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSDropdownSetting(id, label, options, defaultOptionIndex, entryType, hint, collectionId, isServerSetting)) + { + } - /// - public new SSDropdownSetting Base { get; } + /// + public new SSDropdownSetting Base { get; } - /// - public override bool HasValueChanged => LastSelectedIndex != SyncedIndex; + /// + public override bool HasValueChanged => LastSelectedIndex != SyncedIndex; - /// - /// Gets the index the client is claiming to have selected. - /// - /// This is not validated, if you need it validated use . - public int SyncedIndex => Base.SyncSelectionIndexRaw; + /// + /// Gets the index the client is claiming to have selected. + /// + /// This is not validated, if you need it validated use . + public int SyncedIndex => Base.SyncSelectionIndexRaw; - /// - /// Gets the selected index after validation. - /// - public int ValidatedSelectedIndex => Math.Clamp(SyncedIndex, 0, Options.Length - 1); + /// + /// Gets the selected index after validation. + /// + public int ValidatedSelectedIndex => Math.Clamp(SyncedIndex, 0, Options.Length - 1); - /// - /// Gets or sets the options. - /// - public string[] Options + /// + /// Gets or sets the options. + /// + public string[] Options + { + get => Base.Options; + set { - get => Base.Options; - set - { - Base.Options = value; - SendDropdownUpdate(); - } + Base.Options = value; + SendDropdownUpdate(); } + } - /// - /// Gets the selected option as string. - /// - public string SelectedOption => Options[ValidatedSelectedIndex]; - - /// - /// Gets the . - /// - /// Refer to https://github.com/HubertMoszka/Server-Specific-Settings-System/blob/main/SSDropdownSetting.cs#L151 for proper documentation. - public SSDropdownSetting.DropdownEntryType EntryType => Base.EntryType; - - /// - /// Gets the last selected index, or -1 if none was selected previously. - /// - public int LastSelectedIndex { get; private set; } = -1; - - /// - /// Gets the selected option prior to the most recent call as a string, or null if none was selected previously. - /// - public string? LastSelectedOption => LastSelectedIndex < 0 || LastSelectedIndex >= Options.Length ? null : Options[LastSelectedIndex]; - - /// - /// Sends an update to that this has been updated on Server. Only works if is true. - /// - /// The new ID selected. - public void SendServerUpdate(int selectionId) => Base.SendValueUpdate(selectionId, false, IsKnownOwnerHub); - - /// - protected internal override void HandleBeforeSettingUpdate() - { - base.HandleBeforeSettingUpdate(); + /// + /// Gets the selected option as string. + /// + public string SelectedOption => Options[ValidatedSelectedIndex]; - if (LastUpdateType != SettingResponseType.Initial) - LastSelectedIndex = SyncedIndex; - } + /// + /// Gets the . + /// + /// Refer to https://github.com/HubertMoszka/Server-Specific-Settings-System/blob/main/SSDropdownSetting.cs#L151 for proper documentation. + public SSDropdownSetting.DropdownEntryType EntryType => Base.EntryType; + + /// + /// Gets the last selected index, or -1 if none was selected previously. + /// + public int LastSelectedIndex { get; private set; } = -1; + + /// + /// Gets the selected option prior to the most recent call as a string, or null if none was selected previously. + /// + public string? LastSelectedOption => LastSelectedIndex < 0 || LastSelectedIndex >= Options.Length ? null : Options[LastSelectedIndex]; + + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// The new ID selected. + public void SendServerUpdate(int selectionId) => Base.SendValueUpdate(selectionId, false, IsKnownOwnerHub); - /// - /// Sends an update to that has been updated. - /// - private void SendDropdownUpdate() => Base.SendDropdownUpdate(Options, false, IsKnownOwnerHub); + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + + if (LastUpdateType != SettingResponseType.Initial) + LastSelectedIndex = SyncedIndex; } + + /// + /// Sends an update to that has been updated. + /// + private void SendDropdownUpdate() => Base.SendDropdownUpdate(Options, false, IsKnownOwnerHub); } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomHeader.cs b/SecretAPI/Features/UserSettings/CustomHeader.cs index 8822806..d906ea8 100644 --- a/SecretAPI/Features/UserSettings/CustomHeader.cs +++ b/SecretAPI/Features/UserSettings/CustomHeader.cs @@ -1,30 +1,28 @@ -namespace SecretAPI.Features.UserSettings -{ - using System; - using global::UserSettings.ServerSpecific; +namespace SecretAPI.Features.UserSettings; + +using global::UserSettings.ServerSpecific; +/// +/// Wraps . +/// +public class CustomHeader : ISetting +{ /// - /// Wraps . + /// Initializes a new instance of the class. /// - public class CustomHeader : ISetting + /// The label to show. + /// Reduced padding. + /// Hint displayed. + public CustomHeader(string label, bool reducedPadding = false, string? hint = null) { - /// - /// Initializes a new instance of the class. - /// - /// The label to show. - /// Reduced padding. - /// Hint displayed. - public CustomHeader(string label, bool reducedPadding = false, string? hint = null) - { - Base = new SSGroupHeader(label, reducedPadding, hint); - } + Base = new SSGroupHeader(label, reducedPadding, hint); + } - /// - /// Gets a for Example purposes. - /// - public static CustomHeader Examples { get; } = new("Examples", hint: "Features used as examples"); + /// + /// Gets a for Example purposes. + /// + public static CustomHeader Examples { get; } = new("Examples", hint: "Features used as examples"); - /// - public SSGroupHeader Base { get; } - } + /// + public SSGroupHeader Base { get; } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs b/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs index 32ae2aa..27a7ced 100644 --- a/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomKeybindSetting.cs @@ -1,51 +1,50 @@ -namespace SecretAPI.Features.UserSettings -{ - using global::UserSettings.ServerSpecific; - using UnityEngine; +namespace SecretAPI.Features.UserSettings; + +using global::UserSettings.ServerSpecific; +using UnityEngine; +/// +/// Wrapper for . +/// +public abstract class CustomKeybindSetting : CustomSetting, ISetting +{ /// - /// Wrapper for . + /// Initializes a new instance of the class. /// - public abstract class CustomKeybindSetting : CustomSetting, ISetting + /// The setting to wrap. + protected CustomKeybindSetting(SSKeybindSetting setting) + : base(setting) { - /// - /// Initializes a new instance of the class. - /// - /// The setting to wrap. - protected CustomKeybindSetting(SSKeybindSetting setting) - : base(setting) - { - Base = setting; - } + Base = setting; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the setting. - /// The setting's label. - /// The suggested key. - /// Whether to prevent interaction in a GUI. - /// Whether to allow spectators to trigger. - /// The hint to show. - /// The . - protected CustomKeybindSetting( - int? id, - string label, - KeyCode suggestedKey = KeyCode.None, - bool preventInteractionOnGui = true, - bool allowSpectatorTrigger = true, - string? hint = null, - byte collectionId = byte.MaxValue) - : this(new SSKeybindSetting(id, label, suggestedKey, preventInteractionOnGui, allowSpectatorTrigger, hint, collectionId)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the setting. + /// The setting's label. + /// The suggested key. + /// Whether to prevent interaction in a GUI. + /// Whether to allow spectators to trigger. + /// The hint to show. + /// The . + protected CustomKeybindSetting( + int? id, + string label, + KeyCode suggestedKey = KeyCode.None, + bool preventInteractionOnGui = true, + bool allowSpectatorTrigger = true, + string? hint = null, + byte collectionId = byte.MaxValue) + : this(new SSKeybindSetting(id, label, suggestedKey, preventInteractionOnGui, allowSpectatorTrigger, hint, collectionId)) + { + } - /// - public new SSKeybindSetting Base { get; } + /// + public new SSKeybindSetting Base { get; } - /// - /// Gets a value indicating whether the keybind is pressed. - /// - public bool IsPressed => Base.SyncIsPressed; - } + /// + /// Gets a value indicating whether the keybind is pressed. + /// + public bool IsPressed => Base.SyncIsPressed; } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs index 2c75305..3e10dbb 100644 --- a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs @@ -1,116 +1,115 @@ -namespace SecretAPI.Features.UserSettings -{ - using System; - using global::UserSettings.ServerSpecific; - using TMPro; +namespace SecretAPI.Features.UserSettings; + +using System; +using global::UserSettings.ServerSpecific; +using TMPro; +/// +/// Wrapper for . +/// +public abstract class CustomPlainTextSetting : CustomSetting, ISetting +{ /// - /// Wrapper for . + /// Initializes a new instance of the class. /// - public abstract class CustomPlainTextSetting : CustomSetting, ISetting + /// The setting to create wrapper from. + protected CustomPlainTextSetting(SSPlaintextSetting setting) + : base(setting) { - /// - /// Initializes a new instance of the class. - /// - /// The setting to create wrapper from. - protected CustomPlainTextSetting(SSPlaintextSetting setting) - : base(setting) - { - Base = setting; - } + Base = setting; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the setting. - /// The setting's label. - /// The placeholder to use for the setting. - /// The max allowed characters. - /// The content type. - /// The hint to display for the setting. - /// The . - /// See . - protected CustomPlainTextSetting( - int? id, - string label, - string placeholder = "...", - int characterLimit = 64, - TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, - string? hint = null, - byte collectionId = byte.MaxValue, - bool isServerSetting = false) - : this(new SSPlaintextSetting(id, label, placeholder, characterLimit, contentType, hint, collectionId, isServerSetting)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the setting. + /// The setting's label. + /// The placeholder to use for the setting. + /// The max allowed characters. + /// The content type. + /// The hint to display for the setting. + /// The . + /// See . + protected CustomPlainTextSetting( + int? id, + string label, + string placeholder = "...", + int characterLimit = 64, + TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSPlaintextSetting(id, label, placeholder, characterLimit, contentType, hint, collectionId, isServerSetting)) + { + } - /// - public new SSPlaintextSetting Base { get; } + /// + public new SSPlaintextSetting Base { get; } - /// - /// Gets the input text prior to the most recent call. - /// - public string LastInputText { get; private set; } = string.Empty; + /// + /// Gets the input text prior to the most recent call. + /// + public string LastInputText { get; private set; } = string.Empty; - /// - /// Gets the synced input text. - /// - public string InputText => Base.SyncInputText; + /// + /// Gets the synced input text. + /// + public string InputText => Base.SyncInputText; - /// - /// Gets or sets the content type. - /// - public TMP_InputField.ContentType ContentType + /// + /// Gets or sets the content type. + /// + public TMP_InputField.ContentType ContentType + { + get => Base.ContentType; + set { - get => Base.ContentType; - set - { - Base.ContentType = value; - SendPlaintextUpdate(); - } + Base.ContentType = value; + SendPlaintextUpdate(); } + } - /// - /// Gets or sets the placeholder. - /// - public string Placeholder + /// + /// Gets or sets the placeholder. + /// + public string Placeholder + { + get => Base.Placeholder; + set { - get => Base.Placeholder; - set - { - Base.Placeholder = value; - SendPlaintextUpdate(); - } + Base.Placeholder = value; + SendPlaintextUpdate(); } + } - /// - /// Gets or sets the character limit. - /// - public int CharacterLimit + /// + /// Gets or sets the character limit. + /// + public int CharacterLimit + { + get => Base.CharacterLimit; + set { - get => Base.CharacterLimit; - set - { - Base.CharacterLimit = value; - SendPlaintextUpdate(); - } + Base.CharacterLimit = value; + SendPlaintextUpdate(); } + } - /// - /// Sends an update to that this has been updated on Server. Only works if is true. - /// - /// The new text. - public void SendServerUpdate(string text) => Base.SendValueUpdate(text, false, IsKnownOwnerHub); - - /// - protected internal override void HandleBeforeSettingUpdate() - { - base.HandleBeforeSettingUpdate(); - LastInputText = InputText; - } + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// The new text. + public void SendServerUpdate(string text) => Base.SendValueUpdate(text, false, IsKnownOwnerHub); - /// - /// Sends an update to the that or has changed values. - /// - private void SendPlaintextUpdate() => Base.SendPlaintextUpdate(Placeholder, (ushort)Math.Clamp(CharacterLimit, ushort.MinValue, ushort.MaxValue), ContentType, false, IsKnownOwnerHub); + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + LastInputText = InputText; } + + /// + /// Sends an update to the that or has changed values. + /// + private void SendPlaintextUpdate() => Base.SendPlaintextUpdate(Placeholder, (ushort)Math.Clamp(CharacterLimit, ushort.MinValue, ushort.MaxValue), ContentType, false, IsKnownOwnerHub); } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomSetting.cs b/SecretAPI/Features/UserSettings/CustomSetting.cs index 1190e7d..686d803 100644 --- a/SecretAPI/Features/UserSettings/CustomSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSetting.cs @@ -1,379 +1,378 @@ -namespace SecretAPI.Features.UserSettings +namespace SecretAPI.Features.UserSettings; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using global::UserSettings.ServerSpecific; +using LabApi.Events.Handlers; +using LabApi.Features.Enums; +using LabApi.Features.Wrappers; +using Mirror; +using NorthwoodLib.Pools; +using SecretAPI.Extensions; + +/// +/// Wraps . +/// +public abstract class CustomSetting : ISetting { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using global::UserSettings.ServerSpecific; - using LabApi.Events.Handlers; - using LabApi.Features.Enums; - using LabApi.Features.Wrappers; - using Mirror; - using NorthwoodLib.Pools; - using SecretAPI.Extensions; - - /// - /// Wraps . - /// - public abstract class CustomSetting : ISetting + private static readonly Dictionary> ReceivedPlayerSettings = []; + + static CustomSetting() { - private static readonly Dictionary> ReceivedPlayerSettings = []; + SecretApi.Harmony.PatchCategory(nameof(CustomSetting), SecretApi.Assembly); - static CustomSetting() - { - SecretApi.Harmony.PatchCategory(nameof(CustomSetting), SecretApi.Assembly); + ServerSpecificSettingsSync.SendOnJoinFilter = null; + ServerSpecificSettingsSync.DefinedSettings ??= []; // fix null ref + ServerSpecificSettingsSync.ServerOnSettingValueReceived += OnSettingsUpdated; - ServerSpecificSettingsSync.SendOnJoinFilter = null; - ServerSpecificSettingsSync.DefinedSettings ??= []; // fix null ref - ServerSpecificSettingsSync.ServerOnSettingValueReceived += OnSettingsUpdated; + PlayerEvents.Joined += ev => SendSettingsToPlayer(ev.Player); + PlayerEvents.GroupChanged += ev => SendSettingsToPlayer(ev.Player); + PlayerEvents.Left += ev => RemoveStoredPlayer(ev.Player); + } - PlayerEvents.Joined += ev => SendSettingsToPlayer(ev.Player); - PlayerEvents.GroupChanged += ev => SendSettingsToPlayer(ev.Player); - PlayerEvents.Left += ev => RemoveStoredPlayer(ev.Player); - } + /// + /// Initializes a new instance of the class. + /// + /// The setting to use for custom setting. + protected CustomSetting(ServerSpecificSettingBase setting) + { + Base = setting; + } - /// - /// Initializes a new instance of the class. - /// - /// The setting to use for custom setting. - protected CustomSetting(ServerSpecificSettingBase setting) - { - Base = setting; - } + /// + /// Gets the registered custom settings. + /// + public static List CustomSettings { get; } = []; - /// - /// Gets the registered custom settings. - /// - public static List CustomSettings { get; } = []; - - /// - /// Gets a dictionary of player to their received custom settings. - /// - public static IReadOnlyDictionary> PlayerSettings => ReceivedPlayerSettings; - - /// - public ServerSpecificSettingBase Base { get; } - - /// - /// Gets the known owner. - /// - /// This is null on the original object. - public Player? KnownOwner { get; private set; } - - /// - /// Gets the of the setting. - /// - public abstract CustomHeader Header { get; } - - /// - /// Gets an enum indicating the type of the last update. - /// - /// When used inside of it will indicate the current status. - public SettingResponseType LastUpdateType { get; private set; } = SettingResponseType.None; - - /// - /// Gets a value indicating whether the current value received is different to that prior to the most recent call. - /// - public virtual bool HasValueChanged { get; } - - /// - /// Gets or sets a value indicating whether the setting is server side. - /// - /// This will result in client not saving the setting values and allows the server to change the setting . - public bool IsServerSetting - { - get => Base.IsServerOnly; - set => Base.IsServerOnly = value; - } + /// + /// Gets a dictionary of player to their received custom settings. + /// + public static IReadOnlyDictionary> PlayerSettings => ReceivedPlayerSettings; - /// - /// Gets a value indicating whether the setting is the default and not tied to a . - /// - public bool IsDefaultSetting => KnownOwner == null; + /// + public ServerSpecificSettingBase Base { get; } - /// - /// Gets or sets the current label. - /// - public string Label - { - get => Base.Label; - set - { - Base.Label = value; - SendSettingUpdate(); - } - } + /// + /// Gets the known owner. + /// + /// This is null on the original object. + public Player? KnownOwner { get; private set; } - /// - /// Gets or sets the description hint to show. - /// - public string DescriptionHint - { - get => Base.HintDescription; - set - { - Base.HintDescription = value; - SendSettingUpdate(); - } - } + /// + /// Gets the of the setting. + /// + public abstract CustomHeader Header { get; } - /// - /// Gets the current id. - /// - public int Id - { - get => Base.SettingId; - init => Base.SettingId = value; - } + /// + /// Gets an enum indicating the type of the last update. + /// + /// When used inside of it will indicate the current status. + public SettingResponseType LastUpdateType { get; private set; } = SettingResponseType.None; - /// - /// Gets or sets the Collection ID for the setting. Defaults to . - /// - /// Setting value between 0-20 will allow sharing between servers on the same Account ID. - public byte CollectionId + /// + /// Gets a value indicating whether the current value received is different to that prior to the most recent call. + /// + public virtual bool HasValueChanged { get; } + + /// + /// Gets or sets a value indicating whether the setting is server side. + /// + /// This will result in client not saving the setting values and allows the server to change the setting . + public bool IsServerSetting + { + get => Base.IsServerOnly; + set => Base.IsServerOnly = value; + } + + /// + /// Gets a value indicating whether the setting is the default and not tied to a . + /// + public bool IsDefaultSetting => KnownOwner == null; + + /// + /// Gets or sets the current label. + /// + public string Label + { + get => Base.Label; + set { - get => Base.CollectionId; - set => Base.CollectionId = value; + Base.Label = value; + SendSettingUpdate(); } + } - /// - /// Gets or sets a value indicating whether the setting should be shared between servers. - /// - /// should be used if you are using different settings with the same ID and need extra control. - public bool IsShared + /// + /// Gets or sets the description hint to show. + /// + public string DescriptionHint + { + get => Base.HintDescription; + set { - get => CollectionId < ServerSpecificSettingBase.MaxCollections; - set => CollectionId = value ? byte.MinValue : byte.MaxValue; + Base.HintDescription = value; + SendSettingUpdate(); } + } - /// - /// Registers a collection of settings. - /// - /// The settings to register. - public static void Register(params CustomSetting[] settings) => CustomSettings.AddRange(settings); - - /// - /// Registers a collection of settings. - /// - /// The settings to register. - public static void Register(IEnumerable settings) => CustomSettings.AddRange(settings); - - /// - /// Unregisters collection of settings. - /// - /// The settings to unregister. - public static void UnRegister(params CustomSetting[] settings) => CustomSettings.RemoveAll(settings.Contains); - - /// - /// Unregisters a collection of settings. - /// - /// The settings to unregister. - public static void UnRegister(IEnumerable settings) => CustomSettings.RemoveAll(settings.Contains); - - /// - /// Tries to get player specific setting. - /// - /// The player to get settings of. - /// The setting found. - /// The setting type to find. - /// Whether setting was found. - public static bool TryGetPlayerSetting(Player player, [NotNullWhen(true)] out TSetting? setting) - where TSetting : CustomSetting - { - setting = null; + /// + /// Gets the current id. + /// + public int Id + { + get => Base.SettingId; + init => Base.SettingId = value; + } - if (!PlayerSettings.TryGetValue(player, out List? settings)) - return false; + /// + /// Gets or sets the Collection ID for the setting. Defaults to . + /// + /// Setting value between 0-20 will allow sharing between servers on the same Account ID. + public byte CollectionId + { + get => Base.CollectionId; + set => Base.CollectionId = value; + } - foreach (CustomSetting toCheck in settings) - { - if (toCheck is TSetting value) - { - setting = value; - return true; - } - } + /// + /// Gets or sets a value indicating whether the setting should be shared between servers. + /// + /// should be used if you are using different settings with the same ID and need extra control. + public bool IsShared + { + get => CollectionId < ServerSpecificSettingBase.MaxCollections; + set => CollectionId = value ? byte.MinValue : byte.MaxValue; + } - return false; - } + /// + /// Registers a collection of settings. + /// + /// The settings to register. + public static void Register(params CustomSetting[] settings) => CustomSettings.AddRange(settings); - /// - /// Gets a player's . - /// - /// The ID of the setting. - /// The player of which to get the setting from. - /// The setting class to check for. - /// The found matching the params, otherwise null. - public static T? GetPlayerSetting(int id, Player player) - where T : CustomSetting => PlayerSettings.TryGetValue(player, out List settings) - ? settings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T - : null; - - /// - /// Gets a , used for validation. - /// - /// The type of the base setting. - /// The ID of the setting. - /// The found matching the params, otherwise null. - public static CustomSetting? Get(Type type, int id) - => CustomSettings.FirstOrDefault(s => s.Base.SettingId == id && s.Base.GetType() == type); - - /// - /// Gets a , used for validation. - /// - /// The ID of the setting. - /// The setting class to check for. - /// The found matching the params, otherwise null. - public static T? Get(int id) - where T : CustomSetting => CustomSettings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T; - - /// - /// Resyncs all settings to all players. - /// - /// The version of the setting. If null will use . - public static void ResyncServer(int? version = null) - { - // only update to real players that are authenticated - foreach (Player player in Player.GetAll(PlayerSearchFlags.AuthenticatedPlayers)) - SendSettingsToPlayer(player, version); - } + /// + /// Registers a collection of settings. + /// + /// The settings to register. + public static void Register(IEnumerable settings) => CustomSettings.AddRange(settings); - /// - /// Updates the settings of a player based on . - /// - /// The player to update. - /// The version of the setting. If null will use . - /// This will be automatically called on and . - public static void SendSettingsToPlayer(Player player, int? version = null) - { - if (player.IsHost || player.IsNpc) - return; + /// + /// Unregisters collection of settings. + /// + /// The settings to unregister. + public static void UnRegister(params CustomSetting[] settings) => CustomSettings.RemoveAll(settings.Contains); - List playerSettings = ListPool.Shared.Rent(); - foreach (CustomSetting setting in CustomSettings) - { - if (!setting.CanView(player)) - continue; + /// + /// Unregisters a collection of settings. + /// + /// The settings to unregister. + public static void UnRegister(IEnumerable settings) => CustomSettings.RemoveAll(settings.Contains); - CustomSetting playerSpecific = EnsurePlayerSpecificSetting(player, setting); - playerSpecific.PersonalizeSetting(); - playerSettings.Add(playerSpecific); - } + /// + /// Tries to get player specific setting. + /// + /// The player to get settings of. + /// The setting found. + /// The setting type to find. + /// Whether setting was found. + public static bool TryGetPlayerSetting(Player player, [NotNullWhen(true)] out TSetting? setting) + where TSetting : CustomSetting + { + setting = null; - List ordered = ListPool.Shared.Rent(); - foreach (IGrouping grouping in playerSettings.GroupBy(static setting => setting.Header)) + if (!PlayerSettings.TryGetValue(player, out List? settings)) + return false; + + foreach (CustomSetting toCheck in settings) + { + if (toCheck is TSetting value) { - ordered.Add(grouping.Key.Base); - ordered.AddRange(grouping.Select(static setting => setting.Base)); + setting = value; + return true; } + } - if (ServerSpecificSettingsSync.DefinedSettings != null) - ordered.AddRange(ServerSpecificSettingsSync.DefinedSettings); + return false; + } - ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, ordered.ToArray(), version); + /// + /// Gets a player's . + /// + /// The ID of the setting. + /// The player of which to get the setting from. + /// The setting class to check for. + /// The found matching the params, otherwise null. + public static T? GetPlayerSetting(int id, Player player) + where T : CustomSetting => PlayerSettings.TryGetValue(player, out List settings) + ? settings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T + : null; - ListPool.Shared.Return(playerSettings); - ListPool.Shared.Return(ordered); - } + /// + /// Gets a , used for validation. + /// + /// The type of the base setting. + /// The ID of the setting. + /// The found matching the params, otherwise null. + public static CustomSetting? Get(Type type, int id) + => CustomSettings.FirstOrDefault(s => s.Base.SettingId == id && s.Base.GetType() == type); - /// - /// Checks whether a is equal to . - /// - /// The to check. - /// Whether is equal to Owner . - internal bool IsKnownOwnerHub(ReferenceHub? hub) => hub && KnownOwner?.ReferenceHub == hub; - - /// - /// Called before , adding and . - /// - /// This will not have the current status. - protected internal virtual void HandleBeforeSettingUpdate() - { - LastUpdateType = LastUpdateType == SettingResponseType.None - ? SettingResponseType.Initial - : SettingResponseType.Update; - } + /// + /// Gets a , used for validation. + /// + /// The ID of the setting. + /// The setting class to check for. + /// The found matching the params, otherwise null. + public static T? Get(int id) + where T : CustomSetting => CustomSettings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T; + + /// + /// Resyncs all settings to all players. + /// + /// The version of the setting. If null will use . + public static void ResyncServer(int? version = null) + { + // only update to real players that are authenticated + foreach (Player player in Player.GetAll(PlayerSearchFlags.AuthenticatedPlayers)) + SendSettingsToPlayer(player, version); + } - /// - /// Resyncs the setting to its owner. - /// - protected void ResyncToOwner() + /// + /// Updates the settings of a player based on . + /// + /// The player to update. + /// The version of the setting. If null will use . + /// This will be automatically called on and . + public static void SendSettingsToPlayer(Player player, int? version = null) + { + if (player.IsHost || player.IsNpc) + return; + + List playerSettings = ListPool.Shared.Rent(); + foreach (CustomSetting setting in CustomSettings) { - if (KnownOwner == null) - return; + if (!setting.CanView(player)) + continue; - SendSettingsToPlayer(KnownOwner); + CustomSetting playerSpecific = EnsurePlayerSpecificSetting(player, setting); + playerSpecific.PersonalizeSetting(); + playerSettings.Add(playerSpecific); } - /// - /// Checks if a player is able to view a setting. - /// - /// The player to check. - /// A value indicating whether a player is able to view the setting. - protected virtual bool CanView(Player player) => true; - - /// - /// Creates a duplicate of the current setting. Used to properly per player sync and implement . - /// - /// The duplicate setting created. - protected abstract CustomSetting CreateDuplicate(); - - /// - /// Called before setting is sent to a player. - /// - /// This should be used to personalize a setting per player, such as supporters having more options. - protected virtual void PersonalizeSetting() + List ordered = ListPool.Shared.Rent(); + foreach (IGrouping grouping in playerSettings.GroupBy(static setting => setting.Header)) { + ordered.Add(grouping.Key.Base); + ordered.AddRange(grouping.Select(static setting => setting.Base)); } - /// - /// Called when client sends a new value on the setting. - /// - /// You can use to get the current update type. - protected abstract void HandleSettingUpdate(); + if (ServerSpecificSettingsSync.DefinedSettings != null) + ordered.AddRange(ServerSpecificSettingsSync.DefinedSettings); - private static void RemoveStoredPlayer(Player player) => ReceivedPlayerSettings.Remove(player); + ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, ordered.ToArray(), version); - private static void OnSettingsUpdated(ReferenceHub hub, ServerSpecificSettingBase settingBase) - { - if (hub.IsHost) - return; + ListPool.Shared.Return(playerSettings); + ListPool.Shared.Return(ordered); + } + + /// + /// Checks whether a is equal to . + /// + /// The to check. + /// Whether is equal to Owner . + internal bool IsKnownOwnerHub(ReferenceHub? hub) => hub && KnownOwner?.ReferenceHub == hub; - Player player = Player.Get(hub); + /// + /// Called before , adding and . + /// + /// This will not have the current status. + protected internal virtual void HandleBeforeSettingUpdate() + { + LastUpdateType = LastUpdateType == SettingResponseType.None + ? SettingResponseType.Initial + : SettingResponseType.Update; + } - CustomSetting? setting = CustomSettings.FirstOrDefault(s => s.Base.SettingId == settingBase.SettingId); - if (setting == null || !setting.CanView(player)) - return; + /// + /// Resyncs the setting to its owner. + /// + protected void ResyncToOwner() + { + if (KnownOwner == null) + return; - // validate setting existence and then write data from client - CustomSetting newSettingPlayer = EnsurePlayerSpecificSetting(player, setting); - newSettingPlayer.HandleBeforeSettingUpdate(); + SendSettingsToPlayer(KnownOwner); + } - NetworkWriterPooled valueWriter = NetworkWriterPool.Get(); - settingBase.SerializeValue(valueWriter); - newSettingPlayer.Base.DeserializeValue(new NetworkReader(valueWriter.buffer)); - NetworkWriterPool.Return(valueWriter); + /// + /// Checks if a player is able to view a setting. + /// + /// The player to check. + /// A value indicating whether a player is able to view the setting. + protected virtual bool CanView(Player player) => true; - newSettingPlayer.HandleSettingUpdate(); - } + /// + /// Creates a duplicate of the current setting. Used to properly per player sync and implement . + /// + /// The duplicate setting created. + protected abstract CustomSetting CreateDuplicate(); - private static CustomSetting EnsurePlayerSpecificSetting(Player player, CustomSetting toMatch) - { - List settings = ReceivedPlayerSettings.GetOrAdd(player, static () => []); - CustomSetting? currentSetting = settings.FirstOrDefault(s => s.Id == toMatch.Id); - if (currentSetting == null) - { - currentSetting = toMatch.CreateDuplicate(); - currentSetting.KnownOwner = player; - settings.Add(currentSetting); - } + /// + /// Called before setting is sent to a player. + /// + /// This should be used to personalize a setting per player, such as supporters having more options. + protected virtual void PersonalizeSetting() + { + } + + /// + /// Called when client sends a new value on the setting. + /// + /// You can use to get the current update type. + protected abstract void HandleSettingUpdate(); + + private static void RemoveStoredPlayer(Player player) => ReceivedPlayerSettings.Remove(player); + + private static void OnSettingsUpdated(ReferenceHub hub, ServerSpecificSettingBase settingBase) + { + if (hub.IsHost) + return; + + Player player = Player.Get(hub); + + CustomSetting? setting = CustomSettings.FirstOrDefault(s => s.Base.SettingId == settingBase.SettingId); + if (setting == null || !setting.CanView(player)) + return; - return currentSetting; + // validate setting existence and then write data from client + CustomSetting newSettingPlayer = EnsurePlayerSpecificSetting(player, setting); + newSettingPlayer.HandleBeforeSettingUpdate(); + + NetworkWriterPooled valueWriter = NetworkWriterPool.Get(); + settingBase.SerializeValue(valueWriter); + newSettingPlayer.Base.DeserializeValue(new NetworkReader(valueWriter.buffer)); + NetworkWriterPool.Return(valueWriter); + + newSettingPlayer.HandleSettingUpdate(); + } + + private static CustomSetting EnsurePlayerSpecificSetting(Player player, CustomSetting toMatch) + { + List settings = ReceivedPlayerSettings.GetOrAdd(player, static () => []); + CustomSetting? currentSetting = settings.FirstOrDefault(s => s.Id == toMatch.Id); + if (currentSetting == null) + { + currentSetting = toMatch.CreateDuplicate(); + currentSetting.KnownOwner = player; + settings.Add(currentSetting); } - /// - /// Sends an update to that or has changed. - /// - private void SendSettingUpdate() => Base.SendUpdate(Label, DescriptionHint, false, IsKnownOwnerHub); + return currentSetting; } -} + + /// + /// Sends an update to that or has changed. + /// + private void SendSettingUpdate() => Base.SendUpdate(Label, DescriptionHint, false, IsKnownOwnerHub); +} \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs index 1831934..3bb9cb5 100644 --- a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs @@ -1,169 +1,168 @@ -namespace SecretAPI.Features.UserSettings -{ - using global::UserSettings.ServerSpecific; - using UnityEngine; +namespace SecretAPI.Features.UserSettings; + +using global::UserSettings.ServerSpecific; +using UnityEngine; +/// +/// Wrapper for . +/// +public abstract class CustomSliderSetting : CustomSetting, ISetting +{ /// - /// Wrapper for . + /// Initializes a new instance of the class. /// - public abstract class CustomSliderSetting : CustomSetting, ISetting + /// The setting to wrap. + protected CustomSliderSetting(SSSliderSetting setting) + : base(setting) { - /// - /// Initializes a new instance of the class. - /// - /// The setting to wrap. - protected CustomSliderSetting(SSSliderSetting setting) - : base(setting) - { - Base = setting; - } + Base = setting; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the setting. - /// The setting's label. - /// The slider's minimum value. - /// The slider's maximum value. - /// The default value for the slider. - /// Whether it should be an integer (false for float). - /// Value to string format. - /// The final display format. - /// The hint to display. - /// The . - /// See . - protected CustomSliderSetting( - int? id, - string label, - float minValue, - float maxValue, - float defaultValue = 0.0f, - bool integer = false, - string valueToStringFormat = "0.##", - string finalDisplayFormat = "{0}", - string? hint = null, - byte collectionId = byte.MaxValue, - bool isServerSetting = false) - : this(new SSSliderSetting(id, label, minValue, maxValue, defaultValue, integer, valueToStringFormat, finalDisplayFormat, hint, collectionId, isServerSetting)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the setting. + /// The setting's label. + /// The slider's minimum value. + /// The slider's maximum value. + /// The default value for the slider. + /// Whether it should be an integer (false for float). + /// Value to string format. + /// The final display format. + /// The hint to display. + /// The . + /// See . + protected CustomSliderSetting( + int? id, + string label, + float minValue, + float maxValue, + float defaultValue = 0.0f, + bool integer = false, + string valueToStringFormat = "0.##", + string finalDisplayFormat = "{0}", + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSSliderSetting(id, label, minValue, maxValue, defaultValue, integer, valueToStringFormat, finalDisplayFormat, hint, collectionId, isServerSetting)) + { + } - /// - public new SSSliderSetting Base { get; } + /// + public new SSSliderSetting Base { get; } - /// - public override bool HasValueChanged => !Mathf.Approximately(LastSelectedValueFloat, SelectedValueFloat); + /// + public override bool HasValueChanged => !Mathf.Approximately(LastSelectedValueFloat, SelectedValueFloat); - /// - /// Gets the selected value prior to the most recent call as a float. - /// - public float LastSelectedValueFloat { get; private set; } + /// + /// Gets the selected value prior to the most recent call as a float. + /// + public float LastSelectedValueFloat { get; private set; } - /// - /// Gets the selected value prior to the most recent call as an int. - /// - public int LastSelectedValueInt => Mathf.RoundToInt(LastSelectedValueFloat); + /// + /// Gets the selected value prior to the most recent call as an int. + /// + public int LastSelectedValueInt => Mathf.RoundToInt(LastSelectedValueFloat); - /// - /// Gets the synced value selected as a float. - /// - public float SelectedValueFloat => Base.SyncFloatValue; + /// + /// Gets the synced value selected as a float. + /// + public float SelectedValueFloat => Base.SyncFloatValue; - /// - /// Gets the synced value selected as an integer. - /// - public int SelectedValueInt => Base.SyncIntValue; + /// + /// Gets the synced value selected as an integer. + /// + public int SelectedValueInt => Base.SyncIntValue; - /// - /// Gets or sets the value to string format. - /// - public string ValueToStringFormat + /// + /// Gets or sets the value to string format. + /// + public string ValueToStringFormat + { + get => Base.ValueToStringFormat; + set { - get => Base.ValueToStringFormat; - set - { - Base.ValueToStringFormat = value; - SendSliderUpdate(); - } + Base.ValueToStringFormat = value; + SendSliderUpdate(); } + } - /// - /// Gets or sets the final display format. - /// - public string FinalDisplayFormat + /// + /// Gets or sets the final display format. + /// + public string FinalDisplayFormat + { + get => Base.FinalDisplayFormat; + set { - get => Base.FinalDisplayFormat; - set - { - Base.FinalDisplayFormat = value; - SendSliderUpdate(); - } + Base.FinalDisplayFormat = value; + SendSliderUpdate(); } + } - /// - /// Gets or sets the minimum value of the setting. - /// - public float MinimumValue + /// + /// Gets or sets the minimum value of the setting. + /// + public float MinimumValue + { + get => Base.MinValue; + set { - get => Base.MinValue; - set - { - Base.MinValue = value; - SendSliderUpdate(); - } + Base.MinValue = value; + SendSliderUpdate(); } + } - /// - /// Gets or sets the maximum value of the setting. - /// - public float MaximumValue + /// + /// Gets or sets the maximum value of the setting. + /// + public float MaximumValue + { + get => Base.MaxValue; + set { - get => Base.MaxValue; - set - { - Base.MaxValue = value; - SendSliderUpdate(); - } + Base.MaxValue = value; + SendSliderUpdate(); } + } - /// - /// Gets or sets the default value of the setting. - /// - public float DefaultValue - { - get => Base.DefaultValue; - set => Base.DefaultValue = value; - } + /// + /// Gets or sets the default value of the setting. + /// + public float DefaultValue + { + get => Base.DefaultValue; + set => Base.DefaultValue = value; + } - /// - /// Gets or sets a value indicating whether to use integer. False will use float. - /// - public bool UseInteger + /// + /// Gets or sets a value indicating whether to use integer. False will use float. + /// + public bool UseInteger + { + get => Base.Integer; + set { - get => Base.Integer; - set - { - Base.Integer = value; - SendSliderUpdate(); - } + Base.Integer = value; + SendSliderUpdate(); } + } - /// - /// Sends an update to that this has been updated on Server. Only works if is true. - /// - /// The new value that this is set to. - public void SendServerUpdate(float value) => Base.SendValueUpdate(value, false, IsKnownOwnerHub); - - /// - protected internal override void HandleBeforeSettingUpdate() - { - base.HandleBeforeSettingUpdate(); - LastSelectedValueFloat = SelectedValueFloat; - } + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// The new value that this is set to. + public void SendServerUpdate(float value) => Base.SendValueUpdate(value, false, IsKnownOwnerHub); - /// - /// Sends an update that any of the slider values have been updated. - /// - private void SendSliderUpdate() => Base.SendSliderUpdate(MinimumValue, MaximumValue, UseInteger, ValueToStringFormat, FinalDisplayFormat, false, IsKnownOwnerHub); + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + LastSelectedValueFloat = SelectedValueFloat; } + + /// + /// Sends an update that any of the slider values have been updated. + /// + private void SendSliderUpdate() => Base.SendSliderUpdate(MinimumValue, MaximumValue, UseInteger, ValueToStringFormat, FinalDisplayFormat, false, IsKnownOwnerHub); } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs b/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs index e70c67c..220de78 100644 --- a/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomTextAreaSetting.cs @@ -1,61 +1,60 @@ -namespace SecretAPI.Features.UserSettings -{ - using global::UserSettings.ServerSpecific; - using TMPro; +namespace SecretAPI.Features.UserSettings; + +using global::UserSettings.ServerSpecific; +using TMPro; +/// +/// Wrapper for . +/// +public abstract class CustomTextAreaSetting : CustomSetting, ISetting +{ /// - /// Wrapper for . + /// Initializes a new instance of the class. /// - public abstract class CustomTextAreaSetting : CustomSetting, ISetting + /// The setting to wrap. + protected CustomTextAreaSetting(SSTextArea setting) + : base(setting) { - /// - /// Initializes a new instance of the class. - /// - /// The setting to wrap. - protected CustomTextAreaSetting(SSTextArea setting) - : base(setting) - { - Base = setting; - } + Base = setting; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the setting. - /// The content of the setting. - /// The foldout mode. - /// The collapsed text. - /// The align for the text. - protected CustomTextAreaSetting( - int? id, - string content, - SSTextArea.FoldoutMode foldoutMode = SSTextArea.FoldoutMode.NotCollapsable, - string? collapsedText = null, - TextAlignmentOptions textAlignment = TextAlignmentOptions.TopLeft) - : this(new SSTextArea(id, content, foldoutMode, collapsedText, textAlignment)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the setting. + /// The content of the setting. + /// The foldout mode. + /// The collapsed text. + /// The align for the text. + protected CustomTextAreaSetting( + int? id, + string content, + SSTextArea.FoldoutMode foldoutMode = SSTextArea.FoldoutMode.NotCollapsable, + string? collapsedText = null, + TextAlignmentOptions textAlignment = TextAlignmentOptions.TopLeft) + : this(new SSTextArea(id, content, foldoutMode, collapsedText, textAlignment)) + { + } - /// - public new SSTextArea Base { get; } + /// + public new SSTextArea Base { get; } - /// - /// Gets or sets the current content. This is equal to . - /// - public string Content - { - get => Label; - set => Label = value; - } + /// + /// Gets or sets the current content. This is equal to . + /// + public string Content + { + get => Label; + set => Label = value; + } - /// - /// Gets the foldout mode. - /// - public SSTextArea.FoldoutMode Foldout => Base.Foldout; + /// + /// Gets the foldout mode. + /// + public SSTextArea.FoldoutMode Foldout => Base.Foldout; - /// - /// Gets the of the setting. - /// - public TextAlignmentOptions AlignmentOptions => Base.AlignmentOptions; - } + /// + /// Gets the of the setting. + /// + public TextAlignmentOptions AlignmentOptions => Base.AlignmentOptions; } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs b/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs index d73cc61..d042ad2 100644 --- a/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs @@ -1,114 +1,113 @@ -namespace SecretAPI.Features.UserSettings -{ - using global::UserSettings.ServerSpecific; +namespace SecretAPI.Features.UserSettings; + +using global::UserSettings.ServerSpecific; +/// +/// Wrapper for . +/// +public abstract class CustomTwoButtonSetting : CustomSetting, ISetting +{ /// - /// Wrapper for . + /// Initializes a new instance of the class. /// - public abstract class CustomTwoButtonSetting : CustomSetting, ISetting + /// The setting to wrap. + protected CustomTwoButtonSetting(SSTwoButtonsSetting button) + : base(button) { - /// - /// Initializes a new instance of the class. - /// - /// The setting to wrap. - protected CustomTwoButtonSetting(SSTwoButtonsSetting button) - : base(button) - { - Base = button; - } + Base = button; + } - /// - /// Initializes a new instance of the class. - /// - /// The ID of the setting. - /// The setting's label. - /// The first option. - /// The second option. - /// Whether the second option should be default. Default: false. - /// The hint to show. - /// The . - /// See . - protected CustomTwoButtonSetting( - int? id, - string label, - string optionA, - string optionB, - bool defaultIsB = false, - string? hint = null, - byte collectionId = byte.MaxValue, - bool isServerSetting = false) - : this(new SSTwoButtonsSetting(id, label, optionA, optionB, defaultIsB, hint, collectionId, isServerSetting)) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The ID of the setting. + /// The setting's label. + /// The first option. + /// The second option. + /// Whether the second option should be default. Default: false. + /// The hint to show. + /// The . + /// See . + protected CustomTwoButtonSetting( + int? id, + string label, + string optionA, + string optionB, + bool defaultIsB = false, + string? hint = null, + byte collectionId = byte.MaxValue, + bool isServerSetting = false) + : this(new SSTwoButtonsSetting(id, label, optionA, optionB, defaultIsB, hint, collectionId, isServerSetting)) + { + } - /// - public new SSTwoButtonsSetting Base { get; } + /// + public new SSTwoButtonsSetting Base { get; } - /// - /// Gets or sets the current text for the first option. - /// - public string OptionA + /// + /// Gets or sets the current text for the first option. + /// + public string OptionA + { + get => Base.OptionA; + set { - get => Base.OptionA; - set - { - Base.OptionA = value; - SendOptionsUpdate(); - } + Base.OptionA = value; + SendOptionsUpdate(); } + } - /// - /// Gets or sets the current text for the second option. - /// - public string OptionB + /// + /// Gets or sets the current text for the second option. + /// + public string OptionB + { + get => Base.OptionB; + set { - get => Base.OptionB; - set - { - Base.OptionB = value; - SendOptionsUpdate(); - } + Base.OptionB = value; + SendOptionsUpdate(); } + } - /// - public override bool HasValueChanged => WasLastOptionB != IsOptionB; - - /// - /// Gets a value indicating whether the value prior to the most recent call was Option B. - /// - public bool WasLastOptionB { get; private set; } + /// + public override bool HasValueChanged => WasLastOptionB != IsOptionB; - /// - /// Gets a value indicating whether the selected option is currently the first. - /// - public bool IsOptionA => Base.SyncIsA; + /// + /// Gets a value indicating whether the value prior to the most recent call was Option B. + /// + public bool WasLastOptionB { get; private set; } - /// - /// Gets a value indicating whether the selected option is currently the second. - /// - public bool IsOptionB => Base.SyncIsB; + /// + /// Gets a value indicating whether the selected option is currently the first. + /// + public bool IsOptionA => Base.SyncIsA; - /// - /// Gets a value indicating whether the selected option is currently set to the default. - /// - public bool IsDefault => Base.DefaultIsB ? IsOptionB : IsOptionA; + /// + /// Gets a value indicating whether the selected option is currently the second. + /// + public bool IsOptionB => Base.SyncIsB; - /// - /// Sends an update to that this has been updated on Server. Only works if is true. - /// - /// Whether the setting is set to B value now. - public void SendServerUpdate(bool isB) => Base.SendValueUpdate(isB, false, IsKnownOwnerHub); + /// + /// Gets a value indicating whether the selected option is currently set to the default. + /// + public bool IsDefault => Base.DefaultIsB ? IsOptionB : IsOptionA; - /// - protected internal override void HandleBeforeSettingUpdate() - { - base.HandleBeforeSettingUpdate(); - WasLastOptionB = IsOptionB; - } + /// + /// Sends an update to that this has been updated on Server. Only works if is true. + /// + /// Whether the setting is set to B value now. + public void SendServerUpdate(bool isB) => Base.SendValueUpdate(isB, false, IsKnownOwnerHub); - /// - /// Sends an update to the that or has changed values. - /// - private void SendOptionsUpdate() => Base.SendTwoButtonUpdate(OptionA, OptionB, false, IsKnownOwnerHub); + /// + protected internal override void HandleBeforeSettingUpdate() + { + base.HandleBeforeSettingUpdate(); + WasLastOptionB = IsOptionB; } + + /// + /// Sends an update to the that or has changed values. + /// + private void SendOptionsUpdate() => Base.SendTwoButtonUpdate(OptionA, OptionB, false, IsKnownOwnerHub); } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/ISetting.cs b/SecretAPI/Features/UserSettings/ISetting.cs index 6c35959..383c7f6 100644 --- a/SecretAPI/Features/UserSettings/ISetting.cs +++ b/SecretAPI/Features/UserSettings/ISetting.cs @@ -1,17 +1,16 @@ -namespace SecretAPI.Features.UserSettings -{ - using global::UserSettings.ServerSpecific; +namespace SecretAPI.Features.UserSettings; + +using global::UserSettings.ServerSpecific; +/// +/// Interface for to handle the Base. +/// +/// The setting being wrapped. +public interface ISetting + where T : ServerSpecificSettingBase +{ /// - /// Interface for to handle the Base. + /// Gets the base of the setting. /// - /// The setting being wrapped. - public interface ISetting - where T : ServerSpecificSettingBase - { - /// - /// Gets the base of the setting. - /// - public T Base { get; } - } + public T Base { get; } } \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/SettingResponseType.cs b/SecretAPI/Features/UserSettings/SettingResponseType.cs index ea3f5db..9403cd9 100644 --- a/SecretAPI/Features/UserSettings/SettingResponseType.cs +++ b/SecretAPI/Features/UserSettings/SettingResponseType.cs @@ -1,23 +1,22 @@ -namespace SecretAPI.Features.UserSettings +namespace SecretAPI.Features.UserSettings; + +/// +/// The type of response. +/// +public enum SettingResponseType { /// - /// The type of response. + /// Indicates that no response has been recorded. /// - public enum SettingResponseType - { - /// - /// Indicates that no response has been recorded. - /// - None, + None, - /// - /// Indicates that this is the initial response. - /// - Initial, + /// + /// Indicates that this is the initial response. + /// + Initial, - /// - /// Indicates that this is an update, changing the value. - /// - Update, - } + /// + /// Indicates that this is an update, changing the value. + /// + Update, } \ No newline at end of file diff --git a/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs b/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs index d7b94cb..abbb6f7 100644 --- a/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs +++ b/SecretAPI/Patches/Features/SendSettingsPlayerSync.cs @@ -1,22 +1,21 @@ -namespace SecretAPI.Patches.Features -{ - using HarmonyLib; - using LabApi.Features.Wrappers; - using SecretAPI.Attributes; - using SecretAPI.Features.UserSettings; - using UserSettings.ServerSpecific; +namespace SecretAPI.Patches.Features; + +using HarmonyLib; +using LabApi.Features.Wrappers; +using SecretAPI.Attributes; +using SecretAPI.Features.UserSettings; +using UserSettings.ServerSpecific; - /// - /// Fixes to resync with . - /// - [HarmonyPatchCategory(nameof(CustomSetting))] - [HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.SendToPlayer), [typeof(ReferenceHub)])] - internal static class SendSettingsPlayerSync +/// +/// Fixes to resync with . +/// +[HarmonyPatchCategory(nameof(CustomSetting))] +[HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.SendToPlayer), [typeof(ReferenceHub)])] +internal static class SendSettingsPlayerSync +{ + private static bool Prefix(ReferenceHub hub) { - private static bool Prefix(ReferenceHub hub) - { - CustomSetting.SendSettingsToPlayer(Player.Get(hub)); - return false; - } + CustomSetting.SendSettingsToPlayer(Player.Get(hub)); + return false; } } \ No newline at end of file diff --git a/SecretAPI/Patches/Features/SendSettingsServerSync.cs b/SecretAPI/Patches/Features/SendSettingsServerSync.cs index e84d1ff..4f68402 100644 --- a/SecretAPI/Patches/Features/SendSettingsServerSync.cs +++ b/SecretAPI/Patches/Features/SendSettingsServerSync.cs @@ -1,21 +1,20 @@ -namespace SecretAPI.Patches.Features -{ - using HarmonyLib; - using SecretAPI.Attributes; - using SecretAPI.Features.UserSettings; - using UserSettings.ServerSpecific; +namespace SecretAPI.Patches.Features; + +using HarmonyLib; +using SecretAPI.Attributes; +using SecretAPI.Features.UserSettings; +using UserSettings.ServerSpecific; - /// - /// Fixes to resync with . - /// - [HarmonyPatchCategory(nameof(CustomSetting))] - [HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.SendToAll))] - internal static class SendSettingsServerSync +/// +/// Fixes to resync with . +/// +[HarmonyPatchCategory(nameof(CustomSetting))] +[HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.SendToAll))] +internal static class SendSettingsServerSync +{ + private static bool Prefix() { - private static bool Prefix() - { - CustomSetting.ResyncServer(); - return false; - } + CustomSetting.ResyncServer(); + return false; } } \ No newline at end of file diff --git a/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs b/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs index a1b1e74..d366fec 100644 --- a/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs +++ b/SecretAPI/Patches/Features/SettingsOriginalDefinitionFix.cs @@ -1,26 +1,25 @@ -namespace SecretAPI.Patches.Features -{ - using HarmonyLib; - using SecretAPI.Attributes; - using SecretAPI.Features.UserSettings; - using UserSettings.ServerSpecific; +namespace SecretAPI.Patches.Features; - /// - /// Fixes on custom settings. - /// - [HarmonyPatchCategory(nameof(CustomSetting))] - [HarmonyPatch(typeof(ServerSpecificSettingBase), nameof(ServerSpecificSettingBase.OriginalDefinition), MethodType.Getter)] - internal static class SettingsOriginalDefinitionFix - { +using HarmonyLib; +using SecretAPI.Attributes; +using SecretAPI.Features.UserSettings; +using UserSettings.ServerSpecific; + +/// +/// Fixes on custom settings. +/// +[HarmonyPatchCategory(nameof(CustomSetting))] +[HarmonyPatch(typeof(ServerSpecificSettingBase), nameof(ServerSpecificSettingBase.OriginalDefinition), MethodType.Getter)] +internal static class SettingsOriginalDefinitionFix +{ #pragma warning disable SA1313 - private static void Postfix(ServerSpecificSettingBase __instance, ref ServerSpecificSettingBase __result) + private static void Postfix(ServerSpecificSettingBase __instance, ref ServerSpecificSettingBase __result) #pragma warning restore SA1313 - { - // Prevent handling non SecretAPI settings. - if (__result != null) - return; + { + // Prevent handling non SecretAPI settings. + if (__result != null) + return; - __result = CustomSetting.Get(__instance.GetType(), __instance.SettingId)?.Base ?? null!; - } + __result = CustomSetting.Get(__instance.GetType(), __instance.SettingId)?.Base ?? null!; } } \ No newline at end of file diff --git a/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs b/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs index 39cf781..0b6c5f7 100644 --- a/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs +++ b/SecretAPI/Patches/Features/SettingsSyncValidateFix.cs @@ -1,26 +1,25 @@ -namespace SecretAPI.Patches.Features -{ - using HarmonyLib; - using SecretAPI.Attributes; - using SecretAPI.Features.UserSettings; - using UserSettings.ServerSpecific; +namespace SecretAPI.Patches.Features; - /// - /// Fixes validation for . - /// - [HarmonyPatchCategory(nameof(CustomSetting))] - [HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.ServerPrevalidateClientResponse))] - internal static class SettingsSyncValidateFix - { +using HarmonyLib; +using SecretAPI.Attributes; +using SecretAPI.Features.UserSettings; +using UserSettings.ServerSpecific; + +/// +/// Fixes validation for . +/// +[HarmonyPatchCategory(nameof(CustomSetting))] +[HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.ServerPrevalidateClientResponse))] +internal static class SettingsSyncValidateFix +{ #pragma warning disable SA1313 - private static void Postfix(SSSClientResponse msg, ref bool __result) + private static void Postfix(SSSClientResponse msg, ref bool __result) #pragma warning restore SA1313 - { - // prevent overriding already validated settings - if (__result) - return; + { + // prevent overriding already validated settings + if (__result) + return; - __result = CustomSetting.Get(msg.SettingType, msg.Id) != null; - } + __result = CustomSetting.Get(msg.SettingType, msg.Id) != null; } } \ No newline at end of file diff --git a/SecretAPI/SecretApi.cs b/SecretAPI/SecretApi.cs index f41521c..9033e9a 100644 --- a/SecretAPI/SecretApi.cs +++ b/SecretAPI/SecretApi.cs @@ -1,60 +1,59 @@ -namespace SecretAPI +namespace SecretAPI; + +using System; +using System.Reflection; +using HarmonyLib; +using LabApi.Features; +using LabApi.Loader.Features.Plugins; +using LabApi.Loader.Features.Plugins.Enums; +using SecretAPI.Attributes; + +/// +/// Main class handling loading API. +/// +public class SecretApi : Plugin { - using System; - using System.Reflection; - using HarmonyLib; - using LabApi.Features; - using LabApi.Loader.Features.Plugins; - using LabApi.Loader.Features.Plugins.Enums; - using SecretAPI.Attributes; + /// + public override string Name => "SecretAPI"; - /// - /// Main class handling loading API. - /// - public class SecretApi : Plugin - { - /// - public override string Name => "SecretAPI"; + /// + public override string Description => "API for SCP:SL"; - /// - public override string Description => "API for SCP:SL"; + /// + public override string Author => "@obvEve"; - /// - public override string Author => "@obvEve"; + /// + public override LoadPriority Priority => LoadPriority.Highest; - /// - public override LoadPriority Priority => LoadPriority.Highest; + /// + public override Version Version { get; } = Assembly.GetName().Version; - /// - public override Version Version { get; } = Assembly.GetName().Version; + /// + public override Version RequiredApiVersion => LabApiProperties.CurrentVersion; - /// - public override Version RequiredApiVersion => LabApiProperties.CurrentVersion; + /// + /// We use transparent here because this is an API and should not interfere by itself with game logic. + public override bool IsTransparent => true; - /// - /// We use transparent here because this is an API and should not interfere by itself with game logic. - public override bool IsTransparent => true; - - /// - /// Gets the harmony to use for the API. - /// - internal static Harmony Harmony { get; } = new("SecretAPI" + DateTime.Now); + /// + /// Gets the harmony to use for the API. + /// + internal static Harmony Harmony { get; } = new("SecretAPI" + DateTime.Now); - /// - /// Gets the Assembly of the API. - /// - internal static Assembly Assembly { get; } = typeof(SecretApi).Assembly; + /// + /// Gets the Assembly of the API. + /// + internal static Assembly Assembly { get; } = typeof(SecretApi).Assembly; - /// - public override void Enable() - { - CallOnLoadAttribute.Load(Assembly); - } + /// + public override void Enable() + { + CallOnLoadAttribute.Load(Assembly); + } - /// - public override void Disable() - { - Harmony.UnpatchAll(Harmony.Id); - } + /// + public override void Disable() + { + Harmony.UnpatchAll(Harmony.Id); } } \ No newline at end of file From ff35b6adfff99bda5163c6ac907f9ded3b60c9e6 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:58:09 +0200 Subject: [PATCH 15/37] CustomSetting:HasValueChanged -> Explicitly default to false --- SecretAPI/Features/UserSettings/CustomSetting.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecretAPI/Features/UserSettings/CustomSetting.cs b/SecretAPI/Features/UserSettings/CustomSetting.cs index 686d803..e6c86df 100644 --- a/SecretAPI/Features/UserSettings/CustomSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSetting.cs @@ -74,7 +74,7 @@ protected CustomSetting(ServerSpecificSettingBase setting) /// /// Gets a value indicating whether the current value received is different to that prior to the most recent call. /// - public virtual bool HasValueChanged { get; } + public virtual bool HasValueChanged { get; } = false; /// /// Gets or sets a value indicating whether the setting is server side. From 1e175cf0eb6934101d4a3d8c9bc760ab711def60 Mon Sep 17 00:00:00 2001 From: Evelyn <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:27:24 +0200 Subject: [PATCH 16/37] Improvements to RoomExtensions (#84) * TryGetTeleportLocation * RoomSafetyFailReason * RoomSafetyFailReason.KnownBad + public KnownUnsafeRooms * fix RoomSafetyFailReason.KnownBad being unused * Fix errors --- SecretAPI/Enums/RoomSafetyFailReason.cs | 43 +++++++++++++++ SecretAPI/Extensions/RoomExtensions.cs | 70 ++++++++++++++++++++++--- 2 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 SecretAPI/Enums/RoomSafetyFailReason.cs diff --git a/SecretAPI/Enums/RoomSafetyFailReason.cs b/SecretAPI/Enums/RoomSafetyFailReason.cs new file mode 100644 index 0000000..a8b305b --- /dev/null +++ b/SecretAPI/Enums/RoomSafetyFailReason.cs @@ -0,0 +1,43 @@ +namespace SecretAPI.Enums; + +using System; +using MapGeneration; +using SecretAPI.Extensions; +using UnityEngine; + +/// +/// Reasons why should fail. +/// +[Flags] +public enum RoomSafetyFailReason +{ + /// + /// No fail. + /// + None = 0, + + /// + /// Room safety check will fail if warhead has gone off and room is not part of . + /// + Warhead = 1 << 0, + + /// + /// Room safety check will fail if decontamination has gone off and room is part of . + /// + Decontamination = 1 << 1, + + /// + /// Room safety check will fail if room has . + /// + Tesla = 1 << 2, + + /// + /// Room safety check will fail if the listed room fails a check. + /// + MissingFloor = 1 << 3, + + /// + /// Room safety check will fail if the listed room is part of . + /// + KnownBad = 1 << 4, +} \ No newline at end of file diff --git a/SecretAPI/Extensions/RoomExtensions.cs b/SecretAPI/Extensions/RoomExtensions.cs index 5225af3..e95fefe 100644 --- a/SecretAPI/Extensions/RoomExtensions.cs +++ b/SecretAPI/Extensions/RoomExtensions.cs @@ -1,8 +1,12 @@ namespace SecretAPI.Extensions; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using LabApi.Features.Wrappers; using MapGeneration; +using PlayerRoles.FirstPersonControl; +using PlayerRoles.PlayableScps.Scp106; +using SecretAPI.Enums; using UnityEngine; /// @@ -10,9 +14,13 @@ /// public static class RoomExtensions { - private static readonly List KnownUnsafeRooms = + private const float RaycastDistance = 2; + + /// + /// Gets a list of that will be denied by . + /// + public static List KnownUnsafeRooms { get; } = [ - RoomName.HczTesla, // Instant death RoomName.EzEvacShelter, // Stuck permanently RoomName.EzCollapsedTunnel, // Stuck permanently RoomName.HczWaysideIncinerator, // Death @@ -23,18 +31,66 @@ public static class RoomExtensions /// Gets whether a room is safe to teleport to. Will consider decontamination, warhead, teslas and void rooms. /// /// The room to check. + /// Reasons why the safety check should fail. /// Whether the room is safe to teleport to. - public static bool IsSafeToTeleport(this Room room) + public static bool IsSafeToTeleport(this Room room, RoomSafetyFailReason failReasons) { - if (Warhead.IsDetonated && room.Zone != FacilityZone.Surface) + if (failReasons.HasFlag(RoomSafetyFailReason.Warhead) && Warhead.IsDetonated && room.Zone != FacilityZone.Surface) return false; - if (Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) + if (failReasons.HasFlag(RoomSafetyFailReason.Decontamination) && Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) return false; - if (KnownUnsafeRooms.Contains(room.Name)) + if (failReasons.HasFlag(RoomSafetyFailReason.Tesla) && room.Name == RoomName.HczTesla) + return false; + + if (failReasons.HasFlag(RoomSafetyFailReason.KnownBad) && KnownUnsafeRooms.Contains(room.Name)) + return false; + + if (failReasons.HasFlag(RoomSafetyFailReason.MissingFloor) && !Physics.Raycast(room.Position, Vector3.down, out _, RaycastDistance, FpcStateProcessor.Mask)) + return false; + + return true; + } + + /// + /// Tries to get a location to teleport a to. + /// + /// The player to attempt to get a teleport position from. + /// The position found if any, otherwise null. + /// If set to anything other than will only attempt to find in that zone. + /// The default radius allowed nea the found spot. + /// Whether a valid teleport position was correctly found. + public static bool TryGetTeleportLocation(this Player player, [NotNullWhen(true)] out Vector3? position, FacilityZone zone = FacilityZone.None, float defaultRadius = Scp106PocketExitFinder.RaycastRange) + { + position = null; + return player.RoleBase is IFpcRole fpc && TryGetTeleportLocation(fpc, out position, zone); + } + + /// + /// Tries to get a location to teleport a to. + /// + /// The to attempt to get a teleport position from. + /// The position found if any, otherwise null. + /// If set to anything other than will only attempt to find in that zone. + /// The default radius allowed nea the found spot. + /// Whether a valid teleport position was correctly found. + public static bool TryGetTeleportLocation(this IFpcRole fpc, [NotNullWhen(true)] out Vector3? position, FacilityZone zone = FacilityZone.None, float defaultRadius = Scp106PocketExitFinder.RaycastRange) + { + position = null; + + IEnumerable poses = zone == FacilityZone.None + ? SafeLocationFinder.GetLocations(null, null) + : Scp106PocketExitFinder.GetPosesForZone(zone); + + if (!poses.TryGetRandomValue(out Pose pose)) return false; - return Physics.Raycast(room.Position, Vector3.down, out _, 2); + float radius = defaultRadius; + if (Room.TryGetRoomAtPosition(pose.position, out Room? room)) + radius = Scp106PocketExitFinder.GetRaycastRange(room.Zone); + + position = SafeLocationFinder.GetSafePosition(pose.position, pose.forward, radius, fpc.FpcModule.CharController); + return true; } } \ No newline at end of file From aadb2e45d36d6f2417fd0f31fc86d9ecf48e332b Mon Sep 17 00:00:00 2001 From: Evelyn <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:28:20 +0200 Subject: [PATCH 17/37] Prefab Debugging (#88) --- SecretAPI/Debugging/PrefabDebugging.cs | 51 ++++++++++++++++++++++++++ SecretAPI/Features/PrefabManager.cs | 48 ++++++++++++++++++++---- 2 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 SecretAPI/Debugging/PrefabDebugging.cs diff --git a/SecretAPI/Debugging/PrefabDebugging.cs b/SecretAPI/Debugging/PrefabDebugging.cs new file mode 100644 index 0000000..d82e9f2 --- /dev/null +++ b/SecretAPI/Debugging/PrefabDebugging.cs @@ -0,0 +1,51 @@ +#if DEBUG + +namespace SecretAPI.Debugging; + +using System; +using System.Collections.Generic; +using System.Reflection; +using LabApi.Events.Handlers; +using Mirror; +using SecretAPI.Attributes; +using SecretAPI.Features; +using UnityEngine; +using Logger = LabApi.Features.Console.Logger; + +/// +/// Debugs basegame prefabs by logging information about them. +/// +internal static class PrefabDebugging +{ + /// + /// Loads the prefab debugging. + /// + [CallOnLoad] + internal static void Load() + { + ServerEvents.WaitingForPlayers += OnWaiting; + } + + private static void OnWaiting() + { + foreach (PropertyInfo properties in typeof(PrefabManager).GetProperties()) + { + try + { + if (properties.GetValue(null) == null) + Logger.Error($"[PrefabDebugging] {properties.Name} returned a null value!"); + } + catch (Exception ex) + { + Logger.Error($"[PrefabDebugging] {properties.Name} ran into an exception: {ex}"); + } + } + + foreach (KeyValuePair pair in NetworkClient.prefabs) + { + Logger.Debug($"[PrefabDebugging] Key ({pair.Key}) - Value ({pair.Value.name})"); + } + } +} + +#endif \ No newline at end of file diff --git a/SecretAPI/Features/PrefabManager.cs b/SecretAPI/Features/PrefabManager.cs index 80250c3..8008760 100644 --- a/SecretAPI/Features/PrefabManager.cs +++ b/SecretAPI/Features/PrefabManager.cs @@ -2,8 +2,11 @@ using System; using System.Linq; +using AdminToys; using Interactables.Interobjects; using MapGeneration; +using Mirror; +using UnityEngine; /// /// Manages prefabs that have variants and cannot be easily used within . @@ -15,6 +18,12 @@ public static class PrefabManager private const string HczBulkDoorName = "HCZ BulkDoor"; private const string EzDoorName = "EZ BreakableDoor"; + private const string EzArmCameraToyName = "EzArmCameraToy"; + private const string EzCameraToyName = "EzCameraToy"; + private const string LczCameraToyName = "LczCameraToy"; + private const string HczCameraToyName = "HczCameraToy"; + private const string SzCameraToyName = "SzCameraToy"; + /// /// Gets the prefab. /// @@ -23,24 +32,49 @@ public static class PrefabManager /// /// Gets the found in . /// - public static BasicDoor LczDoorPrefab => field ??= GetDoor(LczDoorName); + public static BasicDoor LczDoorPrefab => field ??= GetOrThrow(LczDoorName); /// /// Gets the found in . /// - public static BasicDoor HczDoorPrefab => field ??= GetDoor(HczDoorName); + public static BasicDoor HczDoorPrefab => field ??= GetOrThrow(HczDoorName); /// /// Gets the found in . /// - public static BasicDoor HczBulkDoorPrefab => field ??= GetDoor(HczBulkDoorName); + public static BasicDoor HczBulkDoorPrefab => field ??= GetOrThrow(HczBulkDoorName); /// /// Gets the found in . /// - public static BasicDoor EzDoorPrefab => field ??= GetDoor(EzDoorName); + public static BasicDoor EzDoorPrefab => field ??= GetOrThrow(EzDoorName); + + /// + /// Gets the found in , with an arm extension. + /// + public static Scp079CameraToy EzArmCameraToyPrefab => field ??= GetOrThrow(EzArmCameraToyName); + + /// + /// Gets the found in . + /// + public static Scp079CameraToy EzCameraToyPrefab => field ??= GetOrThrow(EzCameraToyName); + + /// + /// Gets the found in . + /// + public static Scp079CameraToy LczCameraToyPrefab => field ??= GetOrThrow(LczCameraToyName); + + /// + /// Gets the found in . + /// + public static Scp079CameraToy HczCameraToyPrefab => field ??= GetOrThrow(HczCameraToyName); + + /// + /// Gets the found in . + /// + public static Scp079CameraToy SzCameraToyPrefab => field ??= GetOrThrow(SzCameraToyName); - private static BasicDoor GetDoor(string name) - => PrefabStore.AllComponentPrefabs.FirstOrDefault(d => d.name == name) - ?? throw new InvalidOperationException($"[PrefabManager] Failed to get door named {name} | Report this as a bug!"); + private static T GetOrThrow(string name) + where T : NetworkBehaviour => PrefabStore.AllComponentPrefabs.FirstOrDefault(c => c.name == name) + ?? throw new InvalidOperationException($"[PrefabManager] Failed to component ({typeof(T).Name}) by name {name} | Report this as a bug"); } \ No newline at end of file From b0ccc130a60c36c119b87bdf030635d4e0c2ec82 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:32:00 +0200 Subject: [PATCH 18/37] fix: nuget push --- .github/workflows/nuget.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 1be9c83..6ad684c 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -33,7 +33,7 @@ jobs: - name: Build and Pack NuGet env: SL_REFERENCES: ${{ env.REFERENCES_PATH }} - run: dotnet pack -c Release --output ${{ NUGET_PACKAGED_PATH }} + run: dotnet pack -c Release --output ${{ env.NUGET_PACKAGED_PATH }} - name: Push NuGet package run: | From 98da5087b428619c38343fa3a41d53df6e976feb Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:35:03 +0200 Subject: [PATCH 19/37] Bump to V3.0.0-beta2 --- SecretAPI/SecretAPI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index 48282bb..7feb83f 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -4,7 +4,7 @@ net48 latest enable - 3.0.0-beta1 + 3.0.0-beta2 true From 08c3dc6189cd9e03b1cebe97a732d86b5e9f77cf Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:40:52 +0200 Subject: [PATCH 20/37] docs: Remove outdated mention of RoleExtensions and add CallOnLoadAttribute mention in README.md --- .github/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/README.md b/.github/README.md index dd60495..02d086c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -3,11 +3,11 @@ # Features - CollectionExtensions: Extensions to provide utility for collections like lists and arrays. -- RoleExtensions: Extensions to help with handling role specific tasks. Like getting a role's spawn point. - RoomExtensions: Extensions to help with room specific tasks, like checking if a room is safe to teleport to. - HarmonyExtensions: Extensions to provide more utility to Harmony patching, like adding some updated Harmony features which can't be utilised, i.e. patching by category. - CustomPlayerEffect: Create custom status effects using the base-game system. -- IRegister: Handle auto registering certain plugin features inheriting this interface and then running `IRegister.RegisterAll()` in your plugin's initialise method. +- IRegister: Handle auto registering certain plugin features inheriting this interface and then running `IRegister.RegisterAll(Assembly)` in your plugin's initialise method. +- CallOnLoadAttribute: Allows calling static initialize methods on enable via ``CallOnLoadAttribute.Load(Assembly)`` - CustomSetting: Server Specific Settings without the management hassle, control everything for 1 setting in 1 class, including permissions. A better setting system overall. # Examples From 65cc8f2fbc596ccb7aa2e5c030060049a4fe7cb5 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:41:20 +0200 Subject: [PATCH 21/37] CustomHeader::Label & ReducedPadding getters --- SecretAPI/Features/UserSettings/CustomHeader.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/SecretAPI/Features/UserSettings/CustomHeader.cs b/SecretAPI/Features/UserSettings/CustomHeader.cs index d906ea8..5dc96cc 100644 --- a/SecretAPI/Features/UserSettings/CustomHeader.cs +++ b/SecretAPI/Features/UserSettings/CustomHeader.cs @@ -25,4 +25,14 @@ public CustomHeader(string label, bool reducedPadding = false, string? hint = nu /// public SSGroupHeader Base { get; } + + /// + /// Gets the label of the header. + /// + public string Label => Base.Label; + + /// + /// Gets a value indicating whether the padding should be reduced. + /// + public bool ReducedPadding => Base.ReducedPadding; } \ No newline at end of file From 2f85f453b45ca6d4117994478cff06fed2bc3aed Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:53:53 +0200 Subject: [PATCH 22/37] fix: CustomPlainTextSetting not implementing HasValueChanged --- SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs index 3e10dbb..cc759c4 100644 --- a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs @@ -46,6 +46,9 @@ protected CustomPlainTextSetting( /// public new SSPlaintextSetting Base { get; } + /// + public override bool HasValueChanged => LastInputText != InputText; + /// /// Gets the input text prior to the most recent call. /// From 49c79cb4d6ea17cbdcae892b7850e801e6e860b6 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 15:56:57 +0200 Subject: [PATCH 23/37] Improve github actions --- .github/workflows/nuget.yml | 28 +++++++++++----------------- .github/workflows/pull_request.yml | 23 +++++++++-------------- 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 6ad684c..7cb7a3c 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -6,12 +6,11 @@ on: jobs: build: - runs-on: windows-latest + runs-on: ubuntu-latest env: - REFERENCES_URL: https://exmod-team.github.io/SL-References/Dev.zip REFERENCES_PATH: ${{ github.workspace }}/References - NUGET_PACKAGED_PATH: ${{ github.workspace }}/nupkgs + BUILD_OUTPUT: ${{ github.workspace }}/Output steps: - name: Checkout @@ -20,22 +19,17 @@ jobs: - name: Setup Dotnet uses: actions/setup-dotnet@v4.0.1 - - name: Download References - shell: pwsh - run: | - Invoke-WebRequest -Uri ${{ env.REFERENCES_URL }} -OutFile "${{ github.workspace }}/References.zip" - Expand-Archive -Path "${{ github.workspace }}/References.zip" -DestinationPath ${{ env.REFERENCES_PATH }} - -# - name: Rename Assembly-CSharp-Publicized to Assembly-CSharp -# shell: pwsh -# run: Rename-Item -Path "${{ env.REFERENCES_PATH }}\Assembly-CSharp-Publicized.dll" -NewName "Assembly-CSharp.dll" + - name: Download SCP:SL References + uses: Axwabo/scpsl-references-downloader@v1 + id: refs + with: + directory: $ {{ env.REFERENCES_PATH }} - name: Build and Pack NuGet env: - SL_REFERENCES: ${{ env.REFERENCES_PATH }} - run: dotnet pack -c Release --output ${{ env.NUGET_PACKAGED_PATH }} + SL_REFERENCES: ${{ steps.refs.outputs.target }} + run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} - name: Push NuGet package - run: | - $PackageFile = (Get-ChildItem -Path "${NUGET_PACKAGED_PATH}" -Include 'SecretAPI.*.nupkg' -Recurse | Select-Object -First 1).FullName - dotnet nuget push $PackageFile --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json \ No newline at end of file + working-directory: ${{ env.BUILD_OUTPUT }} + run: dotnet nuget push SecretAPI.*.nupkg \ No newline at end of file diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 2de576e..59cab2c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -10,8 +10,8 @@ jobs: runs-on: windows-latest env: - REFERENCES_URL: https://exmod-team.github.io/SL-References/Dev.zip REFERENCES_PATH: ${{ github.workspace }}/References + BUILD_OUTPUT: ${{ github.workspace }}/Output steps: - name: Checkout @@ -20,25 +20,20 @@ jobs: - name: Setup Dotnet uses: actions/setup-dotnet@v4.0.1 - - name: Download References - shell: pwsh - run: | - Invoke-WebRequest -Uri ${{ env.REFERENCES_URL }} -OutFile "${{ github.workspace }}/References.zip" - Expand-Archive -Path "${{ github.workspace }}/References.zip" -DestinationPath ${{ env.REFERENCES_PATH }} - -# - name: Rename Assembly-CSharp-Publicized to Assembly-CSharp -# shell: pwsh -# run: Rename-Item -Path "${{ env.REFERENCES_PATH }}\Assembly-CSharp-Publicized.dll" -NewName "Assembly-CSharp.dll" + - name: Download SCP:SL References + uses: Axwabo/scpsl-references-downloader@v1 + id: refs + with: + directory: $ {{ env.REFERENCES_PATH }} - name: Build env: - SL_REFERENCES: ${{ env.REFERENCES_PATH }} - shell: pwsh - run: dotnet build -c Release + SL_REFERENCES: ${{ steps.refs.outputs.target }} + run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} - name: Upload uses: actions/upload-artifact@v4 with: name: Build Result - path: ${{ github.workspace }}/**/bin/Release/net48/*SecretAPI*.dll + path: ${{ env.BUILD_OUTPUT }} retention-days: 7 \ No newline at end of file From ed82973094c9d4d5767454c5bd29a37cf5bcd485 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:00:58 +0200 Subject: [PATCH 24/37] fix actions? i added a space by accident --- .github/workflows/nuget.yml | 2 +- .github/workflows/pull_request.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 7cb7a3c..4f5c14e 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -23,7 +23,7 @@ jobs: uses: Axwabo/scpsl-references-downloader@v1 id: refs with: - directory: $ {{ env.REFERENCES_PATH }} + directory: ${{ env.REFERENCES_PATH }} - name: Build and Pack NuGet env: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 59cab2c..75be90d 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -24,12 +24,12 @@ jobs: uses: Axwabo/scpsl-references-downloader@v1 id: refs with: - directory: $ {{ env.REFERENCES_PATH }} + directory: ${{ env.REFERENCES_PATH }} - name: Build env: SL_REFERENCES: ${{ steps.refs.outputs.target }} - run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} + run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} - name: Upload uses: actions/upload-artifact@v4 From 9f5a723cadc1631e52f071b37d33db46f88f532a Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:05:09 +0200 Subject: [PATCH 25/37] Fix redundant step + only upload SecretAPI --- .github/workflows/nuget.yml | 3 --- .github/workflows/pull_request.yml | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index 4f5c14e..c0f73c3 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -16,9 +16,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Setup Dotnet - uses: actions/setup-dotnet@v4.0.1 - - name: Download SCP:SL References uses: Axwabo/scpsl-references-downloader@v1 id: refs diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 75be90d..5267b93 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -17,9 +17,6 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Setup Dotnet - uses: actions/setup-dotnet@v4.0.1 - - name: Download SCP:SL References uses: Axwabo/scpsl-references-downloader@v1 id: refs @@ -35,5 +32,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: Build Result - path: ${{ env.BUILD_OUTPUT }} + path: ${{ env.BUILD_OUTPUT }}\SecretAPI*.dll retention-days: 7 \ No newline at end of file From 53762137f4baa0a8bc29cc835f23a79d44be8f0a Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Sun, 5 Apr 2026 00:28:10 +0200 Subject: [PATCH 26/37] Minor code improvements --- SecretAPI/Extensions/FastEnums.cs | 16 +++++++++ SecretAPI/Extensions/PlayerExtensions.cs | 8 ++--- SecretAPI/Extensions/RoomExtensions.cs | 10 +++--- .../Features/Effects/CustomPlayerEffect.cs | 33 +++++++++---------- SecretAPI/Features/PrefabManager.cs | 2 +- 5 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 SecretAPI/Extensions/FastEnums.cs diff --git a/SecretAPI/Extensions/FastEnums.cs b/SecretAPI/Extensions/FastEnums.cs new file mode 100644 index 0000000..1dcfe39 --- /dev/null +++ b/SecretAPI/Extensions/FastEnums.cs @@ -0,0 +1,16 @@ +namespace SecretAPI.Extensions; + +using System; +using SecretAPI.Enums; + +/// +/// Faster flag checking for . +/// +public static class FastEnums +{ + /// + public static bool HasFlagFast(this DoorPermissionCheck @enum, DoorPermissionCheck flag) => (@enum & flag) == flag; + + /// + public static bool HasFlagFast(this RoomSafetyFailReason @enum, RoomSafetyFailReason flag) => (@enum & flag) == flag; +} \ No newline at end of file diff --git a/SecretAPI/Extensions/PlayerExtensions.cs b/SecretAPI/Extensions/PlayerExtensions.cs index b4a8a18..afd6335 100644 --- a/SecretAPI/Extensions/PlayerExtensions.cs +++ b/SecretAPI/Extensions/PlayerExtensions.cs @@ -29,19 +29,19 @@ public StatusEffectBase GetEffect(string name) /// Whether a valid permission was found. public bool HasDoorPermission(IDoorPermissionRequester requester, DoorPermissionCheck checkFlags = DoorPermissionCheck.Default) { - if (checkFlags.HasFlag(DoorPermissionCheck.Bypass) && player.IsBypassEnabled) + if (checkFlags.HasFlagFast(DoorPermissionCheck.Bypass) && player.IsBypassEnabled) return true; - if (checkFlags.HasFlag(DoorPermissionCheck.Role) && player.RoleBase is IDoorPermissionProvider roleProvider && requester.PermissionsPolicy.CheckPermissions(roleProvider.GetPermissions(requester))) + if (checkFlags.HasFlagFast(DoorPermissionCheck.Role) && player.RoleBase is IDoorPermissionProvider roleProvider && requester.PermissionsPolicy.CheckPermissions(roleProvider.GetPermissions(requester))) return true; foreach (Item item in player.Items) { bool isCurrent = item == player.CurrentItem; - if (!checkFlags.HasFlag(DoorPermissionCheck.CurrentItem) && isCurrent) + if (!checkFlags.HasFlagFast(DoorPermissionCheck.CurrentItem) && isCurrent) continue; - if (!checkFlags.HasFlag(DoorPermissionCheck.InventoryExcludingCurrent) && !isCurrent) + if (!checkFlags.HasFlagFast(DoorPermissionCheck.InventoryExcludingCurrent) && !isCurrent) continue; if (item.Base is IDoorPermissionProvider itemProvider && requester.PermissionsPolicy.CheckPermissions(itemProvider.GetPermissions(requester))) diff --git a/SecretAPI/Extensions/RoomExtensions.cs b/SecretAPI/Extensions/RoomExtensions.cs index e95fefe..9b23350 100644 --- a/SecretAPI/Extensions/RoomExtensions.cs +++ b/SecretAPI/Extensions/RoomExtensions.cs @@ -35,19 +35,19 @@ public static class RoomExtensions /// Whether the room is safe to teleport to. public static bool IsSafeToTeleport(this Room room, RoomSafetyFailReason failReasons) { - if (failReasons.HasFlag(RoomSafetyFailReason.Warhead) && Warhead.IsDetonated && room.Zone != FacilityZone.Surface) + if (failReasons.HasFlagFast(RoomSafetyFailReason.Warhead) && Warhead.IsDetonated && room.Zone != FacilityZone.Surface) return false; - if (failReasons.HasFlag(RoomSafetyFailReason.Decontamination) && Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) + if (failReasons.HasFlagFast(RoomSafetyFailReason.Decontamination) && Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) return false; - if (failReasons.HasFlag(RoomSafetyFailReason.Tesla) && room.Name == RoomName.HczTesla) + if (failReasons.HasFlagFast(RoomSafetyFailReason.Tesla) && room.Name == RoomName.HczTesla) return false; - if (failReasons.HasFlag(RoomSafetyFailReason.KnownBad) && KnownUnsafeRooms.Contains(room.Name)) + if (failReasons.HasFlagFast(RoomSafetyFailReason.KnownBad) && KnownUnsafeRooms.Contains(room.Name)) return false; - if (failReasons.HasFlag(RoomSafetyFailReason.MissingFloor) && !Physics.Raycast(room.Position, Vector3.down, out _, RaycastDistance, FpcStateProcessor.Mask)) + if (failReasons.HasFlagFast(RoomSafetyFailReason.MissingFloor) && !Physics.Raycast(room.Position, Vector3.down, out _, RaycastDistance, FpcStateProcessor.Mask)) return false; return true; diff --git a/SecretAPI/Features/Effects/CustomPlayerEffect.cs b/SecretAPI/Features/Effects/CustomPlayerEffect.cs index 6fedb8d..c4b784a 100644 --- a/SecretAPI/Features/Effects/CustomPlayerEffect.cs +++ b/SecretAPI/Features/Effects/CustomPlayerEffect.cs @@ -16,8 +16,6 @@ /// public abstract class CustomPlayerEffect : StatusEffectBase { - private static bool isLoaded; - /// /// Gets a list of types to register (Must inherit ). /// Must be , can be gotten through typeof(Scp207) @@ -43,25 +41,24 @@ internal static void Initialize() EffectsToRegister.Add(typeof(StaminaUsageDisablerEffect)); EffectsToRegister.Add(typeof(SprintDisablerEffect)); - SceneManager.sceneLoaded += (_, _) => - { - if (isLoaded) - return; + SceneManager.sceneLoaded += OnSceneLoaded; + } - isLoaded = true; + private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + SceneManager.sceneLoaded -= OnSceneLoaded; - Transform playerEffects = PrefabStore.Prefab.playerEffectsController.effectsGameObject.transform; - foreach (Type type in EffectsToRegister) + Transform playerEffects = PrefabStore.Prefab.playerEffectsController.effectsGameObject.transform; + foreach (Type type in EffectsToRegister) + { + if (!typeof(StatusEffectBase).IsAssignableFrom(type)) { - if (!typeof(StatusEffectBase).IsAssignableFrom(type)) - { - Logger.Error($"[CustomPlayerEffect.Initialize] {type.FullName} is not a valid StatusEffectBase and thus could not be registered!"); - continue; - } - - // register effect into prefab - new GameObject(type.Name, type).transform.parent = playerEffects; + Logger.Error($"[CustomPlayerEffect.Initialize] {type.FullName} is not a valid StatusEffectBase and thus could not be registered!"); + continue; } - }; + + // register effect into prefab + new GameObject(type.Name, type).transform.parent = playerEffects; + } } } \ No newline at end of file diff --git a/SecretAPI/Features/PrefabManager.cs b/SecretAPI/Features/PrefabManager.cs index 8008760..cfb46c2 100644 --- a/SecretAPI/Features/PrefabManager.cs +++ b/SecretAPI/Features/PrefabManager.cs @@ -76,5 +76,5 @@ public static class PrefabManager private static T GetOrThrow(string name) where T : NetworkBehaviour => PrefabStore.AllComponentPrefabs.FirstOrDefault(c => c.name == name) - ?? throw new InvalidOperationException($"[PrefabManager] Failed to component ({typeof(T).Name}) by name {name} | Report this as a bug"); + ?? throw new InvalidOperationException($"[PrefabManager] Failed to get component ({typeof(T).Name}) by name {name} | Report this as a bug"); } \ No newline at end of file From 1ca4da34e3a9b9572a2cd610d10e0111adc1343b Mon Sep 17 00:00:00 2001 From: Evelyn <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:22:47 +0200 Subject: [PATCH 27/37] PlayerRoundIgnore (#89) * PlayerRoundIgnore * Handle player leaving --- SecretAPI/Debugging/PatchDebugger.cs | 22 +++++++ SecretAPI/Enums/RoundIgnoreStatus.cs | 26 ++++++++ SecretAPI/Extensions/FastEnums.cs | 3 + SecretAPI/Features/PlayerRoundIgnore.cs | 39 ++++++++++++ .../Patches/Features/RoundEndIgnorePatch.cs | 54 ++++++++++++++++ .../Patches/Features/RoundIgnoreCountPatch.cs | 61 +++++++++++++++++++ 6 files changed, 205 insertions(+) create mode 100644 SecretAPI/Debugging/PatchDebugger.cs create mode 100644 SecretAPI/Enums/RoundIgnoreStatus.cs create mode 100644 SecretAPI/Features/PlayerRoundIgnore.cs create mode 100644 SecretAPI/Patches/Features/RoundEndIgnorePatch.cs create mode 100644 SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs diff --git a/SecretAPI/Debugging/PatchDebugger.cs b/SecretAPI/Debugging/PatchDebugger.cs new file mode 100644 index 0000000..652c502 --- /dev/null +++ b/SecretAPI/Debugging/PatchDebugger.cs @@ -0,0 +1,22 @@ +#if DEBUG + +namespace SecretAPI.Debugging; + +using SecretAPI.Attributes; + +/// +/// Patch debugger. +/// +internal static class PatchDebugger +{ + /// + /// Loads the debugs for patches and ensures all patches have loaded. + /// + [CallOnLoad] + internal static void Load() + { + SecretApi.Harmony.PatchAll(SecretApi.Assembly); + } +} + +#endif \ No newline at end of file diff --git a/SecretAPI/Enums/RoundIgnoreStatus.cs b/SecretAPI/Enums/RoundIgnoreStatus.cs new file mode 100644 index 0000000..ee9eafb --- /dev/null +++ b/SecretAPI/Enums/RoundIgnoreStatus.cs @@ -0,0 +1,26 @@ +namespace SecretAPI.Enums; + +using System; + +/// +/// Defines how a player should be ignored during a round. +/// +[Flags] +public enum RoundIgnoreStatus +{ + /// + /// Player will not be ignored. + /// + None = 0, + + /// + /// Player is ignored from . + /// + RoundEndingCheck = 1 << 0, + + /// + /// Player is ignored from . + /// + /// This will not reflect when round is not in progress (either due to it being ended or due to lobby). + ScpTargetCount = 1 << 1, +} \ No newline at end of file diff --git a/SecretAPI/Extensions/FastEnums.cs b/SecretAPI/Extensions/FastEnums.cs index 1dcfe39..2273f85 100644 --- a/SecretAPI/Extensions/FastEnums.cs +++ b/SecretAPI/Extensions/FastEnums.cs @@ -13,4 +13,7 @@ public static class FastEnums /// public static bool HasFlagFast(this RoomSafetyFailReason @enum, RoomSafetyFailReason flag) => (@enum & flag) == flag; + + /// + public static bool HasFlagFast(this RoundIgnoreStatus @enum, RoundIgnoreStatus flag) => (@enum & flag) == flag; } \ No newline at end of file diff --git a/SecretAPI/Features/PlayerRoundIgnore.cs b/SecretAPI/Features/PlayerRoundIgnore.cs new file mode 100644 index 0000000..5b7e589 --- /dev/null +++ b/SecretAPI/Features/PlayerRoundIgnore.cs @@ -0,0 +1,39 @@ +namespace SecretAPI.Features; + +using System.Collections.Generic; +using LabApi.Events.Arguments.PlayerEvents; +using LabApi.Events.Handlers; +using LabApi.Features.Wrappers; +using SecretAPI.Enums; +using SecretAPI.Extensions; + +/// +/// Handles allowing easier ignoring of players for . +/// +public static class PlayerRoundIgnore +{ + extension(Player player) + { + /// + /// Gets or sets the players current . + /// + public RoundIgnoreStatus RoundIgnoreStatus + { + get => PlayerToStatus.GetValueOrDefault(player, RoundIgnoreStatus.None); + set => PlayerToStatus[player] = value; + } + } + + static PlayerRoundIgnore() + { + SecretApi.Harmony.PatchCategory(nameof(PlayerRoundIgnore)); + PlayerEvents.Left += OnPlayerLeft; + } + + /// + /// Gets a collection of to their current . + /// + public static Dictionary PlayerToStatus { get; } = new(); + + private static void OnPlayerLeft(PlayerLeftEventArgs ev) => PlayerToStatus.Remove(ev.Player); +} \ No newline at end of file diff --git a/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs b/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs new file mode 100644 index 0000000..d8611b1 --- /dev/null +++ b/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs @@ -0,0 +1,54 @@ +namespace SecretAPI.Patches.Features; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using LabApi.Features.Wrappers; +using PlayerRoles; +using SecretAPI.Attributes; +using SecretAPI.Enums; +using SecretAPI.Extensions; +using SecretAPI.Features; + +/// +/// Handles patching to implement into . +/// +[HarmonyPatchCategory(nameof(PlayerRoundIgnore))] +[HarmonyPatch] +internal static class RoundEndIgnorePatch +{ + private const string StateMachine = "<_ProcessServerSideCode>d__58"; + private const string MoveNext = "MoveNext"; + private const int ReferenceHubLocalIndex = 20; + + private static MethodInfo TargetMethod() + { + // typeof(RoundSummary).GetNestedTypes(AccessTools.all).ForEach(type => Logger.Debug(type.FullName ?? "NULL")); + Type nestedType = typeof(RoundSummary).GetNestedTypes(AccessTools.all) + .FirstOrDefault(type => type.Name is StateMachine) ?? throw new Exception($"Could not locate state machine for {StateMachine}"); + + // nestedType.GetMethods(AccessTools.all).ForEach(method => Logger.Debug(method.Name)); + MethodInfo moveNextMethod = nestedType.GetMethods(AccessTools.all) + .FirstOrDefault(x => x.Name.Contains(MoveNext)) ?? throw new Exception($"Could not locate {MoveNext} method in state machine"); + + return moveNextMethod; + } + + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + CodeMatcher matcher = new CodeMatcher(instructions, generator) + .MatchEndForward(new CodeMatch(CodeInstruction.Call(typeof(PlayerRolesUtils), nameof(PlayerRolesUtils.GetTeam), [typeof(ReferenceHub)]))) + .CreateLabel(out Label skip) + .Insert( + new CodeInstruction(OpCodes.Ldloc_S, ReferenceHubLocalIndex), + CodeInstruction.Call(typeof(RoundEndIgnorePatch), nameof(IsPlayerIgnored)), + new CodeInstruction(OpCodes.Brtrue_S, skip)); + + return matcher.InstructionEnumeration(); + } + + private static bool IsPlayerIgnored(ReferenceHub hub) => Player.Get(hub).RoundIgnoreStatus.HasFlagFast(RoundIgnoreStatus.RoundEndingCheck); +} \ No newline at end of file diff --git a/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs b/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs new file mode 100644 index 0000000..0a1f32e --- /dev/null +++ b/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs @@ -0,0 +1,61 @@ +namespace SecretAPI.Patches.Features; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using LabApi.Features.Wrappers; +using Mirror; +using SecretAPI.Attributes; +using SecretAPI.Enums; +using SecretAPI.Extensions; +using SecretAPI.Features; + +/// +/// Handles patching to implement into . +/// +[HarmonyPatchCategory(nameof(PlayerRoundIgnore))] +[HarmonyPatch] +internal static class RoundIgnoreCountPatch +{ + private const string StateMachine = "<>c"; + private const string UpdateTargetCount = "UpdateTargetCount"; + + private static MethodInfo TargetMethod() + { + Type nestedType = typeof(RoundSummary).GetNestedTypes(AccessTools.all) + .FirstOrDefault(currentType => currentType.Name is StateMachine) ?? throw new Exception("Could not locate state machine for RoundSummary::<>c"); + + // nestedType.GetMethods(AccessTools.all).ForEach(method => Logger.Debug(method.Name)); + MethodInfo updateCountMethod = nestedType.GetMethods(AccessTools.all) + .FirstOrDefault(x => x.Name.Contains(UpdateTargetCount)) ?? throw new Exception($"Could not locate {UpdateTargetCount} method in state machine"); + + return updateCountMethod; + } + + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + CodeMatcher matcher = new CodeMatcher(instructions, generator) + .Start() + .CreateLabel(out Label skip) + .Insert( + new CodeInstruction(OpCodes.Ldarg_0), + CodeInstruction.Call(typeof(RoundIgnoreCountPatch), nameof(IsPlayerIgnored)), + new CodeInstruction(OpCodes.Brtrue_S, skip)); + + return matcher.InstructionEnumeration(); + } + + private static bool IsPlayerIgnored(ReferenceHub hub) + { + if (!NetworkServer.active || !Round.IsRoundStarted) + return false; + + if (hub == null) + return false; + + return hub && Player.Get(hub).RoundIgnoreStatus.HasFlagFast(RoundIgnoreStatus.ScpTargetCount); + } +} \ No newline at end of file From 16dfc1b1b6ad4f876d6402a99a631f7e46bb508c Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:57:13 +0200 Subject: [PATCH 28/37] 3.0-beta3 --- .../Features/Effects/CustomPlayerEffect.cs | 5 ++--- .../{SprintDisablerEffect.cs => Depleted.cs} | 4 ++-- ...inaUsageDisablerEffect.cs => Energized.cs} | 4 ++-- .../Effects/TemporaryDamageImmunity.cs | 19 ------------------- .../UserSettings/CustomDropdownSetting.cs | 2 +- .../Features/UserSettings/CustomHeader.cs | 2 +- .../UserSettings/CustomPlainTextSetting.cs | 2 +- .../Features/UserSettings/CustomSetting.cs | 4 ++-- .../UserSettings/CustomSliderSetting.cs | 2 +- .../UserSettings/CustomTwoButtonSetting.cs | 2 +- SecretAPI/SecretAPI.csproj | 2 +- 11 files changed, 14 insertions(+), 34 deletions(-) rename SecretAPI/Features/Effects/{SprintDisablerEffect.cs => Depleted.cs} (77%) rename SecretAPI/Features/Effects/{StaminaUsageDisablerEffect.cs => Energized.cs} (74%) delete mode 100644 SecretAPI/Features/Effects/TemporaryDamageImmunity.cs diff --git a/SecretAPI/Features/Effects/CustomPlayerEffect.cs b/SecretAPI/Features/Effects/CustomPlayerEffect.cs index c4b784a..0e63469 100644 --- a/SecretAPI/Features/Effects/CustomPlayerEffect.cs +++ b/SecretAPI/Features/Effects/CustomPlayerEffect.cs @@ -37,9 +37,8 @@ public abstract class CustomPlayerEffect : StatusEffectBase internal static void Initialize() { SecretApi.Harmony.PatchCategory(nameof(CustomPlayerEffect), SecretApi.Assembly); - EffectsToRegister.Add(typeof(TemporaryDamageImmunity)); - EffectsToRegister.Add(typeof(StaminaUsageDisablerEffect)); - EffectsToRegister.Add(typeof(SprintDisablerEffect)); + EffectsToRegister.Add(typeof(Energized)); + EffectsToRegister.Add(typeof(Depleted)); SceneManager.sceneLoaded += OnSceneLoaded; } diff --git a/SecretAPI/Features/Effects/SprintDisablerEffect.cs b/SecretAPI/Features/Effects/Depleted.cs similarity index 77% rename from SecretAPI/Features/Effects/SprintDisablerEffect.cs rename to SecretAPI/Features/Effects/Depleted.cs index 44f197d..82da797 100644 --- a/SecretAPI/Features/Effects/SprintDisablerEffect.cs +++ b/SecretAPI/Features/Effects/Depleted.cs @@ -3,9 +3,9 @@ using PlayerRoles.FirstPersonControl; /// -/// Effect that disables sprinting for a player. Sets stamina to 0 and disables regen. +/// Effect that disables sprinting for a player and disables regeneration of it. /// -public class SprintDisablerEffect : CustomPlayerEffect, IStaminaModifier +public class Depleted : CustomPlayerEffect, IStaminaModifier { /// public bool StaminaModifierActive => IsEnabled; diff --git a/SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs b/SecretAPI/Features/Effects/Energized.cs similarity index 74% rename from SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs rename to SecretAPI/Features/Effects/Energized.cs index d20d6a3..e340605 100644 --- a/SecretAPI/Features/Effects/StaminaUsageDisablerEffect.cs +++ b/SecretAPI/Features/Effects/Energized.cs @@ -3,9 +3,9 @@ using PlayerRoles.FirstPersonControl; /// -/// Effect that disables stamina usage. +/// Effect that removes stamina usage, granting infinite stamina. /// -public class StaminaUsageDisablerEffect : CustomPlayerEffect, IStaminaModifier +public class Energized : CustomPlayerEffect, IStaminaModifier { /// public bool StaminaModifierActive => IsEnabled; diff --git a/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs b/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs deleted file mode 100644 index 25daa52..0000000 --- a/SecretAPI/Features/Effects/TemporaryDamageImmunity.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace SecretAPI.Features.Effects; - -using CustomPlayerEffects; -using PlayerStatsSystem; - -/// -/// Grants a player temporary damage immunity. -/// -public class TemporaryDamageImmunity : CustomPlayerEffect, IDamageModifierEffect -{ - /// - public bool DamageModifierActive => IsEnabled; - - /// - public override EffectClassification Classification => EffectClassification.Technical; - - /// - public float GetDamageModifier(float baseDamage, DamageHandlerBase handler, HitboxType hitboxType) => 0; -} \ No newline at end of file diff --git a/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs b/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs index 2bb966a..feb3ed0 100644 --- a/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomDropdownSetting.cs @@ -100,7 +100,7 @@ public string[] Options public void SendServerUpdate(int selectionId) => Base.SendValueUpdate(selectionId, false, IsKnownOwnerHub); /// - protected internal override void HandleBeforeSettingUpdate() + protected override void HandleBeforeSettingUpdate() { base.HandleBeforeSettingUpdate(); diff --git a/SecretAPI/Features/UserSettings/CustomHeader.cs b/SecretAPI/Features/UserSettings/CustomHeader.cs index 5dc96cc..e9ab950 100644 --- a/SecretAPI/Features/UserSettings/CustomHeader.cs +++ b/SecretAPI/Features/UserSettings/CustomHeader.cs @@ -19,7 +19,7 @@ public CustomHeader(string label, bool reducedPadding = false, string? hint = nu } /// - /// Gets a for Example purposes. + /// Gets a used to contain all the example settings of SecretAPI.Examples. /// public static CustomHeader Examples { get; } = new("Examples", hint: "Features used as examples"); diff --git a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs index cc759c4..dcea8fb 100644 --- a/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomPlainTextSetting.cs @@ -105,7 +105,7 @@ public int CharacterLimit public void SendServerUpdate(string text) => Base.SendValueUpdate(text, false, IsKnownOwnerHub); /// - protected internal override void HandleBeforeSettingUpdate() + protected override void HandleBeforeSettingUpdate() { base.HandleBeforeSettingUpdate(); LastInputText = InputText; diff --git a/SecretAPI/Features/UserSettings/CustomSetting.cs b/SecretAPI/Features/UserSettings/CustomSetting.cs index e6c86df..373a793 100644 --- a/SecretAPI/Features/UserSettings/CustomSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSetting.cs @@ -286,8 +286,8 @@ public static void SendSettingsToPlayer(Player player, int? version = null) /// /// Called before , adding and . /// - /// This will not have the current status. - protected internal virtual void HandleBeforeSettingUpdate() + /// This will not have the current status and should always call back to base. + protected virtual void HandleBeforeSettingUpdate() { LastUpdateType = LastUpdateType == SettingResponseType.None ? SettingResponseType.Initial diff --git a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs index 3bb9cb5..92cfd49 100644 --- a/SecretAPI/Features/UserSettings/CustomSliderSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomSliderSetting.cs @@ -155,7 +155,7 @@ public bool UseInteger public void SendServerUpdate(float value) => Base.SendValueUpdate(value, false, IsKnownOwnerHub); /// - protected internal override void HandleBeforeSettingUpdate() + protected override void HandleBeforeSettingUpdate() { base.HandleBeforeSettingUpdate(); LastSelectedValueFloat = SelectedValueFloat; diff --git a/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs b/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs index d042ad2..9fae6b9 100644 --- a/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs +++ b/SecretAPI/Features/UserSettings/CustomTwoButtonSetting.cs @@ -100,7 +100,7 @@ public string OptionB public void SendServerUpdate(bool isB) => Base.SendValueUpdate(isB, false, IsKnownOwnerHub); /// - protected internal override void HandleBeforeSettingUpdate() + protected override void HandleBeforeSettingUpdate() { base.HandleBeforeSettingUpdate(); WasLastOptionB = IsOptionB; diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index 7feb83f..54fa7df 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -4,7 +4,7 @@ net48 latest enable - 3.0.0-beta2 + 3.0.0-beta3 true From 4affa215effdc110f4d57e4accfa409f9b385b20 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:26:40 +0200 Subject: [PATCH 29/37] Consistency --- .github/workflows/nuget.yml | 2 +- .github/workflows/pull_request.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index c0f73c3..ff4c76f 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -22,7 +22,7 @@ jobs: with: directory: ${{ env.REFERENCES_PATH }} - - name: Build and Pack NuGet + - name: Build env: SL_REFERENCES: ${{ steps.refs.outputs.target }} run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5267b93..8307bcc 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -7,7 +7,7 @@ on: jobs: build: - runs-on: windows-latest + runs-on: ubuntu-latest env: REFERENCES_PATH: ${{ github.workspace }}/References From 41852c85417514e5d9603f06be62ce71f4ec260e Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:37:30 +0200 Subject: [PATCH 30/37] Test setting up dotnet --- .github/workflows/pull_request.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8307bcc..459c93f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -17,6 +17,9 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Setup Dotnet + uses: actions/setup-dotnet@v5.2.0 + - name: Download SCP:SL References uses: Axwabo/scpsl-references-downloader@v1 id: refs From a4b20be3d7a78690df303b23ae8ef601bfcd5409 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:44:15 +0200 Subject: [PATCH 31/37] List References --- .github/workflows/pull_request.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 459c93f..8aaf08a 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -26,6 +26,9 @@ jobs: with: directory: ${{ env.REFERENCES_PATH }} + - name: List references + run: ls ${{ env.REFERENCES_PATH }} + - name: Build env: SL_REFERENCES: ${{ steps.refs.outputs.target }} From c03402c67ed1699906f96eeefc5b853e5eb61a88 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:46:33 +0200 Subject: [PATCH 32/37] ls steps.refs.outputs.target --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8aaf08a..c6b9161 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -27,7 +27,7 @@ jobs: directory: ${{ env.REFERENCES_PATH }} - name: List references - run: ls ${{ env.REFERENCES_PATH }} + run: ls ${{ steps.refs.outputs.target }} - name: Build env: From 0aa728694b5c085d839d301c09631f6e17dca08b Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Wed, 8 Apr 2026 21:55:13 +0200 Subject: [PATCH 33/37] Remove debug --- .github/workflows/pull_request.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index c6b9161..8307bcc 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -17,18 +17,12 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Setup Dotnet - uses: actions/setup-dotnet@v5.2.0 - - name: Download SCP:SL References uses: Axwabo/scpsl-references-downloader@v1 id: refs with: directory: ${{ env.REFERENCES_PATH }} - - name: List references - run: ls ${{ steps.refs.outputs.target }} - - name: Build env: SL_REFERENCES: ${{ steps.refs.outputs.target }} From bd683e0f65542dff57a5e63234b770148daa48d3 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:05:37 +0200 Subject: [PATCH 34/37] Fix: Workflow --- .github/workflows/nuget.yml | 7 +------ .github/workflows/pull_request.yml | 5 ----- Directory.Build.props | 17 +++++++++++++++++ Directory.Build.targets | 22 ++++++++++++++++++++++ SecretAPI/SecretAPI.csproj | 30 ++++++++++++------------------ 5 files changed, 52 insertions(+), 29 deletions(-) create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml index ff4c76f..ca52a08 100644 --- a/.github/workflows/nuget.yml +++ b/.github/workflows/nuget.yml @@ -9,7 +9,6 @@ jobs: runs-on: ubuntu-latest env: - REFERENCES_PATH: ${{ github.workspace }}/References BUILD_OUTPUT: ${{ github.workspace }}/Output steps: @@ -19,14 +18,10 @@ jobs: - name: Download SCP:SL References uses: Axwabo/scpsl-references-downloader@v1 id: refs - with: - directory: ${{ env.REFERENCES_PATH }} - name: Build - env: - SL_REFERENCES: ${{ steps.refs.outputs.target }} run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} - name: Push NuGet package working-directory: ${{ env.BUILD_OUTPUT }} - run: dotnet nuget push SecretAPI.*.nupkg \ No newline at end of file + run: dotnet nuget push SecretAPI.*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json \ No newline at end of file diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8307bcc..c06c820 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -10,7 +10,6 @@ jobs: runs-on: ubuntu-latest env: - REFERENCES_PATH: ${{ github.workspace }}/References BUILD_OUTPUT: ${{ github.workspace }}/Output steps: @@ -20,12 +19,8 @@ jobs: - name: Download SCP:SL References uses: Axwabo/scpsl-references-downloader@v1 id: refs - with: - directory: ${{ env.REFERENCES_PATH }} - name: Build - env: - SL_REFERENCES: ${{ steps.refs.outputs.target }} run: dotnet build -c Release --output ${{ env.BUILD_OUTPUT }} - name: Upload diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..af589d3 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,17 @@ + + + enable + 3.0.0-beta3 + + + + obvEve + obvEve + + true + + git + https://github.com/obvEve/SecretAPI + MIT + + \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000..38f22b4 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,22 @@ + + + + $(SL_REFERENCES) + $(ReferencePath);$(AssemblySearchPaths) + + + + + $(MSBuildThisFileDirectory)ReferencePath.props.user + + + + + + \ No newline at end of file diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index 54fa7df..10c65bd 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -3,21 +3,15 @@ net48 latest - enable - 3.0.0-beta3 true true true - obvEve SecretAPI API to extend SCP:SL LabAPI - git - https://github.com/obvEve/SecretAPI README.md - MIT @@ -37,18 +31,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + From e07b8e0e98d00a05d192d8c31e9887860de2d3cc Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:15:23 +0200 Subject: [PATCH 35/37] Remove hintpath + use LabApi --- SecretAPI.Examples/SecretAPI.Examples.csproj | 25 +++++++++++--------- SecretAPI/SecretAPI.csproj | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/SecretAPI.Examples/SecretAPI.Examples.csproj b/SecretAPI.Examples/SecretAPI.Examples.csproj index 076e87e..f4209f1 100644 --- a/SecretAPI.Examples/SecretAPI.Examples.csproj +++ b/SecretAPI.Examples/SecretAPI.Examples.csproj @@ -9,23 +9,26 @@ + - - - - - - - - - - - + + + + + + + + + + + + diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index 10c65bd..c70f909 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -32,7 +32,7 @@ - + From 447ad380e31a2f9399c74b3fccd060132709beb6 Mon Sep 17 00:00:00 2001 From: Eve <85962933+obvEve@users.noreply.github.com> Date: Thu, 9 Apr 2026 13:19:15 +0200 Subject: [PATCH 36/37] Use .props more --- Directory.Build.props | 8 ++++++++ SecretAPI.Examples/SecretAPI.Examples.csproj | 9 --------- SecretAPI/SecretAPI.csproj | 8 -------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index af589d3..a0d80bf 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -14,4 +14,12 @@ https://github.com/obvEve/SecretAPI MIT + + + true + recommended + true + ../stylecop.ruleset + True + \ No newline at end of file diff --git a/SecretAPI.Examples/SecretAPI.Examples.csproj b/SecretAPI.Examples/SecretAPI.Examples.csproj index f4209f1..7dbfccb 100644 --- a/SecretAPI.Examples/SecretAPI.Examples.csproj +++ b/SecretAPI.Examples/SecretAPI.Examples.csproj @@ -2,7 +2,6 @@ net48 latest - enable true @@ -30,12 +29,4 @@ - - - true - recommended - true - ../stylecop.ruleset - True - diff --git a/SecretAPI/SecretAPI.csproj b/SecretAPI/SecretAPI.csproj index c70f909..0302f70 100644 --- a/SecretAPI/SecretAPI.csproj +++ b/SecretAPI/SecretAPI.csproj @@ -45,12 +45,4 @@ - - true - recommended - true - ../stylecop.ruleset - True - - From fc6477a7cf37bd984052e39f220c3b221fbd2a8e Mon Sep 17 00:00:00 2001 From: Evelyn <85962933+obvEve@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:46:40 +0200 Subject: [PATCH 37/37] Fix stuff (#91) * Try fix stuff * Fix it finally --- SecretAPI/Attributes/CallOnLoadAttribute.cs | 18 +++----- .../{PrefabDebugging.cs => PrefabDebugger.cs} | 2 +- SecretAPI/Extensions/ReflectionExtensions.cs | 44 +++++++++++++++---- .../Patches/Features/RoundEndIgnorePatch.cs | 21 ++++----- .../Patches/Features/RoundIgnoreCountPatch.cs | 28 ++++-------- 5 files changed, 60 insertions(+), 53 deletions(-) rename SecretAPI/Debugging/{PrefabDebugging.cs => PrefabDebugger.cs} (97%) diff --git a/SecretAPI/Attributes/CallOnLoadAttribute.cs b/SecretAPI/Attributes/CallOnLoadAttribute.cs index 4ece8ce..ab0e265 100644 --- a/SecretAPI/Attributes/CallOnLoadAttribute.cs +++ b/SecretAPI/Attributes/CallOnLoadAttribute.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using SecretAPI.Extensions; using SecretAPI.Features; /// @@ -50,21 +51,14 @@ public static void Load(Assembly? assembly = null) internal static void CallAttributeMethodPriority(Assembly assembly) where TAttribute : Attribute, IPriority { - const BindingFlags methodFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; Dictionary methods = new(); - - // get all types - foreach (Type type in assembly.GetTypes()) + foreach (MethodInfo method in assembly.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { - // get all static methods - foreach (MethodInfo method in type.GetMethods(methodFlags)) - { - TAttribute? attribute = method.GetCustomAttribute(); - if (attribute == null) - continue; + TAttribute? attribute = method.GetCustomAttribute(); + if (attribute == null) + continue; - methods.Add(attribute, method); - } + methods.Add(attribute, method); } foreach (KeyValuePair method in methods.OrderBy(static v => v.Key.Priority)) diff --git a/SecretAPI/Debugging/PrefabDebugging.cs b/SecretAPI/Debugging/PrefabDebugger.cs similarity index 97% rename from SecretAPI/Debugging/PrefabDebugging.cs rename to SecretAPI/Debugging/PrefabDebugger.cs index d82e9f2..dc2a893 100644 --- a/SecretAPI/Debugging/PrefabDebugging.cs +++ b/SecretAPI/Debugging/PrefabDebugger.cs @@ -15,7 +15,7 @@ namespace SecretAPI.Debugging; /// /// Debugs basegame prefabs by logging information about them. /// -internal static class PrefabDebugging +internal static class PrefabDebugger { /// /// Loads the prefab debugging. diff --git a/SecretAPI/Extensions/ReflectionExtensions.cs b/SecretAPI/Extensions/ReflectionExtensions.cs index f367c6e..32767cf 100644 --- a/SecretAPI/Extensions/ReflectionExtensions.cs +++ b/SecretAPI/Extensions/ReflectionExtensions.cs @@ -1,8 +1,10 @@ namespace SecretAPI.Extensions; using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; +using HarmonyLib; /// /// Extensions for reflection. @@ -36,16 +38,42 @@ public static string GetLongFuncName(Type type, MethodInfo method) } /// - /// Copies the properties. + /// Searches through an assembly and returns all based on the provided flags. + /// + /// The assembly to search through. + /// The used for the method search within a . + /// A collection of based on the provided assembly and flags. + public static IEnumerable GetMethods(this Assembly assembly, BindingFlags flags) + => assembly.GetTypes().SelectMany(type => type.GetMethods(flags)); + + /// + /// Gets a nested within a . + /// + /// The containing the needed method. + /// The name of the nested type, this does not need to be exact. + /// The name of the method, this does not need to be exact. + /// The found method, or null if none matched. + public static MethodInfo? GetNestedMethod(this Type type, string nestedTypeName, string methodName) + { + return type.GetNestedTypes(AccessTools.all) + .Where(t => t.Name.Contains(nestedTypeName)) + .SelectMany(t => t.GetMethods(AccessTools.all)) + .FirstOrDefault(m => m.Name.Contains(methodName)); + } + + /// + /// Copies the properties from a onto another instance. /// /// The source of the properties to copy. - /// Where to copy to. - public static void CopyProperties(this object source, object destination) + /// Where the source properties should be copied to, this should match the same as source. + public static void CopyPropertiesTo(this object source, object destination) { - Type destinationType = destination.GetType(); - foreach (PropertyInfo property in source.GetType().GetProperties()) - { - destinationType.GetProperty(property.Name)?.SetValue(destination, property.GetValue(source)); - } + Type type = source.GetType(); + + if (type != destination.GetType()) + throw new InvalidOperationException($"[ReflectionExtensions.CopyPropertiesTo] Source and destination types are mismatched: {type.FullName} | {destination.GetType().FullName}"); + + foreach (PropertyInfo property in type.GetProperties()) + property.SetValue(destination, property.GetValue(source)); } } \ No newline at end of file diff --git a/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs b/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs index d8611b1..0a6dc7a 100644 --- a/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs +++ b/SecretAPI/Patches/Features/RoundEndIgnorePatch.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Reflection.Emit; using HarmonyLib; +using LabApi.Features.Console; using LabApi.Features.Wrappers; using PlayerRoles; using SecretAPI.Attributes; @@ -14,37 +15,33 @@ using SecretAPI.Features; /// -/// Handles patching to implement into . +/// Handles patching to implement into . /// [HarmonyPatchCategory(nameof(PlayerRoundIgnore))] [HarmonyPatch] internal static class RoundEndIgnorePatch { - private const string StateMachine = "<_ProcessServerSideCode>d__58"; + private const string StateMachine = "_ProcessServerSideCode"; private const string MoveNext = "MoveNext"; private const int ReferenceHubLocalIndex = 20; + private const int SkipAdvanceAmount = 4; // amount needed to get to IL_0131: br IL_01bd private static MethodInfo TargetMethod() { - // typeof(RoundSummary).GetNestedTypes(AccessTools.all).ForEach(type => Logger.Debug(type.FullName ?? "NULL")); - Type nestedType = typeof(RoundSummary).GetNestedTypes(AccessTools.all) - .FirstOrDefault(type => type.Name is StateMachine) ?? throw new Exception($"Could not locate state machine for {StateMachine}"); - - // nestedType.GetMethods(AccessTools.all).ForEach(method => Logger.Debug(method.Name)); - MethodInfo moveNextMethod = nestedType.GetMethods(AccessTools.all) - .FirstOrDefault(x => x.Name.Contains(MoveNext)) ?? throw new Exception($"Could not locate {MoveNext} method in state machine"); - - return moveNextMethod; + return typeof(RoundSummary).GetNestedMethod(StateMachine, MoveNext) + ?? throw new Exception($"Could not locate state machine for {StateMachine} | {MoveNext}"); } private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { CodeMatcher matcher = new CodeMatcher(instructions, generator) .MatchEndForward(new CodeMatch(CodeInstruction.Call(typeof(PlayerRolesUtils), nameof(PlayerRolesUtils.GetTeam), [typeof(ReferenceHub)]))) + .Advance(SkipAdvanceAmount) .CreateLabel(out Label skip) + .Advance(-SkipAdvanceAmount) .Insert( new CodeInstruction(OpCodes.Ldloc_S, ReferenceHubLocalIndex), - CodeInstruction.Call(typeof(RoundEndIgnorePatch), nameof(IsPlayerIgnored)), + new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(RoundEndIgnorePatch), nameof(IsPlayerIgnored))), new CodeInstruction(OpCodes.Brtrue_S, skip)); return matcher.InstructionEnumeration(); diff --git a/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs b/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs index 0a1f32e..51b79a1 100644 --- a/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs +++ b/SecretAPI/Patches/Features/RoundIgnoreCountPatch.cs @@ -6,6 +6,7 @@ using System.Reflection; using System.Reflection.Emit; using HarmonyLib; +using LabApi.Features.Console; using LabApi.Features.Wrappers; using Mirror; using SecretAPI.Attributes; @@ -25,14 +26,8 @@ internal static class RoundIgnoreCountPatch private static MethodInfo TargetMethod() { - Type nestedType = typeof(RoundSummary).GetNestedTypes(AccessTools.all) - .FirstOrDefault(currentType => currentType.Name is StateMachine) ?? throw new Exception("Could not locate state machine for RoundSummary::<>c"); - - // nestedType.GetMethods(AccessTools.all).ForEach(method => Logger.Debug(method.Name)); - MethodInfo updateCountMethod = nestedType.GetMethods(AccessTools.all) - .FirstOrDefault(x => x.Name.Contains(UpdateTargetCount)) ?? throw new Exception($"Could not locate {UpdateTargetCount} method in state machine"); - - return updateCountMethod; + return typeof(RoundSummary).GetNestedMethod(StateMachine, UpdateTargetCount) + ?? throw new Exception($"Could not locate state machine for {StateMachine} | {UpdateTargetCount}"); } private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) @@ -41,21 +36,14 @@ private static IEnumerable Transpiler(IEnumerable Player.Get(hub).RoundIgnoreStatus.HasFlagFast(RoundIgnoreStatus.ScpTargetCount); } \ No newline at end of file