Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ public class ButtonSetting : SettingBase, IWrapper<SSButton>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
/// <param name="header"><inheritdoc cref="SettingBase.Header"/></param>
/// <param name="onChanged"><inheritdoc cref="SettingBase.OnChanged"/></param>
public ButtonSetting(int id, string label, string buttonText, float holdTime = 0.0f, string hintDescription = null, HeaderSetting header = null, Action<Player, SettingBase> onChanged = null)
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public ButtonSetting(int id, string label, string buttonText, float holdTime = 0.0f, string hintDescription = null, HeaderSetting header = null, Action<Player, SettingBase> onChanged = null, Predicate<Player> playerSync = null, int priority = 0)
: base(new SSButton(id, label, buttonText, holdTime, hintDescription), header, onChanged)
{
Base = (SSButton)base.Base;
PlayerSync = playerSync;
Priority = priority;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public class DropdownSetting : SettingBase, IWrapper<SSDropdownSetting>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
/// <param name="header"><inheritdoc cref="SettingBase.Header"/></param>
/// <param name="onChanged"><inheritdoc cref="SettingBase.OnChanged"/></param>
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public DropdownSetting(
int id,
string label,
Expand All @@ -38,10 +40,14 @@ public DropdownSetting(
SSDropdownSetting.DropdownEntryType dropdownEntryType = SSDropdownSetting.DropdownEntryType.Regular,
string hintDescription = null,
HeaderSetting header = null,
Action<Player, SettingBase> onChanged = null)
Action<Player, SettingBase> onChanged = null,
Predicate<Player> 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;
}

/// <summary>
Expand Down
10 changes: 9 additions & 1 deletion EXILED/Exiled.API/Features/Core/UserSettings/HeaderSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace Exiled.API.Features.Core.UserSettings
{
using System;

using Exiled.API.Interfaces;
using global::UserSettings.ServerSpecific;

Expand All @@ -21,10 +23,14 @@ public class HeaderSetting : SettingBase, IWrapper<SSGroupHeader>
/// <param name="name"><inheritdoc cref="SettingBase.Label"/></param>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
/// <param name="paddling"><inheritdoc cref="ReducedPaddling"/></param>
public HeaderSetting(string name, string hintDescription = "", bool paddling = false)
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public HeaderSetting(string name, string hintDescription = "", bool paddling = false, Predicate<Player> playerSync = null, int priority = 0)
: this(new SSGroupHeader(name, paddling, hintDescription))
{
Base = (SSGroupHeader)base.Base;
PlayerSync = playerSync;
Priority = priority;

Base.SetId(null, name);
}
Expand All @@ -38,6 +44,8 @@ internal HeaderSetting(SSGroupHeader settingBase)
{
Base = settingBase;
Base.SetId(null, settingBase.Label);
PlayerSync = null;
Priority = 0;
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ public class KeybindSetting : SettingBase, IWrapper<SSKeybindSetting>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
/// <param name="header"><inheritdoc cref="SettingBase.Header"/></param>
/// <param name="onChanged"><inheritdoc cref="SettingBase.OnChanged"/></param>
public KeybindSetting(int id, string label, KeyCode suggested, bool preventInteractionOnGUI = false, string hintDescription = "", HeaderSetting header = null, Action<Player, SettingBase> onChanged = null)
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public KeybindSetting(int id, string label, KeyCode suggested, bool preventInteractionOnGUI = false, string hintDescription = "", HeaderSetting header = null, Action<Player, SettingBase> onChanged = null, Predicate<Player> playerSync = null, int priority = 0)
: base(new SSKeybindSetting(id, label, suggested, preventInteractionOnGUI, hintDescription), header, onChanged)
{
Base = (SSKeybindSetting)base.Base;
PlayerSync = playerSync;
Priority = priority;
}

/// <summary>
Expand Down
76 changes: 58 additions & 18 deletions EXILED/Exiled.API/Features/Core/UserSettings/SettingBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,17 @@ public class SettingBase : TypeCastObject<SettingBase>, IWrapper<ServerSpecificS
/// <param name="settingBase">A <see cref="ServerSpecificSettingBase"/> instance.</param>
/// <param name="header"><inheritdoc cref="Header"/></param>
/// <param name="onChanged"><inheritdoc cref="OnChanged"/></param>
internal SettingBase(ServerSpecificSettingBase settingBase, HeaderSetting header, Action<Player, SettingBase> onChanged)
/// <param name="playerSync"><inheritdoc cref="PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="Priority"/></param>
internal SettingBase(ServerSpecificSettingBase settingBase, HeaderSetting header, Action<Player, SettingBase> onChanged, Predicate<Player> playerSync = null, int priority = 0)
{
Base = settingBase;

Header = header;
OnChanged = onChanged;

PlayerSync = playerSync;
Priority = priority;
}

/// <summary>
Expand All @@ -52,6 +57,8 @@ internal SettingBase(ServerSpecificSettingBase settingBase, HeaderSetting header
internal SettingBase(ServerSpecificSettingBase settingBase)
{
Base = settingBase;
Priority = 0;
PlayerSync = null;

if (OriginalDefinition != null)
{
Expand All @@ -74,9 +81,9 @@ public static IReadOnlyDictionary<Player, ReadOnlyCollection<SettingBase>> Synce
public static IReadOnlyCollection<SettingBase> List => Settings;

/// <summary>
/// Gets or sets the predicate for syncing this setting when a player joins.
/// Gets or sets the predicate for who receives this setting.
/// </summary>
public static Predicate<Player> SyncOnJoin { get; set; }
public Predicate<Player> PlayerSync { get; set; }

/// <inheritdoc/>
public ServerSpecificSettingBase Base { get; }
Expand All @@ -90,6 +97,11 @@ public int Id
set => Base.SetId(value, string.Empty);
}

/// <summary>
/// Gets or sets the priority of this setting.
/// </summary>
public int Priority { get; set; }

/// <summary>
/// Gets or sets the label of this setting.
/// </summary>
Expand Down Expand Up @@ -197,7 +209,13 @@ public static T Create<T>(ServerSpecificSettingBase settingBase)
/// <summary>
/// Syncs setting with all players.
/// </summary>
public static void SendToAll() => ServerSpecificSettingsSync.SendToAll();
public static void SendToAll()
{
foreach (Player player in Player.List)
{
SendToPlayer(player);
}
}

/// <summary>
/// Syncs setting with all players according to the specified predicate.
Expand All @@ -216,24 +234,28 @@ public static void SendToAll(Func<Player, bool> predicate)
/// Syncs setting with the specified target.
/// </summary>
/// <param name="player">Target player.</param>
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);
}

/// <summary>
/// Syncs specific settings with the specified target.
/// </summary>
/// <param name="player">Target player.</param>
/// <param name="settings">Settings to send to the player.</param>
public static void SendToPlayer(Player player, IEnumerable<SettingBase> 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());

/// <summary>
/// Registers all settings from the specified collection.
/// </summary>
/// <param name="settings">A collection of settings to register.</param>
/// <param name="predicate">A requirement to meet when sending settings to players.</param>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="SettingBase"/> instances that were successfully registered.</returns>
/// <remarks>This method is used to sync new settings with players.</remarks>
public static IEnumerable<SettingBase> Register(IEnumerable<SettingBase> settings, Func<Player, bool> predicate = null)
public static IEnumerable<SettingBase> Register(IEnumerable<SettingBase> settings)
{
IEnumerable<IGrouping<HeaderSetting, SettingBase>> grouped = settings.Where(s => s != null).GroupBy(s => s.Header);

Expand All @@ -251,32 +273,25 @@ public static IEnumerable<SettingBase> Register(IEnumerable<SettingBase> setting
ServerSpecificSettingsSync.DefinedSettings = (ServerSpecificSettingsSync.DefinedSettings ?? Array.Empty<ServerSpecificSettingBase>()).Concat(result.Select(s => s.Base)).ToArray();
Settings.AddRange(result);

if (predicate == null)
SendToAll();
else
SendToAll(predicate);
SendToAll();

return result;
}

/// <summary>
/// Removes settings from players.
/// </summary>
/// <param name="predicate">Determines which players will receive this update.</param>
/// <param name="settings">Settings to remove. If <c>null</c>, all settings will be removed.</param>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="SettingBase"/> instances that were successfully removed.</returns>
/// <remarks>This method is used to unsync settings from players. Using it with <see cref="Register"/> provides an opportunity to update synced settings.</remarks>
public static IEnumerable<SettingBase> Unregister(Func<Player, bool> predicate = null, IEnumerable<SettingBase> settings = null)
public static IEnumerable<SettingBase> Unregister(IEnumerable<SettingBase> settings = null)
{
List<ServerSpecificSettingBase> list = ListPool<ServerSpecificSettingBase>.Pool.Get(ServerSpecificSettingsSync.DefinedSettings);
List<SettingBase> list2 = new((settings ?? Settings).Where(setting => list.Remove(setting.Base)));

ServerSpecificSettingsSync.DefinedSettings = list.ToArray();

if (predicate == null)
SendToAll();
else
SendToAll(predicate);
SendToAll();

ListPool<ServerSpecificSettingBase>.Pool.Return(list);

Expand Down Expand Up @@ -337,5 +352,30 @@ internal static void OnSettingUpdated(ReferenceHub hub, ServerSpecificSettingBas

setting.OriginalDefinition?.OnChanged?.Invoke(player, setting);
}

/// <summary>
/// Internal method that gets all settings sorted by priority and grouped by header.
/// </summary>
/// <returns>All registered settings sorted by priority and grouped by header.</returns>
internal static IEnumerable<SettingBase> GetGroupedSettings()
{
List<SettingBase> settings = Settings.Where(setting => setting.Header == null).OrderByDescending(setting => setting.Priority).ToList();
List<IGrouping<HeaderSetting, SettingBase>> 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<HeaderSetting, SettingBase> group in groups.ToArray())
{
int index = settings.IndexOf(group.Key);
if (index == -1)
continue;
settings.InsertRange(index, group);
groups.Remove(group);
}
}

return settings;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ public class SliderSetting : SettingBase, IWrapper<SSSliderSetting>
/// <param name="stringFormat"><inheritdoc cref="StringFormat"/></param>
/// <param name="displayFormat"><inheritdoc cref="DisplayFormat"/></param>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
public SliderSetting(int id, string label, float minValue, float maxValue, float defaultValue, bool isInteger = false, string stringFormat = "0.##", string displayFormat = "{0}", string hintDescription = null)
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
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<Player> 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;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,23 @@ public class TextInputSetting : SettingBase, IWrapper<SSTextArea>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
/// <param name="header"><inheritdoc cref="SettingBase.Header"/></param>
/// <param name="onChanged"><inheritdoc cref="SettingBase.OnChanged"/></param>
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public TextInputSetting(
int id,
string label,
SSTextArea.FoldoutMode foldoutMode = SSTextArea.FoldoutMode.NotCollapsable,
TextAlignmentOptions alignment = TextAlignmentOptions.TopLeft,
string hintDescription = null,
HeaderSetting header = null,
Action<Player, SettingBase> onChanged = null)
Action<Player, SettingBase> onChanged = null,
Predicate<Player> playerSync = null,
int priority = 0)
: base(new SSTextArea(id, label, foldoutMode, hintDescription, alignment), header, onChanged)
{
Base = (SSTextArea)base.Base;
PlayerSync = playerSync;
Priority = priority;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ public class TwoButtonsSetting : SettingBase, IWrapper<SSTwoButtonsSetting>
/// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
/// <param name="header"><inheritdoc cref="SettingBase.Header"/></param>
/// <param name="onChanged"><inheritdoc cref="SettingBase.OnChanged"/></param>
public TwoButtonsSetting(int id, string label, string firstOption, string secondOption, bool defaultIsSecond = false, string hintDescription = "", HeaderSetting header = null, Action<Player, SettingBase> onChanged = null)
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public TwoButtonsSetting(int id, string label, string firstOption, string secondOption, bool defaultIsSecond = false, string hintDescription = "", HeaderSetting header = null, Action<Player, SettingBase> onChanged = null, Predicate<Player> playerSync = null, int priority = 0)
: base(new SSTwoButtonsSetting(id, label, firstOption, secondOption, defaultIsSecond, hintDescription), header, onChanged)
{
Base = (SSTwoButtonsSetting)base.Base;
PlayerSync = playerSync;
Priority = priority;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ public class UserTextInputSetting : SettingBase, IWrapper<SSPlaintextSetting>
/// <param name="characterLimit"><inheritdoc cref="CharacterLimit"/></param>
/// <param name="contentType"><inheritdoc cref="ContentType"/></param>
/// /// <param name="hintDescription"><inheritdoc cref="SettingBase.HintDescription"/></param>
public UserTextInputSetting(int id, string label, string placeHolder = "", int characterLimit = 64, TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, string hintDescription = null)
/// <param name="playerSync"><inheritdoc cref="SettingBase.PlayerSync"/></param>
/// <param name="priority"><inheritdoc cref="SettingBase.Priority"/></param>
public UserTextInputSetting(int id, string label, string placeHolder = "", int characterLimit = 64, TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard, string hintDescription = null, Predicate<Player> playerSync = null, int priority = 0)
: this(new SSPlaintextSetting(id, label, placeHolder, characterLimit, contentType, hintDescription))
{
Base = (SSPlaintextSetting)base.Base;
PlayerSync = playerSync;
Priority = priority;
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions EXILED/Exiled.Events/Handlers/Internal/Round.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Exiled.Events.Handlers.Internal
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

using CentralAuth;
Expand Down Expand Up @@ -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))
Expand Down