Skip to content
Merged
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
2 changes: 1 addition & 1 deletion SecretAPI.Examples/ExampleEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class ExampleEntry : Plugin
private Harmony? harmony;

/// <inheritdoc/>
public override string Name { get; } = "SecretAPI.Example";
public override string Name { get; } = "SecretAPI.Examples";

/// <inheritdoc/>
public override string Description { get; } = "An example plugin";
Expand Down
16 changes: 13 additions & 3 deletions SecretAPI.Examples/Settings/ExampleDropdownSetting.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace SecretAPI.Examples.Settings
{
using LabApi.Features.Console;
using LabApi.Features.Wrappers;
using LabApi.Features.Permissions;
using SecretAPI.Features.UserSettings;

/// <summary>
Expand All @@ -10,6 +10,7 @@
public class ExampleDropdownSetting : CustomDropdownSetting
{
private static string[] exampleOptions = ["hi", "test", "yum", "fish", "nugget"];
private static string[] exampleSupporterOptions = ["bucket", "lava", "wanted", "globe"];

/// <summary>
/// Initializes a new instance of the <see cref="ExampleDropdownSetting"/> class.
Expand All @@ -26,9 +27,18 @@ public ExampleDropdownSetting()
protected override CustomSetting CreateDuplicate() => new ExampleDropdownSetting();

/// <inheritdoc/>
protected override void HandleSettingUpdate(Player player)
protected override void UpdatePlayerSetting()
{
Logger.Info(SelectedOption);
if (KnownOwner == null || !KnownOwner.HasAnyPermission("example.supporter"))
return;

Options = exampleSupporterOptions;
}

/// <inheritdoc/>
protected override void HandleSettingUpdate()
{
Logger.Info($"{KnownOwner?.DisplayName ?? "null reference"} selected {SelectedOption} (Index {ValidatedSelectedIndex}/{Options.Length})");
}
}
}
4 changes: 2 additions & 2 deletions SecretAPI.Examples/Settings/ExampleKeybindSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public ExampleKeybindSetting()
protected override CustomSetting CreateDuplicate() => new ExampleKeybindSetting();

/// <inheritdoc />
protected override void HandleSettingUpdate(Player player)
protected override void HandleSettingUpdate()
{
if (!IsPressed)
return;

player.Kill();
KnownOwner?.Kill();
}
}
}
9 changes: 7 additions & 2 deletions SecretAPI/Features/UserSettings/CustomDropdownSetting.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace SecretAPI.Features.UserSettings
{
using System;
using System.Linq;
using global::UserSettings.ServerSpecific;

/// <summary>
Expand Down Expand Up @@ -41,19 +42,23 @@ protected CustomDropdownSetting(
/// <inheritdoc/>
public new SSDropdownSetting Base { get; }

/// <summary>
/// Gets the selected index after validation.
/// </summary>
public int ValidatedSelectedIndex => Math.Clamp(Base.SyncSelectionIndexRaw, 0, Options.Length - 1);

/// <summary>
/// Gets or sets the options.
/// </summary>
public string[] Options
{
get => Base.Options;
[Obsolete("Setting this value is not currently supported.")]
set => Base.Options = value;
}

/// <summary>
/// Gets the selected option as string.
/// </summary>
public string SelectedOption => Base.SyncSelectionText;
public string SelectedOption => Options[ValidatedSelectedIndex];
}
}
19 changes: 0 additions & 19 deletions SecretAPI/Features/UserSettings/CustomKeybindSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,6 @@ protected CustomKeybindSetting(SSKeybindSetting setting)
Base = setting;
}

/// <summary>
/// Initializes a new instance of the <see cref="CustomKeybindSetting"/> class.
/// </summary>
/// <param name="id">The ID of the setting.</param>
/// <param name="label">The setting's label.</param>
/// <param name="suggestedKey">The suggested key.</param>
/// <param name="preventInteractionOnGui">Whether to prevent interaction in a GUI.</param>
/// <param name="hint">The hint to show.</param>
[Obsolete("Use CustomKeybindSetting(int?, string, KeyCode, bool, bool, string?)")]
protected CustomKeybindSetting(
int? id,
string label,
KeyCode suggestedKey = KeyCode.None,
bool preventInteractionOnGui = true,
string? hint = null)
: this(id, label, suggestedKey, preventInteractionOnGui, true, hint)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="CustomKeybindSetting"/> class.
/// </summary>
Expand Down
129 changes: 83 additions & 46 deletions SecretAPI/Features/UserSettings/CustomSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using LabApi.Features.Console;
using LabApi.Features.Wrappers;
using Mirror;
using NorthwoodLib.Pools;
using SecretAPI.Extensions;

/// <summary>
Expand Down Expand Up @@ -53,30 +54,26 @@ protected CustomSetting(ServerSpecificSettingBase setting)
/// <inheritdoc />
public ServerSpecificSettingBase Base { get; }

/// <summary>
/// Gets the known owner.
/// </summary>
/// <remarks>This is null on the original object .</remarks>
public Player? KnownOwner { get; private set; }

/// <summary>
/// Gets the <see cref="CustomHeader"/> of the setting.
/// </summary>
public abstract CustomHeader Header { get; }

/// <summary>
/// Gets or sets the current label.
/// Gets the current label.
/// </summary>
public string Label
{
get => Base.Label;
[Obsolete("Should not be set after creation.")]
set => Base.Label = value;
}
public string Label => Base.Label;

/// <summary>
/// Gets or sets the current id.
/// Gets the current id.
/// </summary>
public int Id
{
get => Base.SettingId;
[Obsolete("Should not be set after creation.")]
set => Base.SettingId = value;
}
public int Id => Base.SettingId;

/// <summary>
/// Registers a collection of settings.
Expand All @@ -91,15 +88,16 @@ public int Id
public static void Register(IEnumerable<CustomSetting> settings) => CustomSettings.AddRange(settings);

/// <summary>
/// Tries to get player specific setting.
/// Unregisters collection of settings.
/// </summary>
/// <param name="player">The player to get settings of.</param>
/// <param name="setting">The setting found.</param>
/// <typeparam name="T">The setting type to find.</typeparam>
/// <returns>Whether setting was found.</returns>
[Obsolete("Use TryGetPlayerSetting<TSetting>(Player, out TSetting)")]
public static bool TryGet<T>(Player player, [NotNullWhen(true)] out T? setting)
where T : CustomSetting => TryGetPlayerSetting<T>(player, out setting);
/// <param name="settings">The settings to unregister.</param>
public static void UnRegister(params CustomSetting[] settings) => CustomSettings.RemoveAll(s => settings.Contains(s));

/// <summary>
/// Unregisters a collection of settings.
/// </summary>
/// <param name="settings">The settings to unregister.</param>
public static void UnRegister(IEnumerable<CustomSetting> settings) => CustomSettings.RemoveAll(s => settings.Contains(s));

/// <summary>
/// Tries to get player specific setting.
Expand Down Expand Up @@ -128,6 +126,18 @@ public static bool TryGetPlayerSetting<TSetting>(Player player, [NotNullWhen(tru
return false;
}

/// <summary>
/// Gets a player's <see cref="CustomSetting"/>.
/// </summary>
/// <param name="id">The ID of the setting.</param>
/// <param name="player">The player of which to get the setting from.</param>
/// <typeparam name="T">The setting class to check for.</typeparam>
/// <returns>The found <see cref="CustomSetting"/> matching the params, otherwise null.</returns>
public static T? GetPlayerSetting<T>(int id, Player player)
where T : CustomSetting => PlayerSettings.TryGetValue(player, out List<CustomSetting> settings)
? settings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T
: null;

/// <summary>
/// Gets a <see cref="CustomSetting"/>, used for validation.
/// </summary>
Expand All @@ -147,14 +157,14 @@ public static bool TryGetPlayerSetting<TSetting>(Player player, [NotNullWhen(tru
where T : CustomSetting => CustomSettings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T;

/// <summary>
/// Gets a player's <see cref="CustomSetting"/>.
/// Resyncs all settings to all players.
/// </summary>
/// <param name="id">The ID of the setting.</param>
/// <param name="player">The player of which to get the setting from.</param>
/// <typeparam name="T">The setting class to check for.</typeparam>
/// <returns>The found <see cref="CustomSetting"/> matching the params, otherwise null.</returns>
public static T? GetPlayerSetting<T>(int id, Player player)
where T : CustomSetting => PlayerSettings.TryGetValue(player, out List<CustomSetting> settings) ? settings.FirstOrDefault(s => s.Base.SettingId == id && s.GetType() == typeof(T)) as T : null;
/// <param name="version">The version of the setting. If null will use <see cref="ServerSpecificSettingsSync.Version"/>.</param>
public static void ResyncServer(int? version = null)
{
foreach (Player player in Player.ReadyList)
SendSettingsToPlayer(player, version);
}

/// <summary>
/// Updates the settings of a player based on <see cref="CanView"/>.
Expand All @@ -164,24 +174,43 @@ public static bool TryGetPlayerSetting<TSetting>(Player player, [NotNullWhen(tru
/// <remarks>This will be automatically called on <see cref="PlayerEvents.Joined"/> and <see cref="PlayerEvents.GroupChanged"/>.</remarks>
public static void SendSettingsToPlayer(Player player, int? version = null)
{
version ??= ServerSpecificSettingsSync.Version;
if (player.IsHost)
return;

IEnumerable<CustomSetting> hasAccess = CustomSettings.Where(s => s.CanView(player));
List<ServerSpecificSettingBase> ordered = [];
foreach (IGrouping<CustomHeader, CustomSetting> grouping in hasAccess.GroupBy(setting => setting.Header))
List<CustomSetting> playerSettings = ListPool<CustomSetting>.Shared.Rent();
foreach (CustomSetting setting in CustomSettings)
{
ordered.Add(grouping.Key.Base);
ordered.AddRange(grouping.Select(setting => setting.Base));
if (!setting.CanView(player))
continue;

CustomSetting playerSpecific = EnsurePlayerSpecificSetting(player, setting);
playerSpecific.UpdatePlayerSetting();
playerSettings.Add(playerSpecific);
}

// force update stuff like CustomTextAreaSetting to be in PlayerSettings
foreach (CustomSetting setting in hasAccess)
EnsurePlayerSpecificSetting(player, setting);
List<ServerSpecificSettingBase> ordered = ListPool<ServerSpecificSettingBase>.Shared.Rent();
foreach (IGrouping<CustomHeader, CustomSetting> grouping in playerSettings.GroupBy(static setting => setting.Header))
{
ordered.Add(grouping.Key.Base);
ordered.AddRange(grouping.Select(static setting => setting.Base));
}

if (ServerSpecificSettingsSync.DefinedSettings != null)
ordered.AddRange(ServerSpecificSettingsSync.DefinedSettings);

ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, [.. ordered], version);
ServerSpecificSettingsSync.SendToPlayer(player.ReferenceHub, ordered.ToArray(), version);

ListPool<CustomSetting>.Shared.Return(playerSettings);
ListPool<ServerSpecificSettingBase>.Shared.Return(ordered);
}

/// <summary>
/// Resyncs the setting to its owner.
/// </summary>
protected void ResyncToOwner()
{
if (KnownOwner != null)
SendSettingsToPlayer(KnownOwner);
}

/// <summary>
Expand All @@ -198,10 +227,16 @@ public static void SendSettingsToPlayer(Player player, int? version = null)
protected abstract CustomSetting CreateDuplicate();

/// <summary>
/// Handles the updating of a setting.
/// Called before setting is sent to a player. Should be used to create player specific options.
/// </summary>
/// <param name="player">The player to update.</param>
protected abstract void HandleSettingUpdate(Player player);
protected virtual void UpdatePlayerSetting()
{
}

/// <summary>
/// Called when client sends a new value on the setting.
/// </summary>
protected abstract void HandleSettingUpdate();

private static void RemoveStoredPlayer(Player player) => ReceivedPlayerSettings.Remove(player);

Expand All @@ -218,13 +253,14 @@ private static void OnSettingsUpdated(ReferenceHub hub, ServerSpecificSettingBas

CustomSetting newSettingPlayer = EnsurePlayerSpecificSetting(player, setting);

NetworkWriter entryWriter = new();
// NetworkWriter entryWriter = new();
// settingBase.SerializeEntry(entryWriter);
// newSettingPlayer.Base.DeserializeEntry(new NetworkReader(entryWriter.buffer));
NetworkWriter valueWriter = new();
settingBase.SerializeEntry(entryWriter);
settingBase.SerializeValue(valueWriter);
newSettingPlayer.Base.DeserializeEntry(new NetworkReader(entryWriter.buffer));
newSettingPlayer.Base.DeserializeValue(new NetworkReader(valueWriter.buffer));
newSettingPlayer.HandleSettingUpdate(player);

newSettingPlayer.HandleSettingUpdate();
}

private static CustomSetting EnsurePlayerSpecificSetting(Player player, CustomSetting toMatch)
Expand All @@ -234,6 +270,7 @@ private static CustomSetting EnsurePlayerSpecificSetting(Player player, CustomSe
if (currentSetting == null)
{
currentSetting = toMatch.CreateDuplicate();
currentSetting.KnownOwner = player;
settings.Add(currentSetting);
}

Expand Down
11 changes: 9 additions & 2 deletions SecretAPI/Features/UserSettings/CustomSliderSetting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ protected CustomSliderSetting(
public float MinimumValue
{
get => Base.MinValue;
[Obsolete("Setting this value is not currently supported.")]
set => Base.MinValue = value;
}

Expand All @@ -73,7 +72,6 @@ public float MinimumValue
public float MaximumValue
{
get => Base.MaxValue;
[Obsolete("Setting this value is not currently supported.")]
set => Base.MaxValue = value;
}

Expand All @@ -86,5 +84,14 @@ public float DefaultValue
[Obsolete("Setting this value is not currently supported.")]
set => Base.DefaultValue = value;
}

/// <summary>
/// Gets or sets a value indicating whether to use integer. False will use float.
/// </summary>
public bool UseInteger
{
get => Base.Integer;
set => Base.Integer = value;
}
}
}
22 changes: 22 additions & 0 deletions SecretAPI/Patches/Features/SendSettingsPlayerSync.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace SecretAPI.Patches.Features
{
using HarmonyLib;
using LabApi.Features.Wrappers;
using SecretAPI.Attribute;
using SecretAPI.Features.UserSettings;
using UserSettings.ServerSpecific;

/// <summary>
/// Fixes <see cref="ServerSpecificSettingsSync.SendToPlayer(ReferenceHub)"/> to resync with <see cref="CustomSetting.SendSettingsToPlayer"/>.
/// </summary>
[HarmonyPatchCategory(nameof(CustomSetting))]
[HarmonyPatch(typeof(ServerSpecificSettingsSync), nameof(ServerSpecificSettingsSync.SendToPlayer), [typeof(ReferenceHub)])]
internal static class SendSettingsPlayerSync
{
private static bool Prefix(ReferenceHub hub)
{
CustomSetting.SendSettingsToPlayer(Player.Get(hub));
return false;
}
}
}
Loading