diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/ButtonSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/ButtonSetting.cs index b150e01a44..5293670cfe 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/ButtonSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/ButtonSetting.cs @@ -28,10 +28,14 @@ public class ButtonSetting : SettingBase, IWrapper /// /// /// - public ButtonSetting(int id, string label, string buttonText, float holdTime = 0.0f, string hintDescription = null, HeaderSetting header = null, Action onChanged = null) + /// + /// + public ButtonSetting(int id, string label, string buttonText, float holdTime = 0.0f, string hintDescription = null, HeaderSetting header = null, Action onChanged = null, Predicate playerSync = null, int priority = 0) : base(new SSButton(id, label, buttonText, holdTime, hintDescription), header, onChanged) { Base = (SSButton)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/DropdownSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/DropdownSetting.cs index 88886839bd..706902d13d 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/DropdownSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/DropdownSetting.cs @@ -30,6 +30,8 @@ public class DropdownSetting : SettingBase, IWrapper /// /// /// + /// + /// public DropdownSetting( int id, string label, @@ -38,10 +40,14 @@ public DropdownSetting( SSDropdownSetting.DropdownEntryType dropdownEntryType = SSDropdownSetting.DropdownEntryType.Regular, string hintDescription = null, HeaderSetting header = null, - Action onChanged = null) + Action onChanged = null, + Predicate playerSync = null, + int priority = 0) : base(new SSDropdownSetting(id, label, options.ToArray(), defaultOptionIndex, dropdownEntryType, hintDescription), header, onChanged) { Base = (SSDropdownSetting)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/HeaderSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/HeaderSetting.cs index 78fd4ee32d..dad7666039 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/HeaderSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/HeaderSetting.cs @@ -7,6 +7,8 @@ namespace Exiled.API.Features.Core.UserSettings { + using System; + using Exiled.API.Interfaces; using global::UserSettings.ServerSpecific; @@ -21,10 +23,14 @@ public class HeaderSetting : SettingBase, IWrapper /// /// /// - public HeaderSetting(string name, string hintDescription = "", bool paddling = false) + /// + /// + public HeaderSetting(string name, string hintDescription = "", bool paddling = false, Predicate playerSync = null, int priority = 0) : this(new SSGroupHeader(name, paddling, hintDescription)) { Base = (SSGroupHeader)base.Base; + PlayerSync = playerSync; + Priority = priority; Base.SetId(null, name); } @@ -38,6 +44,8 @@ internal HeaderSetting(SSGroupHeader settingBase) { Base = settingBase; Base.SetId(null, settingBase.Label); + PlayerSync = null; + Priority = 0; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/KeybindSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/KeybindSetting.cs index cfb6b3bbca..d2fa5a031a 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/KeybindSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/KeybindSetting.cs @@ -28,10 +28,14 @@ public class KeybindSetting : SettingBase, IWrapper /// /// /// - public KeybindSetting(int id, string label, KeyCode suggested, bool preventInteractionOnGUI = false, string hintDescription = "", HeaderSetting header = null, Action onChanged = null) + /// + /// + public KeybindSetting(int id, string label, KeyCode suggested, bool preventInteractionOnGUI = false, string hintDescription = "", HeaderSetting header = null, Action onChanged = null, Predicate playerSync = null, int priority = 0) : base(new SSKeybindSetting(id, label, suggested, preventInteractionOnGUI, hintDescription), header, onChanged) { Base = (SSKeybindSetting)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs b/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs index b27ae1fc78..526a48901c 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs @@ -37,12 +37,17 @@ public class SettingBase : TypeCastObject, IWrapperA instance. /// /// - internal SettingBase(ServerSpecificSettingBase settingBase, HeaderSetting header, Action onChanged) + /// + /// + internal SettingBase(ServerSpecificSettingBase settingBase, HeaderSetting header, Action onChanged, Predicate playerSync = null, int priority = 0) { Base = settingBase; Header = header; OnChanged = onChanged; + + PlayerSync = playerSync; + Priority = priority; } /// @@ -52,6 +57,8 @@ internal SettingBase(ServerSpecificSettingBase settingBase, HeaderSetting header internal SettingBase(ServerSpecificSettingBase settingBase) { Base = settingBase; + Priority = 0; + PlayerSync = null; if (OriginalDefinition != null) { @@ -74,9 +81,9 @@ public static IReadOnlyDictionary> Synce public static IReadOnlyCollection List => Settings; /// - /// Gets or sets the predicate for syncing this setting when a player joins. + /// Gets or sets the predicate for who receives this setting. /// - public static Predicate SyncOnJoin { get; set; } + public Predicate PlayerSync { get; set; } /// public ServerSpecificSettingBase Base { get; } @@ -90,6 +97,11 @@ public int Id set => Base.SetId(value, string.Empty); } + /// + /// Gets or sets the priority of this setting. + /// + public int Priority { get; set; } + /// /// Gets or sets the label of this setting. /// @@ -197,7 +209,13 @@ public static T Create(ServerSpecificSettingBase settingBase) /// /// Syncs setting with all players. /// - public static void SendToAll() => ServerSpecificSettingsSync.SendToAll(); + public static void SendToAll() + { + foreach (Player player in Player.List) + { + SendToPlayer(player); + } + } /// /// Syncs setting with all players according to the specified predicate. @@ -216,7 +234,12 @@ public static void SendToAll(Func predicate) /// Syncs setting with the specified target. /// /// Target player. - public static void SendToPlayer(Player player) => ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub); + public static void SendToPlayer(Player player) + { + ServerSpecificSettingBase[] settings = GetGroupedSettings().Where(setting => setting.PlayerSync == null || setting.PlayerSync(player)).Select(setting => setting.Base).ToArray(); + if (settings.Any()) + ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, settings); + } /// /// Syncs specific settings with the specified target. @@ -224,16 +247,15 @@ public static void SendToAll(Func predicate) /// Target player. /// Settings to send to the player. public static void SendToPlayer(Player player, IEnumerable settings) => - ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, settings.Select(setting => setting.Base).ToArray()); + ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, settings.OrderByDescending(setting => setting.Priority).Select(setting => setting.Base).ToArray()); /// /// Registers all settings from the specified collection. /// /// A collection of settings to register. - /// A requirement to meet when sending settings to players. /// A of instances that were successfully registered. /// This method is used to sync new settings with players. - public static IEnumerable Register(IEnumerable settings, Func predicate = null) + public static IEnumerable Register(IEnumerable settings) { IEnumerable> grouped = settings.Where(s => s != null).GroupBy(s => s.Header); @@ -251,10 +273,7 @@ public static IEnumerable Register(IEnumerable setting ServerSpecificSettingsSync.DefinedSettings = (ServerSpecificSettingsSync.DefinedSettings ?? Array.Empty()).Concat(result.Select(s => s.Base)).ToArray(); Settings.AddRange(result); - if (predicate == null) - SendToAll(); - else - SendToAll(predicate); + SendToAll(); return result; } @@ -262,21 +281,17 @@ public static IEnumerable Register(IEnumerable setting /// /// Removes settings from players. /// - /// Determines which players will receive this update. /// Settings to remove. If null, all settings will be removed. /// A of instances that were successfully removed. /// This method is used to unsync settings from players. Using it with provides an opportunity to update synced settings. - public static IEnumerable Unregister(Func predicate = null, IEnumerable settings = null) + public static IEnumerable Unregister(IEnumerable settings = null) { List list = ListPool.Pool.Get(ServerSpecificSettingsSync.DefinedSettings); List list2 = new((settings ?? Settings).Where(setting => list.Remove(setting.Base))); ServerSpecificSettingsSync.DefinedSettings = list.ToArray(); - if (predicate == null) - SendToAll(); - else - SendToAll(predicate); + SendToAll(); ListPool.Pool.Return(list); @@ -337,5 +352,30 @@ internal static void OnSettingUpdated(ReferenceHub hub, ServerSpecificSettingBas setting.OriginalDefinition?.OnChanged?.Invoke(player, setting); } + + /// + /// Internal method that gets all settings sorted by priority and grouped by header. + /// + /// All registered settings sorted by priority and grouped by header. + internal static IEnumerable GetGroupedSettings() + { + List settings = Settings.Where(setting => setting.Header == null).OrderByDescending(setting => setting.Priority).ToList(); + List> groups = settings.GroupBy(setting => setting.Header).Where(group => group.Key != null).ToList(); + + // look this was the best I could come up with :sob: + while (groups.Any()) + { + foreach (IGrouping group in groups.ToArray()) + { + int index = settings.IndexOf(group.Key); + if (index == -1) + continue; + settings.InsertRange(index, group); + groups.Remove(group); + } + } + + return settings; + } } } diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/SliderSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/SliderSetting.cs index 66d88174d9..a5c9ec2f37 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/SliderSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/SliderSetting.cs @@ -29,10 +29,14 @@ public class SliderSetting : SettingBase, IWrapper /// /// /// - public SliderSetting(int id, string label, float minValue, float maxValue, float defaultValue, bool isInteger = false, string stringFormat = "0.##", string displayFormat = "{0}", string hintDescription = null) + /// + /// + public SliderSetting(int id, string label, float minValue, float maxValue, float defaultValue, bool isInteger = false, string stringFormat = "0.##", string displayFormat = "{0}", string hintDescription = null, Predicate playerSync = null, int priority = 0) : this(new SSSliderSetting(id, label, minValue, maxValue, defaultValue, isInteger, stringFormat, displayFormat, hintDescription)) { Base = (SSSliderSetting)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/TextInputSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/TextInputSetting.cs index 69c7875ad1..0772412e26 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/TextInputSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/TextInputSetting.cs @@ -28,6 +28,8 @@ public class TextInputSetting : SettingBase, IWrapper /// /// /// + /// + /// public TextInputSetting( int id, string label, @@ -35,10 +37,14 @@ public TextInputSetting( TextAlignmentOptions alignment = TextAlignmentOptions.TopLeft, string hintDescription = null, HeaderSetting header = null, - Action onChanged = null) + Action onChanged = null, + Predicate playerSync = null, + int priority = 0) : base(new SSTextArea(id, label, foldoutMode, hintDescription, alignment), header, onChanged) { Base = (SSTextArea)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/TwoButtonsSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/TwoButtonsSetting.cs index 52c596ae76..6d361d23ee 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/TwoButtonsSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/TwoButtonsSetting.cs @@ -28,10 +28,14 @@ public class TwoButtonsSetting : SettingBase, IWrapper /// /// /// - public TwoButtonsSetting(int id, string label, string firstOption, string secondOption, bool defaultIsSecond = false, string hintDescription = "", HeaderSetting header = null, Action onChanged = null) + /// + /// + public TwoButtonsSetting(int id, string label, string firstOption, string secondOption, bool defaultIsSecond = false, string hintDescription = "", HeaderSetting header = null, Action onChanged = null, Predicate playerSync = null, int priority = 0) : base(new SSTwoButtonsSetting(id, label, firstOption, secondOption, defaultIsSecond, hintDescription), header, onChanged) { Base = (SSTwoButtonsSetting)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.API/Features/Core/UserSettings/UserTextInputSetting.cs b/EXILED/Exiled.API/Features/Core/UserSettings/UserTextInputSetting.cs index 8944558e03..d04bfa1c7e 100644 --- a/EXILED/Exiled.API/Features/Core/UserSettings/UserTextInputSetting.cs +++ b/EXILED/Exiled.API/Features/Core/UserSettings/UserTextInputSetting.cs @@ -27,10 +27,14 @@ public class UserTextInputSetting : SettingBase, IWrapper /// /// /// /// - public UserTextInputSetting(int id, string label, string placeHolder = "", int characterLimit = 64, TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, string hintDescription = null) + /// + /// + public UserTextInputSetting(int id, string label, string placeHolder = "", int characterLimit = 64, TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, string hintDescription = null, Predicate playerSync = null, int priority = 0) : this(new SSPlaintextSetting(id, label, placeHolder, characterLimit, contentType, hintDescription)) { Base = (SSPlaintextSetting)base.Base; + PlayerSync = playerSync; + Priority = priority; } /// diff --git a/EXILED/Exiled.Events/Handlers/Internal/Round.cs b/EXILED/Exiled.Events/Handlers/Internal/Round.cs index 99212af062..fb9db97c68 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/Round.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/Round.cs @@ -8,6 +8,7 @@ namespace Exiled.Events.Handlers.Internal { using System.Collections.Generic; + using System.Collections.ObjectModel; using System.Linq; using CentralAuth; @@ -97,8 +98,7 @@ public static void OnVerified(VerifiedEventArgs ev) { RoleAssigner.CheckLateJoin(ev.Player.ReferenceHub, ClientInstanceMode.ReadyClient); - if (SettingBase.SyncOnJoin != null && SettingBase.SyncOnJoin(ev.Player)) - SettingBase.SendToPlayer(ev.Player); + SettingBase.SendToPlayer(ev.Player); // TODO: Remove if this has been fixed for https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/52 foreach (Room room in Room.List.Where(current => current.AreLightsOff))