From 979fcc5c0889ec6200425f9807c6e0f18b8e615c Mon Sep 17 00:00:00 2001
From: "Redforce04(Main-PC)" <74625280+Redforce04@users.noreply.github.com>
Date: Thu, 5 Jun 2025 16:21:03 -0500
Subject: [PATCH] Sylecopify + Change project version to Dotnet sdk type.
---
.gitattributes | 63 +
Config.cs | 113 +-
Configs/GroupHeader.cs | 66 +-
Configs/LabelButton.cs | 66 +-
Directory.Build.props | 7 -
EventHandler.cs | 287 +++--
Examples/AbilitiesExample.cs | 223 ++--
Examples/DemoExample.cs | 98 +-
Examples/LightSpawnerExample.cs | 364 +++---
Examples/MainExample.cs | 34 +-
Examples/PrimitiveSpawnerExample.cs | 404 ++++---
Examples/TextAreaExample.cs | 113 +-
Features/AssemblyMenu.cs | 71 +-
Features/CompatibilityConfig.cs | 25 +
Features/ComptabilityConfig.cs | 19 -
Features/Interfaces/ISetting.cs | 22 +-
Features/Log.cs | 253 ++--
Features/Menu.cs | 1076 ++++++++++-------
Features/Parameters.cs | 309 ++---
Features/Utils.cs | 53 +-
Features/Wrappers/Button.cs | 66 +-
Features/Wrappers/Dropdown.cs | 75 +-
Features/Wrappers/Keybind.cs | 99 +-
Features/Wrappers/Plaintext.cs | 72 +-
Features/Wrappers/Slider.cs | 81 +-
Features/Wrappers/YesNoButton.cs | 72 +-
NuGet.config | 6 +
Patches/CompatibilityPatches/Compatibility.cs | 137 +++
.../CompatibilityGetter.cs | 71 ++
.../SendToPlayerDSPatch.cs | 28 +
.../SendToPlayerPatch.cs | 97 +-
Patches/CompatibilityPatches/SetIdPatch.cs | 42 +
Patches/ExiledPatch.cs | 32 +
Patches/OriginalDefinitionPatch.cs | 76 ++
Patches/PrevalidateResponsePatch.cs | 28 +
Patchs/CompatibilizerPatchs/Compatibilizer.cs | 104 --
.../CompatibilizerGetter.cs | 46 -
.../SendToPlayerDSPatch.cs | 18 -
Patchs/CompatibilizerPatchs/SetIdPatch.cs | 29 -
Patchs/ExiledPatch.cs | 22 -
Patchs/OriginalDefinitionPatch.cs | 57 -
Patchs/PrevalidateResponsePatch.cs | 18 -
Plugin.cs | 249 ++--
Properties/AssemblyInfo.cs | 36 +-
SSMenuSystem.csproj | 205 ++--
SSMenuSystem.sln.DotSettings | 2 +
SSMenuSystem.sln.DotSettings.user | 3 +
Translation.cs | 107 +-
stylecop.json | 21 +
49 files changed, 3143 insertions(+), 2422 deletions(-)
create mode 100644 .gitattributes
delete mode 100644 Directory.Build.props
create mode 100644 Features/CompatibilityConfig.cs
delete mode 100644 Features/ComptabilityConfig.cs
create mode 100644 NuGet.config
create mode 100644 Patches/CompatibilityPatches/Compatibility.cs
create mode 100644 Patches/CompatibilityPatches/CompatibilityGetter.cs
create mode 100644 Patches/CompatibilityPatches/SendToPlayerDSPatch.cs
rename {Patchs/CompatibilizerPatchs => Patches/CompatibilityPatches}/SendToPlayerPatch.cs (61%)
create mode 100644 Patches/CompatibilityPatches/SetIdPatch.cs
create mode 100644 Patches/ExiledPatch.cs
create mode 100644 Patches/OriginalDefinitionPatch.cs
create mode 100644 Patches/PrevalidateResponsePatch.cs
delete mode 100644 Patchs/CompatibilizerPatchs/Compatibilizer.cs
delete mode 100644 Patchs/CompatibilizerPatchs/CompatibilizerGetter.cs
delete mode 100644 Patchs/CompatibilizerPatchs/SendToPlayerDSPatch.cs
delete mode 100644 Patchs/CompatibilizerPatchs/SetIdPatch.cs
delete mode 100644 Patchs/ExiledPatch.cs
delete mode 100644 Patchs/OriginalDefinitionPatch.cs
delete mode 100644 Patchs/PrevalidateResponsePatch.cs
create mode 100644 SSMenuSystem.sln.DotSettings
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..5896c16
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/Config.cs b/Config.cs
index 8fe7f15..b81ee73 100644
--- a/Config.cs
+++ b/Config.cs
@@ -1,69 +1,82 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem;
+
using System.ComponentModel;
-using SSMenuSystem.Features;
+
#if EXILED
using Exiled.API.Interfaces;
#endif
+using Features;
-namespace SSMenuSystem
-{
- ///
- public class Config
+///
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+public class Config
#if EXILED
- : IConfig
+ : IConfig
#endif
- {
- ///
- public bool IsEnabled { get; set; } = true;
+{
+ ///
+ /// Gets or sets a value indicating whether the plugin should be enabled.
+ ///
+ public bool IsEnabled { get; set; } = true;
- ///
- public bool Debug { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the plugin should display debugging logs.
+ ///
+ public bool Debug { get; set; }
- ///
- /// Gets or sets a value indicating whether pins is allowed or not (pin is a thing that has been displayed on all menus).
- ///
- [Description("Whether pins is allowed or not (pin is a thing that has been displayed on all menus).")]
- public bool AllowPinnedContent { get; set; } = true;
+ ///
+ /// Gets or sets a value indicating whether pins is allowed or not (pin is a thing that has been displayed on all menus).
+ ///
+ [Description("Whether pins is allowed or not (pin is a thing that has been displayed on all menus).")]
+ public bool AllowPinnedContent { get; set; } = true;
- ///
- /// Gets or sets a value indicating whether clients (= non-moderators) whould see errors or not.
- ///
- [Description("Whether clients (= non-moderators) whould see errors or not.")]
- public bool ShowErrorToClient { get; set; } = true;
+ ///
+ /// Gets or sets a value indicating whether clients (= non-moderators) should see errors or not.
+ ///
+ [Description("Whether clients (= non-moderators) should see errors or not.")]
+ public bool ShowErrorToClient { get; set; } = true;
- ///
- /// Gets or sets a value indicating whether clients (= non-moderators) whould see total errors (= some plugins-content name) or not. HIGLY UNRECOMMENDED TO SET TRUE.
- ///
- [Description("Whether clients (= non-moderators) whould see total errors (= some plugins-content name) or not. HIGLY UNRECOMMENDED TO SET TRUE.")]
- public bool ShowFullErrorToClient { get; set; } = false;
+ ///
+ /// Gets or sets a value indicating whether clients (= non-moderators) should see total errors (= some plugins-content name) or not. It is advised to leave this as false.
+ ///
+ [Description("Whether clients (= non-moderators) should see total errors (= some plugins-content name) or not. It is advised to leave this as false.")]
+ public bool ShowFullErrorToClient { get; set; } = false;
- ///
- /// Gets or sets a value indicating whether moderators (= has RA access) whould see total errors (= some plugins-content name).
- ///
- [Description("Whether moderators (= has RA access) whould see total errors (= some plugins-content name).")]
- public bool ShowFullErrorToModerators { get; set; } = true;
+ ///
+ /// Gets or sets a value indicating whether moderators (= has RA access) should see total errors (= some plugins-content name).
+ ///
+ [Description("Whether moderators (= has RA access) should see total errors (= some plugins-content name).")]
+ public bool ShowFullErrorToModerators { get; set; } = true;
- ///
- /// If there is only one menu registered and this set to false, this menu would be automatiquely displayed.
- ///
- [Description("If there is only one menu registered and this set to false, this menu would be automatiquely displayed.")]
- public bool ForceMainMenuEvenIfOnlyOne { get; set; }
+ ///
+ /// Gets or sets a value indicating whether singularly registered menus should be automatically shown.
+ ///
+ [Description("Indicates whether singularly registered menus should be automatically shown.")]
+ public bool ForceMainMenuEvenIfOnlyOne { get; set; }
- ///
- /// Gets or sets a value indicating whether examples is enabled. Warning: if set to true, some content of examples would be Game breaking (speed ability, scan ability, etc...)
- ///
- [Description("Whether examples is enabled. Warning: if set to true, some content of examples would be Game breaking (speed ability, scan ability, etc...).")]
- public bool EnableExamples { get; set; } = true;
+ ///
+ /// Gets or sets a value indicating whether examples is enabled. Warning: if set to true, some content of examples would be Game breaking (speed ability, scan ability, etc...)
+ ///
+ [Description("Whether examples is enabled. Warning: if set to true, some content of examples would be Game breaking (speed ability, scan ability, etc...).")]
+ public bool EnableExamples { get; set; } = true;
- ///
- /// The comptability system config.
- ///
- public ComptabilityConfig ComptabilitySystem { get; set; } = new();
+ ///
+ /// Gets or sets the compatibility system config.
+ ///
+ public CompatibilityConfig CompatibilitySystem { get; set; } = new ();
#if !EXILED
- ///
- /// Plugin translations.
- ///
- public Translation Translation { get; set; } = new();
+ ///
+ /// Gets or sets the plugin's translations.
+ ///
+ [Description("Translations for the plugin.")]
+ public Translation Translation { get; set; } = new ();
#endif
- }
}
\ No newline at end of file
diff --git a/Configs/GroupHeader.cs b/Configs/GroupHeader.cs
index 963cd48..6d332e0 100644
--- a/Configs/GroupHeader.cs
+++ b/Configs/GroupHeader.cs
@@ -1,38 +1,44 @@
-namespace SSMenuSystem.Configs
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Configs;
+
+///
+/// Button labels config.
+///
+public class GroupHeader
{
///
- /// Button labels config.
+ /// Initializes a new instance of the class.
///
- public class GroupHeader
+ /// the label text.
+ /// the button text.
+ public GroupHeader(string label, string? hint)
{
- ///
- /// The label of button (displayed at the left).
- ///
- public string Label { get; set; }
+ this.Label = label;
+ this.Hint = hint ?? "MISSING_HINT";
+ }
- ///
- /// The Button content (displayed on the button).
- ///
- public string Hint { get; set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public GroupHeader()
+ {
+ this.Label = "MISSING_LABEL";
+ this.Hint = "MISSING_HINT";
+ }
- ///
- /// Initialize a new instance of
- ///
- /// the label text.
- /// the button text.
- public GroupHeader(string label, string hint)
- {
- Label = label;
- Hint = hint;
- }
+ ///
+ /// Gets or sets the label of button (displayed at the left).
+ ///
+ public string Label { get; set; }
- ///
- /// Default constructor of .
- ///
- public GroupHeader()
- {
- Label = "MISSING_LABEL";
- Hint = "MISSING_HINT";
- }
- }
+ ///
+ /// Gets or sets the Button content (displayed on the button).
+ ///
+ public string Hint { get; set; }
}
\ No newline at end of file
diff --git a/Configs/LabelButton.cs b/Configs/LabelButton.cs
index 1d16e90..ebbccb1 100644
--- a/Configs/LabelButton.cs
+++ b/Configs/LabelButton.cs
@@ -1,38 +1,44 @@
-namespace SSMenuSystem.Configs
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Configs;
+
+///
+/// Button labels config.
+///
+public class LabelButton
{
///
- /// Button labels config.
+ /// Initializes a new instance of the class.
///
- public class LabelButton
+ /// the label text.
+ /// the button text.
+ public LabelButton(string label, string buttonText)
{
- ///
- /// The label of button (displayed at the left).
- ///
- public string Label { get; set; }
+ this.Label = label;
+ this.ButtonText = buttonText;
+ }
- ///
- /// The Button content (displayed on the button).
- ///
- public string ButtonText { get; set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LabelButton()
+ {
+ this.Label = "MISSING_LABEL";
+ this.ButtonText = "MISSING_VALUE";
+ }
- ///
- /// Initialize a new instance of
- ///
- /// the label text.
- /// the button text.
- public LabelButton(string label, string buttonText)
- {
- Label = label;
- ButtonText = buttonText;
- }
+ ///
+ /// Gets or sets the label of button (displayed at the left).
+ ///
+ public string Label { get; set; }
- ///
- /// Default constructor of .
- ///
- public LabelButton()
- {
- Label = "MISSING_LABEL";
- ButtonText = "MISSING_VALUE";
- }
- }
+ ///
+ /// Gets or sets the Button content (displayed on the button).
+ ///
+ public string ButtonText { get; set; }
}
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
deleted file mode 100644
index 826e4af..0000000
--- a/Directory.Build.props
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- $(MSBuildThisFileDirectory)\SSMenuSystem.ruleset
-
-
\ No newline at end of file
diff --git a/EventHandler.cs b/EventHandler.cs
index 6a82991..d08e992 100644
--- a/EventHandler.cs
+++ b/EventHandler.cs
@@ -1,159 +1,202 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem;
+
using System;
using System.Collections.Generic;
using System.Linq;
+
+using Features;
+using Features.Wrappers;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.CustomHandlers;
-using SSMenuSystem.Features;
-using SSMenuSystem.Features.Wrappers;
-using UserSettings.ServerSpecific;
using MEC;
-using Exiled.Events.EventArgs.Player;
-using Log = SSMenuSystem.Features.Log;
+using UserSettings.ServerSpecific;
-namespace SSMenuSystem
+using Log = Features.Log;
+
+///
+/// The event handler.
+///
+// ReSharper disable once ClassNeverInstantiated.Global
+internal class EventHandler : CustomEventsHandler
{
- // ReSharper disable once ClassNeverInstantiated.Global
- internal class EventHandler : CustomEventsHandler
+ ///
+ /// Triggered when receiving a ServerSpecificSetting input from a player.
+ ///
+ /// The player's hub.
+ /// The ServerSpecificSetting.
+ /// Thrown if the ServerSpecificSetting was not registered via this plugin.
+ /// Thrown if the ServerSpecificSetting has an invalid id.
+ public static void OnReceivingInput(ReferenceHub hub, ServerSpecificSettingBase ss)
{
- public override void OnPlayerJoined(PlayerJoinedEventArgs ev) => Timing.RunCoroutine(Parameters.SyncAll(ev.Player.ReferenceHub));
- public override void OnPlayerLeft(PlayerLeftEventArgs ev) => Menu.DeletePlayer(ev.Player.ReferenceHub);
- public override void OnPlayerGroupChanged(PlayerGroupChangedEventArgs ev) =>
- SyncChangedGroup(ev.Player.ReferenceHub);
-
- private static void SyncChangedGroup(ReferenceHub hub)
+ try
{
- Timing.CallDelayed(0.1f, () =>
+ if (Parameters.SyncCache.TryGetValue(hub, out List value))
{
- if (Parameters.SyncCache.ContainsKey(hub))
- return;
+ value.Add(ss);
+ Log.Debug("received value that been flagged as \"SyncCached\". Redirected values to Cache.");
+ return;
+ }
- Menu menu = Menu.GetCurrentPlayerMenu(hub);
- menu?.Reload(hub);
- if (menu == null)
- Menu.LoadForPlayer(hub, null);
- });
- }
+ if (ss.OriginalDefinition != null)
+ {
+ ss.Label = ss.OriginalDefinition.Label;
+ ss.HintDescription = ss.OriginalDefinition.HintDescription;
+ ss.SettingId = ss.OriginalDefinition.SettingId;
+ }
+ else
+ {
+ // is a pin or header.
+ ss.SettingId -= Menu.GetCurrentPlayerMenu(hub)?.Hash ?? 0;
+ }
- public static void OnReceivingInput(ReferenceHub hub, ServerSpecificSettingBase ss)
- {
- try
+ // return to menu
+ if (ss.SettingId == -999)
{
- if (Parameters.SyncCache.TryGetValue(hub, out List value))
- {
- value.Add(ss);
- Log.Debug("received value that been flagged as \"SyncCached\". Redirected values to Cache.");
- return;
- }
+ Menu.LoadForPlayer(hub, null);
+ return;
+ }
- if (ss.OriginalDefinition != null)
- {
- ss.Label = ss.OriginalDefinition.Label;
- ss.HintDescription = ss.OriginalDefinition.HintDescription;
- ss.SettingId = ss.OriginalDefinition.SettingId;
- }
- else // is a pin or header
- ss.SettingId -= Menu.GetCurrentPlayerMenu(hub)?.Hash ?? 0;
+ // check permissions
+ Menu? menu = Menu.GetCurrentPlayerMenu(hub);
+ if (!menu?.CheckAccess(hub) ?? false)
+ {
+ Log.Warn($"{hub.nicknameSync.MyNick} tried to interact with menu {menu?.Name} which is disabled for him.");
+ Menu.LoadForPlayer(hub, null);
+ return;
+ }
- // return to menu
- if (ss.SettingId == -999)
+ // global/local keybinds
+ if (ss.SettingId > Keybind.Increment && ss is SSKeybindSetting setting)
+ {
+ Keybind? loadedKeybind = Menu.TryGetKeybinding(hub, ss, menu);
+ if (loadedKeybind != null)
{
- Menu.LoadForPlayer(hub, null);
+ loadedKeybind.Action?.Invoke(hub, setting.SyncIsPressed);
return;
}
+ }
- // check permissions
- Menu menu = Menu.GetCurrentPlayerMenu(hub);
- if (!menu?.CheckAccess(hub) ?? false)
+ // load main menu
+ if (ss.SettingId == 0 && menu != null)
+ {
+ // return to upper menu (or main menu)
+ Menu? m = Menu.GetMenu(menu.MenuRelated!);
+ Menu.LoadForPlayer(hub, m);
+ }
+
+ // Load method when input is used on specific menu.
+ else if (menu != null)
+ {
+ if (ss.SettingId < 0)
{
- Log.Warn($"{hub.nicknameSync.MyNick} tried to interact with menu {menu.Name} which is disabled for him.");
- Menu.LoadForPlayer(hub, null);
- return;
+ Menu.LoadForPlayer(hub, menu.TryGetSubMenu(ss.SettingId));
}
-
- // global/local keybinds
- if (ss.SettingId > Keybind.Increment && ss is SSKeybindSetting setting)
+ else
{
- Keybind loadedKeybind = Menu.TryGetKeybinding(hub, ss, menu);
- if (loadedKeybind != null)
+ if (menu.InternalSettingsSync[hub].Any(x => x.SettingId == ss.SettingId))
{
- loadedKeybind.Action?.Invoke(hub, setting.SyncIsPressed);
- return;
+ menu.InternalSettingsSync[hub][menu.InternalSettingsSync[hub].FindIndex(x => x.SettingId == ss.SettingId)] = ss;
}
- }
-
- // load main menu
- if (ss.SettingId == 0 && menu != null)
- {
- // return to upper menu (or main menu)
- Menu m = Menu.GetMenu(menu.MenuRelated);
- Menu.LoadForPlayer(hub, m);
- }
- // load method when input is used on specific menu.
- else if (menu != null)
- {
- if (ss.SettingId < 0)
- Menu.LoadForPlayer(hub, menu.TryGetSubMenu(ss.SettingId));
else
{
- if (menu.InternalSettingsSync[hub].Any(x => x.SettingId == ss.SettingId))
- menu.InternalSettingsSync[hub][menu.InternalSettingsSync[hub].FindIndex(x => x.SettingId == ss.SettingId)] = ss;
- else
- menu.InternalSettingsSync[hub].Add(ss);
-
- ServerSpecificSettingBase s = menu.SentSettings.TryGetValue(hub, out ServerSpecificSettingBase[] customSettings) ? customSettings.FirstOrDefault(b => b.SettingId == ss.SettingId) : null;
- if (s == null)
- throw new Exception("Failed to find the sent setting.");
-
- switch (s)
- {
- case Button wBtn:
- wBtn.Action?.Invoke(hub, (SSButton)ss);
- break;
- case Dropdown wDropdown:
- wDropdown.Action?.Invoke(hub, wDropdown.Options[((SSDropdownSetting)ss).SyncSelectionIndexRaw], ((SSDropdownSetting)ss).SyncSelectionIndexRaw, (SSDropdownSetting)ss);
- break;
- case Plaintext wPlaintext:
- wPlaintext.OnChanged?.Invoke(hub, ((SSPlaintextSetting)ss).SyncInputText, (SSPlaintextSetting)ss);
- break;
- case Slider wSlider:
- wSlider.Action?.Invoke(hub, ((SSSliderSetting)ss).SyncFloatValue, (SSSliderSetting)ss);
- break;
- case YesNoButton wYesNo:
- wYesNo.Action?.Invoke(hub, ((SSTwoButtonsSetting)ss).SyncIsA, (SSTwoButtonsSetting)ss);
- break;
- }
-
- if (ss.SettingId > menu.Hash)
- ss.SettingId -= menu.Hash;
- menu.OnInput(hub, ss);
+ menu.InternalSettingsSync[hub].Add(ss);
}
- }
- // load selected menu.
- else
- {
- if (!Menu.Menus.Any(x => x.Id == ss.SettingId))
- throw new KeyNotFoundException($"invalid loaded id ({ss.SettingId}). please report this bug to developers.");
- Menu m = Menu.Menus.FirstOrDefault(x => x.Id == ss.SettingId);
- Menu.LoadForPlayer(hub, m);
+
+ ServerSpecificSettingBase? s = menu.SentSettings.TryGetValue(hub, out ServerSpecificSettingBase[] customSettings) ? customSettings.FirstOrDefault(b => b.SettingId == ss.SettingId) : null;
+ if (s is null)
+ {
+ throw new Exception("Failed to find the sent setting.");
+ }
+
+ switch (s)
+ {
+ case Button wBtn:
+ wBtn.Action?.Invoke(hub, (SSButton)ss);
+ break;
+ case Dropdown wDropdown:
+ wDropdown.Action?.Invoke(hub, wDropdown.Options[((SSDropdownSetting)ss).SyncSelectionIndexRaw], ((SSDropdownSetting)ss).SyncSelectionIndexRaw, (SSDropdownSetting)ss);
+ break;
+ case Plaintext wPlaintext:
+ wPlaintext.OnChanged?.Invoke(hub, ((SSPlaintextSetting)ss).SyncInputText, (SSPlaintextSetting)ss);
+ break;
+ case Slider wSlider:
+ wSlider.Action?.Invoke(hub, ((SSSliderSetting)ss).SyncFloatValue, (SSSliderSetting)ss);
+ break;
+ case YesNoButton wYesNo:
+ wYesNo.Action?.Invoke(hub, ((SSTwoButtonsSetting)ss).SyncIsA, (SSTwoButtonsSetting)ss);
+ break;
+ }
+
+ if (ss.SettingId > menu.Hash)
+ {
+ ss.SettingId -= menu.Hash;
+ }
+
+ menu.OnInput(hub, ss);
}
}
- catch (Exception e)
+
+ // load selected menu.
+ else
{
- Log.Error($"there is a error while receiving input {ss.SettingId} ({ss.Label}): {e.Message}\nActivate Debugger to show full details.");
-#if DEBUG
- Log.Error(e.ToString());
-#else
- Log.Debug(e.ToString());
-#endif
- if (Plugin.Instance.Config.ShowErrorToClient)
+ if (Menu.Menus.All(x => x.Id != ss.SettingId))
{
- Features.Utils.SendToPlayer(hub, null, new ServerSpecificSettingBase[]
- {
- new SSTextArea(-5, $"{Plugin.Instance.Translation.ServerError}\n{((hub.serverRoles.RemoteAdmin || Plugin.Instance.Config.ShowFullErrorToClient) && Plugin.Instance.Config.ShowFullErrorToModerators ? e.ToString() : Plugin.Instance.Translation.NoPermission)}", SSTextArea.FoldoutMode.CollapsedByDefault, Plugin.Instance.Translation.ServerError),
- new SSButton(-999, Plugin.Instance.Translation.ReloadButton.Label, Plugin.Instance.Translation.ReloadButton.ButtonText)
- });
+ throw new KeyNotFoundException($"invalid loaded id ({ss.SettingId}). please report this bug to developers.");
}
+
+ Menu? m = Menu.Menus.FirstOrDefault(x => x.Id == ss.SettingId);
+ Menu.LoadForPlayer(hub, m);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error($"there is a error while receiving input {ss.SettingId} ({ss.Label}): {e.Message}\nActivate Debugger to show full details.");
+ #if DEBUG
+ Log.Error(e.ToString());
+ #else
+ Log.Debug(e.ToString());
+ #endif
+ if (Plugin.Instance!.Config.ShowErrorToClient)
+ {
+ Utils.SendToPlayer(
+ hub,
+ null,
+ [new SSTextArea(-5, $"{Plugin.Instance.Translation.ServerError}\n{((hub.serverRoles.RemoteAdmin || Plugin.Instance.Config.ShowFullErrorToClient) && Plugin.Instance.Config.ShowFullErrorToModerators ? e.ToString() : Plugin.Instance.Translation.NoPermission)}", SSTextArea.FoldoutMode.CollapsedByDefault, Plugin.Instance.Translation.ServerError), new SSButton(-999, Plugin.Instance.Translation.ReloadButton.Label, Plugin.Instance.Translation.ReloadButton.ButtonText),]);
}
}
}
+
+ ///
+ public override void OnPlayerJoined(PlayerJoinedEventArgs ev) => Timing.RunCoroutine(Parameters.SyncAll(ev.Player.ReferenceHub));
+
+ ///
+ public override void OnPlayerLeft(PlayerLeftEventArgs ev) => Menu.DeletePlayer(ev.Player.ReferenceHub);
+
+ ///
+ public override void OnPlayerGroupChanged(PlayerGroupChangedEventArgs ev) => SyncChangedGroup(ev.Player.ReferenceHub);
+
+ private static void SyncChangedGroup(ReferenceHub hub)
+ {
+ Timing.CallDelayed(0.1f, () =>
+ {
+ if (Parameters.SyncCache.ContainsKey(hub))
+ {
+ return;
+ }
+
+ Menu? menu = Menu.GetCurrentPlayerMenu(hub);
+ menu?.Reload(hub);
+ if (menu == null)
+ {
+ Menu.LoadForPlayer(hub, null);
+ }
+ });
+ }
}
\ No newline at end of file
diff --git a/Examples/AbilitiesExample.cs b/Examples/AbilitiesExample.cs
index b9af122..75f009f 100644
--- a/Examples/AbilitiesExample.cs
+++ b/Examples/AbilitiesExample.cs
@@ -1,133 +1,184 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Examples;
+
using System;
using System.Collections.Generic;
+
using CustomPlayerEffects;
using InventorySystem;
using InventorySystem.Items;
using PlayerRoles;
using PlayerRoles.FirstPersonControl;
using PlayerStatsSystem;
-using SSMenuSystem.Features;
-using SSMenuSystem.Features.Wrappers;
+using Features;
+using Features.Wrappers;
using UnityEngine;
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Examples
+///
+/// An example menu for demonstrating various abilities.
+///
+internal class AbilitiesExample : Menu
{
- internal class AbilitiesExample : Menu
+ private const float HealAllyHp = 50f;
+ private const float HealAllyRange = 3.5f;
+ private const byte BoostIntensity = 60;
+ private const float BoostHealthDrain = 5f;
+ private static readonly HashSet ActiveSpeedBoosts = new ();
+ private List settings = null!;
+
+ ///
+ public override string Name { get; set; } = "Abilities Extension";
+
+ ///
+ public override int Id { get; set; } = -8;
+
+ ///
+ public override Type? MenuRelated { get; set; } = typeof(MainExample);
+
+ ///
+ public override ServerSpecificSettingBase[] Settings => this.GetSettings();
+
+ ///
+ protected override void OnRegistered()
{
- private const float HealAllyHp = 50f;
- private const float HealAllyRange = 3.5f;
- private const byte BoostIntensity = 60;
- private const float BoostHealthDrain = 5f;
- private List _settings;
- private static readonly HashSet ActiveSpeedBoosts = new();
+ ReferenceHub.OnPlayerRemoved += OnDisconnect;
+ PlayerRoleManager.OnRoleChanged += this.OnRoleChanged;
+ StaticUnityMethods.OnUpdate += this.OnUpdate;
+ }
- public override ServerSpecificSettingBase[] Settings => GetSettings();
+ private static void OnDisconnect(ReferenceHub hub)
+ {
+ ActiveSpeedBoosts.Remove(hub);
+ }
- private void TryHealAlly(ReferenceHub sender)
+ private void TryHealAlly(ReferenceHub sender)
+ {
+ ItemIdentifier curItem = sender.inventory.CurItem;
+ if (curItem.TypeId != ItemType.Medkit)
{
- ItemIdentifier curItem = sender.inventory.CurItem;
- if (curItem.TypeId != ItemType.Medkit)
- return;
- Vector3 position = sender.PlayerCameraReference.position;
- Vector3 forward = sender.PlayerCameraReference.forward;
-
- while (Physics.Raycast(position, forward, out RaycastHit hitInfo, HealAllyRange) && hitInfo.collider.TryGetComponent(out HitboxIdentity component) && !HitboxIdentity.IsEnemy(component.TargetHub, sender))
- {
- if (component.TargetHub == sender)
- {
- position += forward * 0.08f;
- }
- else
- {
- component.TargetHub.playerStats.GetModule().ServerHeal(HealAllyHp);
- sender.inventory.ServerRemoveItem(curItem.SerialNumber, null);
- break;
- }
- }
+ return;
}
- private void SetSpeedBoost(ReferenceHub hub, bool state)
+ Vector3 position = sender.PlayerCameraReference.position;
+ Vector3 forward = sender.PlayerCameraReference.forward;
+
+ while (Physics.Raycast(position, forward, out RaycastHit hitInfo, HealAllyRange) && hitInfo.collider.TryGetComponent(out HitboxIdentity component) && !HitboxIdentity.IsEnemy(component.TargetHub, sender))
{
- MovementBoost effect = hub.playerEffectsController.GetEffect();
- if (state && hub.IsHuman())
+ if (component.TargetHub == sender)
{
- effect.ServerSetState(BoostIntensity);
- ActiveSpeedBoosts.Add(hub);
+ position += forward * 0.08f;
}
else
{
- effect.ServerDisable();
- ActiveSpeedBoosts.Remove(hub);
+ component.TargetHub.playerStats.GetModule().ServerHeal(HealAllyHp);
+ sender.inventory.ServerRemoveItem(curItem.SerialNumber, null);
+ break;
}
}
- private ServerSpecificSettingBase[] GetSettings()
+ }
+
+ private void SetSpeedBoost(ReferenceHub hub, bool state)
+ {
+ MovementBoost effect = hub.playerEffectsController.GetEffect();
+ if (state && hub.IsHuman())
+ {
+ effect.ServerSetState(BoostIntensity);
+ ActiveSpeedBoosts.Add(hub);
+ }
+ else
{
- _settings = new List
- {
- new SSGroupHeader("Abilities"),
- new Keybind(ExampleId.HealAlly, "Heal Ally", (hub, isPressed) =>
+ effect.ServerDisable();
+ ActiveSpeedBoosts.Remove(hub);
+ }
+ }
+
+ private ServerSpecificSettingBase[] GetSettings()
+ {
+ this.settings =
+ [
+ new SSGroupHeader("Abilities"), new Keybind(
+ ExampleId.HealAlly,
+ "Heal Ally",
+ (hub, isPressed) =>
+ {
+ if (isPressed)
{
- if (isPressed)
- TryHealAlly(hub);
- }, KeyCode.H, hint:
- $"Press this key while holding a medkit to instantly heal a stationary ally for {HealAllyHp} HP.", isGlobal:false),
- new Keybind(ExampleId.SpeedBoostKey, "Speed Boost (Human-only)", (hub, isPressed) =>
+ this.TryHealAlly(hub);
+ }
+ },
+ suggestedKey: KeyCode.H,
+ hint: $"Press this key while holding a medkit to instantly heal a stationary ally for {HealAllyHp} HP.",
+ isGlobal: false),
+
+ new Keybind(
+ ExampleId.SpeedBoostKey,
+ "Speed Boost (Human-only)",
+ (hub, isPressed) =>
{
- if (hub.GetParameter(ExampleId.SpeedBoostToggle).SyncIsB)
+ if (hub.GetParameter(ExampleId.SpeedBoostToggle) !.SyncIsB)
{
if (!isPressed)
+ {
return;
- SetSpeedBoost(hub, !ActiveSpeedBoosts.Contains(hub));
+ }
+
+ this.SetSpeedBoost(hub, !ActiveSpeedBoosts.Contains(hub));
}
else
- SetSpeedBoost(hub, isPressed);
- }, KeyCode.Y, hint: "Increase your speed by draining your health.", isGlobal: false),
- new SSTwoButtonsSetting(ExampleId.SpeedBoostToggle, "Speed Boost - Activation Mode", "Hold", "Toggle")
- };
+ {
+ this.SetSpeedBoost(hub, isPressed);
+ }
+ },
+ suggestedKey: KeyCode.Y,
+ hint: "Increase your speed by draining your health.",
+ isGlobal: false),
- return _settings.ToArray();
- }
+ new SSTwoButtonsSetting(
+ ExampleId.SpeedBoostToggle,
+ "Speed Boost - Activation Mode",
+ "Hold",
+ "Toggle"),
- private static void OnDisconnect(ReferenceHub hub)
- {
- ActiveSpeedBoosts.Remove(hub);
- }
+ ];
- private void OnRoleChanged(
- ReferenceHub userHub,
- PlayerRoleBase prevRole,
- PlayerRoleBase newRole)
- {
- this.SetSpeedBoost(userHub, false);
- }
+ return this.settings.ToArray();
+ }
+
+ private void OnRoleChanged(
+ ReferenceHub userHub,
+ PlayerRoleBase prevRole,
+ PlayerRoleBase newRole)
+ {
+ this.SetSpeedBoost(userHub, false);
+ }
- protected override void OnRegistered()
+ private void OnUpdate()
+ {
+ if (!StaticUnityMethods.IsPlaying)
{
- ReferenceHub.OnPlayerRemoved += OnDisconnect;
- PlayerRoleManager.OnRoleChanged += OnRoleChanged;
- StaticUnityMethods.OnUpdate += OnUpdate;
+ return;
}
- private void OnUpdate()
+ foreach (ReferenceHub activeSpeedBoost in ActiveSpeedBoosts)
{
- if (!StaticUnityMethods.IsPlaying)
- return;
- foreach (ReferenceHub activeSpeedBoost in ActiveSpeedBoosts)
+ if (!Mathf.Approximately(activeSpeedBoost.GetVelocity().SqrMagnitudeIgnoreY(), 0.0f))
{
- if (!Mathf.Approximately(activeSpeedBoost.GetVelocity().SqrMagnitudeIgnoreY(), 0.0f))
- activeSpeedBoost.playerStats.DealDamage(new UniversalDamageHandler(Time.deltaTime * BoostHealthDrain, DeathTranslations.Scp207));
+ activeSpeedBoost.playerStats.DealDamage(new UniversalDamageHandler(Time.deltaTime * BoostHealthDrain, DeathTranslations.Scp207));
}
}
+ }
- public override string Name { get; set; } = "Abilities Extension";
- public override int Id { get; set; } = -8;
- private static class ExampleId
- {
- public static readonly int SpeedBoostKey = 5;
- public static readonly int SpeedBoostToggle = 7;
- public static readonly int HealAlly = 9;
- }
- public override Type MenuRelated { get; set; } = typeof(MainExample);
+ private static class ExampleId
+ {
+ public static readonly int SpeedBoostKey = 5;
+ public static readonly int SpeedBoostToggle = 7;
+ public static readonly int HealAlly = 9;
}
}
\ No newline at end of file
diff --git a/Examples/DemoExample.cs b/Examples/DemoExample.cs
index d96fb3f..85efaa5 100644
--- a/Examples/DemoExample.cs
+++ b/Examples/DemoExample.cs
@@ -1,54 +1,58 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Examples;
+
using System;
-using SSMenuSystem.Features;
+using Features;
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Examples
+///
+/// A demo example menu.
+///
+internal class DemoExample : Menu
{
- internal class DemoExample : Menu
- {
- private string[] _options = new string[4]
- {
- "Option 1",
- "Option 2",
- "Option 3",
- "Option 4"
- };
+ private string[] options =
+ [
+ "Option 1",
+ "Option 2",
+ "Option 3",
+ "Option 4",
+ ];
+
+ ///
+ public override ServerSpecificSettingBase[] Settings =>
+ [
+ new SSGroupHeader("GroupHeader"),
+ new SSTwoButtonsSetting(1, "TwoButtonsSetting", "Option A", "Option B"),
+ new SSTextArea(2, "TextArea"),
+ new SSTextArea(3, "Multiline collapsable TextArea.\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", SSTextArea.FoldoutMode.ExtendedByDefault),
+ new SSSliderSetting(4, "SliderSetting", 0.0f, 1f),
+ new SSPlaintextSetting(5, "Plaintext"),
+ new SSKeybindSetting(6, "KeybindSetting"),
+ new SSDropdownSetting(7, "DropdownSetting", this.options),
+ new SSDropdownSetting(8, "Scrollable DropdownSetting", this.options, entryType: SSDropdownSetting.DropdownEntryType.Scrollable),
+ new SSButton(9, "Button", "Press me!"),
+ new SSGroupHeader("Hints", hint: "Group headers are used to separate settings into subcategories."),
+ new SSTwoButtonsSetting(10, "Another TwoButtonsSetting", "Option A", "Option B", hint: "Two Buttons are used to store Boolean values."),
+ new SSSliderSetting(11, "Another SliderSetting", 0.0f, 1f, hint: "Sliders store a numeric value within a defined range."),
+ new SSPlaintextSetting(12, "Another Plaintext", hint: "Plaintext fields store any provided text."),
+ new SSKeybindSetting(13, "Another KeybindSetting", hint: "Allows checking if the player is currently holding the action key."),
+ new SSDropdownSetting(14, "Another DropdownSetting", this.options, hint: "Stores an integer value between 0 and the length of options minus 1."),
+ new SSDropdownSetting(15, "Another Scrollable DropdownSetting", this.options, entryType: SSDropdownSetting.DropdownEntryType.Scrollable, hint: "Alternative to dropdown. API is the same as in regular dropdown, but the client-side entry behaves differently."),
+ new SSButton(16, "Another Button", "Press me! (again)", hint: "Triggers an event whenever it is pressed."),
+ ];
+
+ ///
+ public override string Name { get; set; } = "Demo Example";
- public override ServerSpecificSettingBase[] Settings => new ServerSpecificSettingBase[]
- {
- new SSGroupHeader("GroupHeader"),
- new SSTwoButtonsSetting(1, "TwoButtonsSetting", "Option A", "Option B"),
- new SSTextArea(2, "TextArea"),
- new SSTextArea(3,
- "Multiline collapsable TextArea.\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
- SSTextArea.FoldoutMode.ExtendedByDefault),
- new SSSliderSetting(4, "SliderSetting", 0.0f, 1f),
- new SSPlaintextSetting(5, "Plaintext"),
- new SSKeybindSetting(6, "KeybindSetting"),
- new SSDropdownSetting(7, "DropdownSetting", _options),
- new SSDropdownSetting(8, "Scrollable DropdownSetting", _options,
- entryType: SSDropdownSetting.DropdownEntryType.Scrollable),
- new SSButton(9, "Button", "Press me!"),
- new SSGroupHeader("Hints", hint: "Group headers are used to separate settings into subcategories."),
- new SSTwoButtonsSetting(10, "Another TwoButtonsSetting", "Option A", "Option B",
- hint: "Two Buttons are used to store Boolean values."),
- new SSSliderSetting(11, "Another SliderSetting", 0.0f, 1f,
- hint: "Sliders store a numeric value within a defined range."),
- new SSPlaintextSetting(12, "Another Plaintext", hint: "Plaintext fields store any provided text."),
- new SSKeybindSetting(13, "Another KeybindSetting",
- hint: "Allows checking if the player is currently holding the action key."),
- new SSDropdownSetting(14, "Another DropdownSetting", _options,
- hint: "Stores an integer value between 0 and the length of options minus 1."),
- new SSDropdownSetting(15, "Another Scrollable DropdownSetting", _options,
- entryType: SSDropdownSetting.DropdownEntryType.Scrollable,
- hint:
- "Alternative to dropdown. API is the same as in regular dropdown, but the client-side entry behaves differently."),
- new SSButton(16, "Another Button", "Press me! (again)",
- hint: "Triggers an event whenever it is pressed.")
- };
+ ///
+ public override int Id { get; set; } = -6;
- public override string Name { get; set; } = "Demo Example";
- public override int Id { get; set; } = -6;
- public override Type MenuRelated { get; set; } = typeof(MainExample);
- }
+ ///
+ public override Type? MenuRelated { get; set; } = typeof(MainExample);
}
\ No newline at end of file
diff --git a/Examples/LightSpawnerExample.cs b/Examples/LightSpawnerExample.cs
index 7a9cdcc..5aacb50 100644
--- a/Examples/LightSpawnerExample.cs
+++ b/Examples/LightSpawnerExample.cs
@@ -1,212 +1,252 @@
-using System;
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+#pragma warning disable SA1011 // Square bracket nullability symbol spacing.
+namespace SSMenuSystem.Examples;
+
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+
using AdminToys;
using GameCore;
using Mirror;
+using Features;
+using Features.Wrappers;
using UnityEngine;
-using SSMenuSystem.Features;
-using SSMenuSystem.Features.Wrappers;
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Examples
+///
+/// A light spawner demo.
+///
+internal class LightSpawnerExample : Menu
{
- internal class LightSpawnerExample : Menu
- {
- private List _settings;
- private readonly List _addedSettings = new();
- private List _presets;
- private LightShadows[] _shadowsType;
- private LightType[] _lightType;
- private LightShape[] _lightShape;
+ private readonly List addedSettings = new ();
+ private readonly List spawnedToys = new ();
+ private List? settings;
+ private List? presets;
+ private LightShadows[]? shadowsType;
+ private LightType[]? lightType;
+ private LightShape[]? lightShape;
+ private SSTextArea? selectedColorTextArea;
+
+ ///
+ public override ServerSpecificSettingBase[] Settings => this.GetSettings();
+
+ ///
+ public override string Name { get; set; } = "Light Spawner";
+
+ ///
+ public override int Id { get; set; } = -5;
- private SSTextArea _selectedColorTextArea;
- private bool AnySpawned => !_spawnedToys.IsEmpty();
- private readonly List _spawnedToys = new();
+ ///
+ public override Type? MenuRelated { get; set; } = typeof(MainExample);
- public override ServerSpecificSettingBase[] Settings => GetSettings();
+ private bool AnySpawned => !this.spawnedToys.IsEmpty();
- private ServerSpecificSettingBase[] GetSettings()
+ ///
+ public override bool CheckAccess(ReferenceHub hub) => PermissionsHandler.IsPermitted(hub.serverRoles.Permissions, PlayerPermissions.FacilityManagement);
+
+ ///
+ public override void OnInput(ReferenceHub hub, ServerSpecificSettingBase setting)
+ {
+ if (setting.SettingId > ExampleId.DestroySpecific)
{
- _presets ??= new List
- {
- new("White", Color.white),
- new("Black", Color.black),
- new("Gray", Color.gray),
- new("Red", Color.red),
- new("Green", Color.green),
- new("Blue", Color.blue),
- new("Yellow", Color.yellow),
- new("Cyan", Color.cyan),
- new("Magenta", Color.magenta),
- };
-
- _shadowsType ??= EnumUtils.Values;
- _lightType ??= EnumUtils.Values;
- _lightShape ??= EnumUtils.Values;
- _selectedColorTextArea ??= new SSTextArea(ExampleId.SelectedColor, "Selected Color: None");
-
- _settings = new List
- {
- new Slider(ExampleId.Intensity, "Intensity", 0, 100, (hub, _, _) => ReloadColorInfoForUser(hub), 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Slider(ExampleId.Range, "Range", 0, 100, null, 10, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Dropdown(ExampleId.Color, "Color (preset)", _presets.Select(x => x.Name).ToArray(), (hub, _, _, _) => ReloadColorInfoForUser(hub)),
- new Plaintext(ExampleId.CustomColor, "Custom Color (R G B)", (hub, _, _) => ReloadColorInfoForUser(hub), characterLimit:11, hint: "Leave empty to use a preset."),
- _selectedColorTextArea,
- new Dropdown(ExampleId.ShadowType, "Shadows Type", _shadowsType.Select(x => x.ToString()).ToArray()),
- new Slider(ExampleId.ShadowStrength, "Shadow Strength", 0, 100),
- new Dropdown(ExampleId.LightType, "Light Type", _lightType.Select(x => x.ToString()).ToArray()),
- new Dropdown(ExampleId.LightShape, "Light Shape", _lightShape.Select(x => x.ToString()).ToArray()),
- new Slider(ExampleId.SpotAngle, "Spot Angle", 0, 100, null, 30, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Slider(ExampleId.InnerSpotAngle, "Inner Spot Angle", 0, 100, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Button(ExampleId.ConfirmSpawning, "Confirm Spawning", "Spawn", (hub, _) => Spawn(hub))
- };
- _settings.AddRange(_addedSettings);
- return _settings.ToArray();
+ this.Destroy(setting.SettingId);
}
- private void ReloadColorInfoForUser(ReferenceHub hub) => _selectedColorTextArea.SendTextUpdate(GetColorInfoForUser(hub), receiveFilter:(h) => h == hub);
-
- private void Spawn(ReferenceHub sender)
+ if (setting.SettingId == ExampleId.DestroyAll)
{
- LightSourceToy lightSourceToy = null;
- foreach (GameObject gameObject in NetworkClient.prefabs.Values)
- {
- if (gameObject.TryGetComponent(out LightSourceToy component))
- {
- lightSourceToy = UnityEngine.Object.Instantiate(component);
- lightSourceToy.OnSpawned(sender, new ArraySegment(Array.Empty()));
- break;
- }
- }
+ this.DestroyAll();
+ }
- if (lightSourceToy == null)
- return;
- lightSourceToy.NetworkLightIntensity = sender.GetParameter(ExampleId.Intensity).SyncFloatValue;
- lightSourceToy.NetworkLightRange = sender.GetParameter(ExampleId.Range).SyncFloatValue;
- Color color = GetColorInfo(sender);
- lightSourceToy.NetworkLightColor = color;
- lightSourceToy.NetworkShadowType = (LightShadows)sender.GetParameter(ExampleId.ShadowType).SyncSelectionIndexRaw;
- lightSourceToy.NetworkShadowStrength = sender.GetParameter(ExampleId.ShadowStrength).SyncFloatValue;
- lightSourceToy.NetworkLightType = (LightType)sender.GetParameter(ExampleId.LightType).SyncSelectionIndexRaw;
- lightSourceToy.NetworkLightShape = (LightShape)sender.GetParameter(ExampleId.LightShape).SyncSelectionIndexRaw;
- lightSourceToy.NetworkSpotAngle = sender.GetParameter(ExampleId.SpotAngle).SyncFloatValue;
- lightSourceToy.NetworkInnerSpotAngle = sender.GetParameter(ExampleId.InnerSpotAngle).SyncFloatValue;
-
- if (!AnySpawned)
- {
- _addedSettings.Add(new SSGroupHeader("Spawned Lights"));
- _addedSettings.Add(new Button(ExampleId.DestroyAll, "All Lights", "Destroy All (HOLD)", null, 2));
- }
+ base.OnInput(hub, setting);
+ }
- string hint =
- $"{lightSourceToy.LightType} Color: {color} SpawnPosition: {lightSourceToy.transform.position}" + "\n" + ("Spawned by " + sender.LoggedNameFromRefHub() + " at round time " + RoundStart.RoundLength.ToString("hh\\:mm\\:ss\\.fff", CultureInfo.InvariantCulture));
- _addedSettings.Add(new Button(ExampleId.DestroySpecific + (int)lightSourceToy.netId, $"Primitive NetID#{lightSourceToy.netId}", "Destroy (HOLD)", null, 0.4f, hint));
- _spawnedToys.Add(lightSourceToy);
- ReloadAll();
- }
+ ///
+ /// Gets a color info for a user.
+ ///
+ /// The player's hub.
+ /// The color string for a user.
+ public string GetColorInfoForUser(ReferenceHub hub)
+ {
+ return "Selected color: ███████████";
+ }
+
+ private ServerSpecificSettingBase[] GetSettings()
+ {
+ this.presets ??=
+ [
+ new ("White", Color.white),
+ new ("Black", Color.black),
+ new ("Gray", Color.gray),
+ new ("Red", Color.red),
+ new ("Green", Color.green),
+ new ("Blue", Color.blue),
+ new ("Yellow", Color.yellow),
+ new ("Cyan", Color.cyan),
+ new ("Magenta", Color.magenta),
+ ];
+
+ this.shadowsType ??= EnumUtils.Values;
+ this.lightType ??= EnumUtils.Values;
+ this.lightShape ??= EnumUtils.Values;
+ this.selectedColorTextArea ??= new SSTextArea(ExampleId.SelectedColor, "Selected Color: None");
+
+ this.settings =
+ [
+ new Slider(ExampleId.Intensity, "Intensity", 0, 100, (hub, _, _) => this.ReloadColorInfoForUser(hub), 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Slider(ExampleId.Range, "Range", 0, 100, null, 10, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Dropdown(ExampleId.Color, "Color (preset)", this.presets.Select(x => x.Name).ToArray(), (hub, _, _, _) => this.ReloadColorInfoForUser(hub)),
+ new Plaintext(ExampleId.CustomColor, "Custom Color (R G B)", (hub, _, _) => this.ReloadColorInfoForUser(hub), characterLimit: 11, hint: "Leave empty to use a preset."), this.selectedColorTextArea,
+ new Dropdown(ExampleId.ShadowType, "Shadows Type", this.shadowsType.Select(x => x.ToString()).ToArray()),
+ new Slider(ExampleId.ShadowStrength, "Shadow Strength", 0, 100),
+ new Dropdown(ExampleId.LightType, "Light Type", this.lightType.Select(x => x.ToString()).ToArray()),
+ new Dropdown(ExampleId.LightShape, "Light Shape", this.lightShape.Select(x => x.ToString()).ToArray()),
+ new Slider(ExampleId.SpotAngle, "Spot Angle", 0, 100, null, 30, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Slider(ExampleId.InnerSpotAngle, "Inner Spot Angle", 0, 100, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Button(ExampleId.ConfirmSpawning, "Confirm Spawning", "Spawn", (hub, _) => this.Spawn(hub)),
+ ];
+ this.settings.AddRange(this.addedSettings);
+ return this.settings.ToArray();
+ }
+
+ private void ReloadColorInfoForUser(ReferenceHub hub) => this.selectedColorTextArea?.SendTextUpdate(this.GetColorInfoForUser(hub), receiveFilter: (h) => h == hub);
- private void DestroyAll()
+ private void Spawn(ReferenceHub sender)
+ {
+ LightSourceToy? lightSourceToy = null;
+ foreach (GameObject gameObject in NetworkClient.prefabs.Values)
{
- foreach (LightSourceToy toy in _spawnedToys.ToList())
+ if (!gameObject.TryGetComponent(out LightSourceToy component))
{
- _spawnedToys.Remove(toy);
- NetworkServer.Destroy(toy.gameObject);
+ continue;
}
- _addedSettings.Clear();
- ReloadAll();
+ lightSourceToy = UnityEngine.Object.Instantiate(component);
+ lightSourceToy.OnSpawned(sender, new ArraySegment(Array.Empty()));
+ break;
}
- public override void OnInput(ReferenceHub hub, ServerSpecificSettingBase setting)
+ if (!lightSourceToy)
{
- if (setting.SettingId > ExampleId.DestroySpecific)
- Destroy(setting.SettingId);
- if (setting.SettingId == ExampleId.DestroyAll)
- DestroyAll();
+ return;
+ }
- base.OnInput(hub, setting);
+ lightSourceToy!.NetworkLightIntensity = sender.GetParameter(ExampleId.Intensity) !.SyncFloatValue;
+ lightSourceToy.NetworkLightRange = sender.GetParameter(ExampleId.Range) !.SyncFloatValue;
+ Color color = this.GetColorInfo(sender);
+ lightSourceToy.NetworkLightColor = color;
+ lightSourceToy.NetworkShadowType = (LightShadows)sender.GetParameter(ExampleId.ShadowType) !.SyncSelectionIndexRaw;
+ lightSourceToy.NetworkShadowStrength = sender.GetParameter(ExampleId.ShadowStrength) !.SyncFloatValue;
+ lightSourceToy.NetworkLightType = (LightType)sender.GetParameter(ExampleId.LightType) !.SyncSelectionIndexRaw;
+ lightSourceToy.NetworkLightShape = (LightShape)sender.GetParameter(ExampleId.LightShape) !.SyncSelectionIndexRaw;
+ lightSourceToy.NetworkSpotAngle = sender.GetParameter(ExampleId.SpotAngle) !.SyncFloatValue;
+ lightSourceToy.NetworkInnerSpotAngle = sender.GetParameter(ExampleId.InnerSpotAngle) !.SyncFloatValue;
+
+ if (!this.AnySpawned)
+ {
+ this.addedSettings.Add(new SSGroupHeader("Spawned Lights"));
+ this.addedSettings.Add(new Button(ExampleId.DestroyAll, "All Lights", "Destroy All (HOLD)", null, 2));
}
+ string hint = $"{lightSourceToy.LightType} Color: {color} SpawnPosition: {lightSourceToy.transform.position}" + "\n" + ("Spawned by " + sender.LoggedNameFromRefHub() + " at round time " + RoundStart.RoundLength.ToString(@"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture));
+ this.addedSettings.Add(new Button(ExampleId.DestroySpecific + (int)lightSourceToy.netId, $"Primitive NetID#{lightSourceToy.netId}", "Destroy (HOLD)", null, 0.4f, hint));
+ this.spawnedToys.Add(lightSourceToy);
+ this.ReloadAll();
+ }
- private void Destroy(int netId)
+ private void DestroyAll()
+ {
+ foreach (LightSourceToy toy in this.spawnedToys.ToList())
{
- if (_addedSettings.Any(x => x.SettingId == netId))
- _addedSettings.Remove(_addedSettings.First(x => x.SettingId == netId));
-
- foreach (LightSourceToy toy in _spawnedToys.ToList().Where(toy => toy.netId == netId - ExampleId.DestroySpecific))
- {
- _spawnedToys.Remove(toy);
- NetworkServer.Destroy(toy.gameObject);
- break;
- }
+ this.spawnedToys.Remove(toy);
+ NetworkServer.Destroy(toy.gameObject);
+ }
- if (!AnySpawned)
- _addedSettings.Clear();
+ this.addedSettings.Clear();
+ this.ReloadAll();
+ }
- ReloadAll();
+ private void Destroy(int netId)
+ {
+ if (this.addedSettings.Any(x => x.SettingId == netId))
+ {
+ this.addedSettings.Remove(this.addedSettings.First(x => x.SettingId == netId));
}
- public string GetColorInfoForUser(ReferenceHub hub)
+ foreach (LightSourceToy toy in this.spawnedToys.ToList().Where(toy => toy.netId == netId - ExampleId.DestroySpecific))
{
- return "Selected color: ███████████";
+ this.spawnedToys.Remove(toy);
+ NetworkServer.Destroy(toy.gameObject);
+ break;
}
- private Color GetColorInfo(ReferenceHub hub)
+ if (!this.AnySpawned)
{
- string[] array = hub.GetParameter(ExampleId.CustomColor).SyncInputText.Split(' ');
- int selectionIndex = hub.GetParameter(ExampleId.Color).SyncSelectionIndexRaw;
- Color color = _presets[selectionIndex].Color;
-
- return new Color(
- !array.TryGet(0, out string element1) || !float.TryParse(element1, out float result1)
- ? color.r
- : result1 / byte.MaxValue,
- !array.TryGet(1, out string element2) || !float.TryParse(element2, out float result2)
- ? color.g
- : result2 / byte.MaxValue,
- !array.TryGet(2, out string element3) || !float.TryParse(element3, out float result3)
- ? color.b
- : result3 / byte.MaxValue);
+ this.addedSettings.Clear();
}
- public override bool CheckAccess(ReferenceHub hub) => PermissionsHandler.IsPermitted(hub.serverRoles.Permissions, PlayerPermissions.FacilityManagement);
+ this.ReloadAll();
+ }
- public override string Name { get; set; } = "Light Spawner";
- public override int Id { get; set; } = -5;
+ private Color GetColorInfo(ReferenceHub hub)
+ {
+ string[] array = hub.GetParameter(ExampleId.CustomColor) !.SyncInputText.Split(' ');
+ int selectionIndex = hub.GetParameter(ExampleId.Color) !.SyncSelectionIndexRaw;
+ Color color = this.presets![selectionIndex].Color;
+
+#pragma warning disable SA1118 // parameter spans multiple lines.
+ return new Color(
+ !array.TryGet(0, out string element1) || !float.TryParse(element1, out float result1)
+ ? color.r
+ : result1 / byte.MaxValue,
+ !array.TryGet(1, out string element2) || !float.TryParse(element2, out float result2)
+ ? color.g
+ : result2 / byte.MaxValue,
+ !array.TryGet(2, out string element3) || !float.TryParse(element3, out float result3)
+ ? color.b
+ : result3 / byte.MaxValue);
+#pragma warning restore SA1118
+ }
+ private static class ExampleId
+ {
// ReSharper disable ConvertToConstant.Local
- private static class ExampleId
- {
- internal static readonly int Intensity = 1;
- internal static readonly int Range = 2;
- internal static readonly int Color = 3;
- internal static readonly int CustomColor = 4;
- internal static readonly int SelectedColor = 5;
- internal static readonly int ShadowType = 6;
- internal static readonly int ShadowStrength = 7;
- internal static readonly int LightType = 8;
- internal static readonly int LightShape = 9;
- internal static readonly int SpotAngle = 10;
- internal static readonly int InnerSpotAngle = 11;
- internal static readonly int ConfirmSpawning = 12;
- internal static readonly int DestroyAll = 13;
- internal static readonly int DestroySpecific = 14;
- }
+ internal static readonly int Intensity = 1;
+ internal static readonly int Range = 2;
+ internal static readonly int Color = 3;
+ internal static readonly int CustomColor = 4;
+ internal static readonly int SelectedColor = 5;
+ internal static readonly int ShadowType = 6;
+ internal static readonly int ShadowStrength = 7;
+ internal static readonly int LightType = 8;
+ internal static readonly int LightShape = 9;
+ internal static readonly int SpotAngle = 10;
+ internal static readonly int InnerSpotAngle = 11;
+ internal static readonly int ConfirmSpawning = 12;
+ internal static readonly int DestroyAll = 13;
+ internal static readonly int DestroySpecific = 14;
+
// ReSharper restore ConvertToConstant.Local
+ }
+
+#pragma warning disable SA1201 // A struct should not follow a class
+ private readonly struct ColorPreset
+ {
+ public readonly string Name;
+ public readonly Color Color;
- private readonly struct ColorPreset
+ public ColorPreset(string name, Color color)
{
- public ColorPreset(string name, Color color)
- {
- Name = name;
- Color = color;
- }
- public readonly string Name;
- public readonly Color Color;
+ this.Name = name;
+ this.Color = color;
}
-
- public override Type MenuRelated { get; set; } = typeof(MainExample);
}
-}
\ No newline at end of file
+}
+#pragma warning restore SA1011, SA1201
diff --git a/Examples/MainExample.cs b/Examples/MainExample.cs
index 86e5bcf..f12476f 100644
--- a/Examples/MainExample.cs
+++ b/Examples/MainExample.cs
@@ -1,12 +1,28 @@
-using SSMenuSystem.Features;
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+#pragma warning disable SA1010 // brackets.
+namespace SSMenuSystem.Examples;
+
+using Features;
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Examples
+///
+/// The main example menu.
+///
+internal class MainExample : Menu
{
- internal class MainExample : Menu
- {
- public override ServerSpecificSettingBase[] Settings => new ServerSpecificSettingBase[] { };
- public override string Name { get; set; } = "Examples";
- public override int Id { get; set; } = -200987;
- }
-}
\ No newline at end of file
+ ///
+ public override ServerSpecificSettingBase[] Settings => [];
+
+ ///
+ public override string Name { get; set; } = "Examples";
+
+ ///
+ public override int Id { get; set; } = -200987;
+}
+#pragma warning restore SA1010
diff --git a/Examples/PrimitiveSpawnerExample.cs b/Examples/PrimitiveSpawnerExample.cs
index 270d70e..f7e4f1c 100644
--- a/Examples/PrimitiveSpawnerExample.cs
+++ b/Examples/PrimitiveSpawnerExample.cs
@@ -1,223 +1,277 @@
-using System;
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Examples;
+
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+
using AdminToys;
-using Exiled.Events.Commands.Hub;
using GameCore;
using Mirror;
-using SSMenuSystem.Features;
-using SSMenuSystem.Features.Wrappers;
+using Features;
+using Features.Wrappers;
using UnityEngine;
using UserSettings.ServerSpecific;
using Utils.NonAllocLINQ;
-using Log = SSMenuSystem.Features.Log;
-namespace SSMenuSystem.Examples
+using Log = Features.Log;
+
+///
+/// The primitive spawner example.
+///
+internal class PrimitiveSpawnerExample : Menu
{
- internal class PrimitiveSpawnerExample : Menu
- {
- private List _settings;
- private readonly List _addedSettings = new();
- private List _presets;
- private SSTextArea _selectedColorTextArea;
- private bool AnySpawned => !_spawnedToys.IsEmpty();
- private readonly List _spawnedToys = new();
+ private readonly List addedSettings = new ();
+ private readonly List spawnedToys = new ();
+ private List? settings;
+ private List? presets;
+ private SSTextArea? selectedColorTextArea;
+
+ ///
+ public override ServerSpecificSettingBase[] Settings => this.GetSettings();
+
+ ///
+ public override string Name { get; set; } = "Primitive Spawner";
+
+ ///
+ public override int Id { get; set; } = -4;
- public override ServerSpecificSettingBase[] Settings => GetSettings();
+ ///
+ public override Type? MenuRelated { get; set; } = typeof(MainExample);
- public ServerSpecificSettingBase[] GetSettings()
+ private bool AnySpawned => !this.spawnedToys.IsEmpty();
+
+ ///
+ /// Triggered whenever a player uses an input.
+ ///
+ /// The player using the input.
+ /// The server specific setting being used.
+ public override void OnInput(ReferenceHub hub, ServerSpecificSettingBase setting)
+ {
+ if (setting.SettingId > ExampleId.DestroySpecific)
{
- _presets = new List
- {
- new("White", Color.white),
- new("Black", Color.black),
- new("Gray", Color.gray),
- new("Red", Color.red),
- new("Green", Color.green),
- new("Blue", Color.blue),
- new("Yellow", Color.yellow),
- new("Cyan", Color.cyan),
- new("Magenta", Color.magenta),
- };
-
- _selectedColorTextArea ??= new SSTextArea(ExampleId.SelectedColor, "Selected Color: None");
-
- _settings = new List
- {
- new Dropdown(ExampleId.Type, "Type", EnumUtils.Values.Select(x => x.ToString()).ToArray(), (hub, _, _, _) => ReloadColorInfoForUser(hub)),
- new Dropdown(ExampleId.Color, "Color (preset)", _presets.Select(x => x.Name).ToArray(), (hub, _, _, _) => ReloadColorInfoForUser(hub)),
- new Slider(ExampleId.Opacity, "Opacity", 0, 100, (hub, _, _) => ReloadColorInfoForUser(hub), 100, true, finalDisplayFormat: "{0}%"),
- new Plaintext(ExampleId.CustomColor, "Color", (hub, _, _) => ReloadColorInfoForUser(hub), characterLimit:11, hint: "Leave empty to use a preset."),
- _selectedColorTextArea,
- new YesNoButton(ExampleId.Collisions, "Collisions", "Enabled", "Disabled"),
- new YesNoButton(ExampleId.Renderer, "Renderer", "Visible", "Invisible", null, false, "Invisible primitives can still receive collisions."),
- new Slider(ExampleId.ScaleX, "Scale (X)", 0, 50, null, 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Slider(ExampleId.ScaleY, "Scale (Y)", 0, 50, null, 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Slider(ExampleId.ScaleZ, "Scale (Z)", 0, 50, null, 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
- new Button(ExampleId.ConfirmSpawning, "Confirm Spawning", "Spawn", (hub, _) => Spawn(hub))
- };
-
- _settings.AddRange(_addedSettings);
-
- return _settings.ToArray();
+ this.Destroy(setting.SettingId);
}
- public void ReloadColorInfoForUser(ReferenceHub hub)
+ if (setting.SettingId == ExampleId.DestroyAll)
{
- Log.Info("reload color info for user " + hub.nicknameSync.MyNick + " triggered.");
- _selectedColorTextArea.SendTextUpdate(GetColorInfoForUser(hub), receiveFilter: (h) => h == hub);
+ this.DestroyAll();
}
- public void Spawn(ReferenceHub sender)
- {
- PrimitiveObjectToy primitiveObjectToy = null;
- foreach (GameObject gameObject in NetworkClient.prefabs.Values.ToList())
- {
- if (gameObject.TryGetComponent(out PrimitiveObjectToy component))
- {
- primitiveObjectToy = UnityEngine.Object.Instantiate(component);
- primitiveObjectToy.OnSpawned(sender, new ArraySegment(Array.Empty()));
- break;
- }
- }
+ base.OnInput(hub, setting);
+ }
- if (!primitiveObjectToy)
- return;
- int selection = sender.GetParameter(ExampleId.Type).SyncSelectionIndexRaw;
- primitiveObjectToy.NetworkPrimitiveType = (PrimitiveType)selection;
-
- Color color = GetColorInfo(sender);
- primitiveObjectToy.NetworkMaterialColor = color;
- Vector3 scale = new(sender.GetParameter(ExampleId.ScaleX).SyncFloatValue,
- sender.GetParameter(ExampleId.ScaleY).SyncFloatValue,
- sender.GetParameter(ExampleId.ScaleZ).SyncFloatValue);
- primitiveObjectToy.transform.localScale = scale;
- primitiveObjectToy.NetworkScale = scale;
- PrimitiveFlags collisions =
- sender.GetParameter(ExampleId.Collisions).SyncIsA
- ? PrimitiveFlags.Collidable
- : PrimitiveFlags.None;
-
- PrimitiveFlags visible =
- sender.GetParameter(ExampleId.Renderer).SyncIsA
- ? PrimitiveFlags.Visible
- : PrimitiveFlags.None;
-
- primitiveObjectToy.NetworkPrimitiveFlags = collisions | visible;
-
- if (!AnySpawned)
- {
- _addedSettings.Add(new SSGroupHeader("Spawned Primitives"));
- _addedSettings.Add(new Button(ExampleId.DestroyAll, "All Primitives", "Destroy All (HOLD)", null, 2));
- }
- string hint =
- $"{primitiveObjectToy.PrimitiveType} Color: {color} Size: {scale} SpawnPosition: {primitiveObjectToy.transform.position}" + "\n" + "Spawned by " + sender.LoggedNameFromRefHub() + " at round time " + RoundStart.RoundLength.ToString(@"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture);
- _addedSettings.Add(new Button(ExampleId.DestroySpecific + (int)primitiveObjectToy.netId, $"Primitive NetID#{primitiveObjectToy.netId}", "Destroy (HOLD)", null, 0.4f, hint));
- _spawnedToys.Add(primitiveObjectToy); ReloadAll();
- }
+ ///
+ public override bool CheckAccess(ReferenceHub hub) => PermissionsHandler.IsPermitted(hub.serverRoles.Permissions, PlayerPermissions.FacilityManagement);
+
+ ///
+ /// Gets a color for a specific user.
+ ///
+ /// The player to get the color for.
+ /// The color string for the player.
+ // ReSharper disable once MemberCanBePrivate.Global
+ public string GetColorInfoForUser(ReferenceHub hub)
+ {
+ return "Selected color: ███████████";
+ }
+
+ ///
+ /// Gets the cached settings.
+ ///
+ /// The cached settings.
+ // ReSharper disable once MemberCanBePrivate.Global
+ public ServerSpecificSettingBase[] GetSettings()
+ {
+ this.presets =
+ [
+ new ("White", Color.white),
+ new ("Black", Color.black),
+ new ("Gray", Color.gray),
+ new ("Red", Color.red),
+ new ("Green", Color.green),
+ new ("Blue", Color.blue),
+ new ("Yellow", Color.yellow),
+ new ("Cyan", Color.cyan),
+ new ("Magenta", Color.magenta),
+ ];
+
+ this.selectedColorTextArea ??= new SSTextArea(ExampleId.SelectedColor, "Selected Color: None");
+
+ this.settings =
+ [
+ new Dropdown(ExampleId.Type, "Type", EnumUtils.Values.Select(x => x.ToString()).ToArray(), (hub, _, _, _) => this.ReloadColorInfoForUser(hub)),
+ new Dropdown(ExampleId.Color, "Color (preset)", this.presets.Select(x => x.Name).ToArray(), (hub, _, _, _) => this.ReloadColorInfoForUser(hub)),
+ new Slider(ExampleId.Opacity, "Opacity", 0, 100, (hub, _, _) => this.ReloadColorInfoForUser(hub), 100, true, finalDisplayFormat: "{0}%"),
+ new Plaintext(ExampleId.CustomColor, "Color", (hub, _, _) => this.ReloadColorInfoForUser(hub), characterLimit: 11, hint: "Leave empty to use a preset."), this.selectedColorTextArea,
+ new YesNoButton(ExampleId.Collisions, "Collisions", "Enabled", "Disabled"),
+ new YesNoButton(ExampleId.Renderer, "Renderer", "Visible", "Invisible", null, false, "Invisible primitives can still receive collisions."),
+ new Slider(ExampleId.ScaleX, "Scale (X)", 0, 50, null, 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Slider(ExampleId.ScaleY, "Scale (Y)", 0, 50, null, 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Slider(ExampleId.ScaleZ, "Scale (Z)", 0, 50, null, 1, valueToStringFormat: "0.00", finalDisplayFormat: "x{0}"),
+ new Button(ExampleId.ConfirmSpawning, "Confirm Spawning", "Spawn", (hub, _) => this.Spawn(hub)),
+ ];
+
+ this.settings.AddRange(this.addedSettings);
+
+ return this.settings.ToArray();
+ }
+
+ ///
+ /// Reloads color info for a specific user.
+ ///
+ /// The user to reload the color info for.
+ // ReSharper disable once MemberCanBePrivate.Global
+ public void ReloadColorInfoForUser(ReferenceHub hub)
+ {
+ Log.Info("reload color info for user " + hub.nicknameSync.MyNick + " triggered.");
+ this.selectedColorTextArea?.SendTextUpdate(this.GetColorInfoForUser(hub), receiveFilter: (h) => h == hub);
+ }
- private void DestroyAll()
+ ///
+ /// Spawns a primitive for a specific user.
+ ///
+ /// The user to spawn the primitive for.
+ // ReSharper disable once MemberCanBePrivate.Global
+ public void Spawn(ReferenceHub sender)
+ {
+ PrimitiveObjectToy? primitiveObjectToy = null;
+ foreach (GameObject gameObject in NetworkClient.prefabs.Values.ToList())
{
- foreach (PrimitiveObjectToy toy in _spawnedToys.ToList())
+ if (gameObject.TryGetComponent(out PrimitiveObjectToy component))
{
- NetworkServer.Destroy(toy.gameObject);
- _spawnedToys.Remove(toy);
+ primitiveObjectToy = UnityEngine.Object.Instantiate(component);
+ primitiveObjectToy.OnSpawned(sender, new ArraySegment([]));
+ break;
}
-
- _addedSettings.Clear();
- ReloadAll();
}
- public override void OnInput(ReferenceHub hub, ServerSpecificSettingBase setting)
+ if (!primitiveObjectToy)
{
- if (setting.SettingId > ExampleId.DestroySpecific)
- Destroy(setting.SettingId);
- if (setting.SettingId == ExampleId.DestroyAll)
- DestroyAll();
-
- base.OnInput(hub, setting);
+ return;
}
- private void Destroy(int netId)
+ int selection = sender.GetParameter(ExampleId.Type) !.SyncSelectionIndexRaw;
+ primitiveObjectToy!.NetworkPrimitiveType = (PrimitiveType)selection;
+
+ Color color = this.GetColorInfo(sender);
+ primitiveObjectToy.NetworkMaterialColor = color;
+ Vector3 scale = new (sender.GetParameter(ExampleId.ScaleX) !.SyncFloatValue,
+ sender.GetParameter(ExampleId.ScaleY) !.SyncFloatValue,
+ sender.GetParameter(ExampleId.ScaleZ) !.SyncFloatValue);
+ primitiveObjectToy.transform.localScale = scale;
+ primitiveObjectToy.NetworkScale = scale;
+ PrimitiveFlags collisions =
+ sender.GetParameter(ExampleId.Collisions) !.SyncIsA
+ ? PrimitiveFlags.Collidable
+ : PrimitiveFlags.None;
+
+ PrimitiveFlags visible =
+ sender.GetParameter(ExampleId.Renderer) !.SyncIsA
+ ? PrimitiveFlags.Visible
+ : PrimitiveFlags.None;
+
+ primitiveObjectToy.NetworkPrimitiveFlags = collisions | visible;
+
+ if (!this.AnySpawned)
{
- int buttonId = netId;
+ this.addedSettings.Add(new SSGroupHeader("Spawned Primitives"));
+ this.addedSettings.Add(new Button(ExampleId.DestroyAll, "All Primitives", "Destroy All (HOLD)", null, 2));
+ }
- if (_addedSettings.Any(x => x.SettingId == buttonId))
- _addedSettings.Remove(_addedSettings.First(x => x.SettingId == buttonId));
+ string hint = $"{primitiveObjectToy.PrimitiveType} Color: {color} Size: {scale} SpawnPosition: {primitiveObjectToy.transform.position}" + "\n" + "Spawned by " + sender.LoggedNameFromRefHub() + " at round time " + RoundStart.RoundLength.ToString(@"hh\:mm\:ss\.fff", CultureInfo.InvariantCulture);
+ this.addedSettings.Add(new Button(ExampleId.DestroySpecific + (int)primitiveObjectToy.netId, $"Primitive NetID#{primitiveObjectToy.netId}", "Destroy (HOLD)", null, 0.4f, hint));
+ this.spawnedToys.Add(primitiveObjectToy);
+ this.ReloadAll();
+ }
- foreach (PrimitiveObjectToy toy in _spawnedToys.ToList().Where(toy => toy.netId == netId - ExampleId.DestroySpecific))
- {
- _spawnedToys.Remove(toy);
- NetworkServer.Destroy(toy.gameObject);
- }
+ private void DestroyAll()
+ {
+ foreach (PrimitiveObjectToy toy in this.spawnedToys.ToList())
+ {
+ NetworkServer.Destroy(toy.gameObject);
+ this.spawnedToys.Remove(toy);
+ }
+
+ this.addedSettings.Clear();
+ this.ReloadAll();
+ }
- if (!AnySpawned)
- _addedSettings.Clear();
+ private void Destroy(int netId)
+ {
+ int buttonId = netId;
- ReloadAll();
+ if (this.addedSettings.Any(x => x.SettingId == buttonId))
+ {
+ this.addedSettings.Remove(this.addedSettings.First(x => x.SettingId == buttonId));
}
- public string GetColorInfoForUser(ReferenceHub hub)
+ foreach (PrimitiveObjectToy toy in this.spawnedToys.ToList().Where(toy => toy.netId == netId - ExampleId.DestroySpecific))
{
- return "Selected color: ███████████";
+ this.spawnedToys.Remove(toy);
+ NetworkServer.Destroy(toy.gameObject);
}
- private Color GetColorInfo(ReferenceHub hub)
+ if (!this.AnySpawned)
{
- string[] array = hub.GetParameter(ExampleId.CustomColor)
- .SyncInputText.Split(' ');
- int selectionIndex = hub.GetParameter(ExampleId.Color)
- .SyncSelectionIndexRaw;
- Color color = _presets[selectionIndex].Color;
- return new Color(
- !array.TryGet(0, out string element1) || !float.TryParse(element1, out float result1)
- ? color.r
- : result1 / byte.MaxValue,
- !array.TryGet(1, out string element2) || !float.TryParse(element2, out float result2)
- ? color.g
- : result2 / byte.MaxValue,
- !array.TryGet(2, out string element3) || !float.TryParse(element3, out float result3)
- ? color.b
- : result3 / byte.MaxValue,
- hub.GetParameter(ExampleId.Opacity)
- .SyncFloatValue / 100f);
+ this.addedSettings.Clear();
}
- public override bool CheckAccess(ReferenceHub hub) => PermissionsHandler.IsPermitted(hub.serverRoles.Permissions, PlayerPermissions.FacilityManagement);
+ this.ReloadAll();
+ }
- public override string Name { get; set; } = "Primitive Spawner";
- public override int Id { get; set; } = -4;
+ private Color GetColorInfo(ReferenceHub hub)
+ {
+ string[] array = hub.GetParameter(ExampleId.CustomColor) !.SyncInputText.Split(' ');
+ int selectionIndex = hub.GetParameter(ExampleId.Color) !.SyncSelectionIndexRaw;
+ Color color = this.presets![selectionIndex].Color;
+#pragma warning disable SA1118 // Spans multiple lines.
+ return new Color(
+ !array.TryGet(0, out string element1) || !float.TryParse(element1, out float result1)
+ ? color.r
+ : result1 / byte.MaxValue,
+ !array.TryGet(1, out string element2) || !float.TryParse(element2, out float result2)
+ ? color.g
+ : result2 / byte.MaxValue,
+ !array.TryGet(2, out string element3) || !float.TryParse(element3, out float result3)
+ ? color.b
+ : result3 / byte.MaxValue,
+ hub.GetParameter(ExampleId.Opacity) !.SyncFloatValue / 100f);
+#pragma warning restore SA1118
+ }
+ private static class ExampleId
+ {
// ReSharper disable ConvertToConstant.Local
- private static class ExampleId
- {
- internal static readonly int Type = 1;
- internal static readonly int Color = 2;
- internal static readonly int Opacity = 3;
- internal static readonly int CustomColor = 4;
- internal static readonly int SelectedColor = 5;
- internal static readonly int Collisions = 6;
- internal static readonly int Renderer = 7;
- internal static readonly int ScaleX = 8;
- internal static readonly int ScaleY = 9;
- internal static readonly int ScaleZ = 10;
- internal static readonly int ConfirmSpawning = 11;
- internal static readonly int DestroyAll = 12;
- internal static readonly int DestroySpecific = 13;
- }
+ internal static readonly int Type = 1;
+ internal static readonly int Color = 2;
+ internal static readonly int Opacity = 3;
+ internal static readonly int CustomColor = 4;
+ internal static readonly int SelectedColor = 5;
+ internal static readonly int Collisions = 6;
+ internal static readonly int Renderer = 7;
+ internal static readonly int ScaleX = 8;
+ internal static readonly int ScaleY = 9;
+ internal static readonly int ScaleZ = 10;
+ internal static readonly int ConfirmSpawning = 11;
+ internal static readonly int DestroyAll = 12;
+ internal static readonly int DestroySpecific = 13;
+
// ReSharper restore ConvertToConstant.Local
+ }
- private readonly struct ColorPreset
- {
- public ColorPreset(string name, Color color)
- {
- Name = name;
- Color = color;
- }
- public readonly string Name;
- public readonly Color Color;
- }
- public override Type MenuRelated { get; set; } = typeof(MainExample);
+#pragma warning disable SA1201 // Struct shouldn't follow a class.
+ private readonly struct ColorPreset(string name, Color color)
+ {
+ public readonly string Name = name;
+ public readonly Color Color = color;
}
-}
\ No newline at end of file
+
+#pragma warning restore SA1201
+}
diff --git a/Examples/TextAreaExample.cs b/Examples/TextAreaExample.cs
index f565300..e10824b 100644
--- a/Examples/TextAreaExample.cs
+++ b/Examples/TextAreaExample.cs
@@ -1,67 +1,86 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Examples;
+
using System;
using System.Collections.Generic;
+
using PlayerRoles;
using PlayerRoles.FirstPersonControl;
-using SSMenuSystem.Features;
-using SSMenuSystem.Features.Wrappers;
+using Features;
+using Features.Wrappers;
using UnityEngine;
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Examples
+///
+/// The text area example menu.
+///
+internal class TextAreaExample : Menu
{
- internal class TextAreaExample : Menu
- {
- public override ServerSpecificSettingBase[] Settings => GetSettings();
- private List _settings;
- private SSTextArea _responseScan;
+ private List? settings;
+ private SSTextArea? responseScan;
- private ServerSpecificSettingBase[] GetSettings()
- {
- _responseScan ??= new SSTextArea(null, "Not Scanned yet");
- _settings = new List
- {
- new SSGroupHeader("Different Text Area Types"),
- new SSTextArea(1,
- "This text area supports Rich Text Tags."),
- new SSTextArea(2, "This is another multi-line text area, but this one features auto-generated preview text when collapsed, with ellipses appearing when the text no longer fits. It also has an option enabled to collapse automatically when you switch off this settings tab. In other words, you will need to re-expand this text area each time you visit this tab.", SSTextArea.FoldoutMode.CollapseOnEntry),
- new SSTextArea(3, "This multi-line text area is expanded by default but can be collapsed if needed. It will retain its previous state when toggling this tab on and off.", SSTextArea.FoldoutMode.ExtendedByDefault),
- new SSTextArea(4, "This multi-line text area is similar to the one above, but it will re-expand itself after collapsing each time you visit this tab.", SSTextArea.FoldoutMode.ExtendOnEntry),
- new SSTextArea(5, "This multi-line text area cannot be collapsed.\nIt remains fully expanded at all times, but supports URL links.\nExample link: [Click]"),
- new SSGroupHeader("Human Scanner", false, "Generates a list of alive humans with their distances to you. The size is automatically adjusted based on the number of results."),
- _responseScan,
- new Button(6, "Scan for players.", "Scan", (hub, _) => OnScannerButtonPressed(hub))
- };
+ ///
+ public override ServerSpecificSettingBase[] Settings => this.GetSettings();
- return _settings.ToArray();
- }
- public override string Name { get; set; } = "Text Area";
- public override int Id { get; set; } = -7;
+ ///
+ public override string Name { get; set; } = "Text Area";
+
+ ///
+ public override int Id { get; set; } = -7;
+ ///
+ public override Type? MenuRelated { get; set; } = typeof(MainExample);
- public void OnScannerButtonPressed(ReferenceHub sender)
+ ///
+ /// Triggered whenever the scanner button is pressed.
+ ///
+ /// The player that pressed the button.
+ private void OnScannerButtonPressed(ReferenceHub sender)
+ {
+ if (!(sender.roleManager.CurrentRole is IFpcRole currentRole1))
{
- if (!(sender.roleManager.CurrentRole is IFpcRole currentRole1))
- {
- this._responseScan.SendTextUpdate("Your current role is not supported.", false, x => x == sender);
- }
- else
+ this.responseScan?.SendTextUpdate("Your current role is not supported.", false, x => x == sender);
+ }
+ else
+ {
+ string? str1 = null;
+ foreach (ReferenceHub allHub in ReferenceHub.AllHubs)
{
- string str1 = null;
- foreach (ReferenceHub allHub in ReferenceHub.AllHubs)
+ if (allHub.roleManager.CurrentRole is HumanRole currentRole && !(allHub == sender))
{
- if (allHub.roleManager.CurrentRole is HumanRole currentRole && !(allHub == sender))
- {
- float num = Vector3.Distance(currentRole.FpcModule.Position, currentRole1.FpcModule.Position);
- string str2 =
- $"\n-{allHub.nicknameSync.DisplayName} ({currentRole.GetColoredName()}) - {num} m";
- if (str1 == null)
- str1 = "Detected humans: ";
- str1 += str2;
- }
+ float num = Vector3.Distance(currentRole.FpcModule.Position, currentRole1.FpcModule.Position);
+ string str2 = $"\n-{allHub.nicknameSync.DisplayName} ({currentRole.GetColoredName()}) - {num} m";
+ str1 ??= "Detected humans: ";
+
+ str1 += str2;
}
- this._responseScan.SendTextUpdate(str1 ?? "No humans detected.", false, x => x == sender);
}
+
+ this.responseScan?.SendTextUpdate(str1 ?? "No humans detected.", false, x => x == sender);
}
- public override Type MenuRelated { get; set; } = typeof(MainExample);
+ }
+
+ private ServerSpecificSettingBase[] GetSettings()
+ {
+ this.responseScan ??= new SSTextArea(null, "Not Scanned yet");
+ this.settings =
+ [
+ new SSGroupHeader("Different Text Area Types"),
+ new SSTextArea(1, "This text area supports Rich Text Tags."),
+ new SSTextArea(2, "This is another multi-line text area, but this one features auto-generated preview text when collapsed, with ellipses appearing when the text no longer fits. It also has an option enabled to collapse automatically when you switch off this settings tab. In other words, you will need to re-expand this text area each time you visit this tab.", SSTextArea.FoldoutMode.CollapseOnEntry),
+ new SSTextArea(3, "This multi-line text area is expanded by default but can be collapsed if needed. It will retain its previous state when toggling this tab on and off.", SSTextArea.FoldoutMode.ExtendedByDefault),
+ new SSTextArea(4, "This multi-line text area is similar to the one above, but it will re-expand itself after collapsing each time you visit this tab.", SSTextArea.FoldoutMode.ExtendOnEntry),
+ new SSTextArea(5, "This multi-line text area cannot be collapsed.\nIt remains fully expanded at all times, but supports URL links.\nExample link: [Click]"),
+ new SSGroupHeader("Human Scanner", false, "Generates a list of alive humans with their distances to you. The size is automatically adjusted based on the number of results."), this.responseScan,
+ new Button(6, "Scan for players.", "Scan", (hub, _) => this.OnScannerButtonPressed(hub)),
+ ];
+
+ return this.settings.ToArray();
}
}
\ No newline at end of file
diff --git a/Features/AssemblyMenu.cs b/Features/AssemblyMenu.cs
index 27dd53b..15767e7 100644
--- a/Features/AssemblyMenu.cs
+++ b/Features/AssemblyMenu.cs
@@ -1,20 +1,61 @@
-using System;
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+#pragma warning disable SA1010, SA1011 // Opening square brackets should be spaced correctly. Closing square bracket should be followed by a space.
+namespace SSMenuSystem.Features;
+
using System.Collections.Generic;
using System.Reflection;
+
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Features
+///
+/// The Assembly Menu.
+///
+internal class AssemblyMenu : Menu
{
- internal class AssemblyMenu : Menu
- {
- internal Assembly Assembly { get; set; }
- internal ServerSpecificSettingBase[] OverrideSettings { get; set; }
- public override ServerSpecificSettingBase[] Settings => OverrideSettings ?? Array.Empty();
- public override string Name { get; set; }
- public override int Id { get; set; }
- public override bool CheckAccess(ReferenceHub hub) =>
- (ActuallySendedToClient.TryGetValue(hub, out ServerSpecificSettingBase[] settings) && settings != null && !settings.IsEmpty()) ||
- (OverrideSettings != null && !OverrideSettings.IsEmpty());
- internal Dictionary ActuallySendedToClient { get; set; } = new();
- }
-}
\ No newline at end of file
+ ///
+ /// Gets the server specific settings.
+ ///
+ public override ServerSpecificSettingBase[] Settings => this.OverrideSettings ?? [];
+
+ ///
+ /// Gets or sets the name.
+ ///
+ public override string Name { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the id of the menu.
+ ///
+ public override int Id { get; set; }
+
+ ///
+ /// Gets or Sets the dictionary of server specific settings actually sent to the client.
+ ///
+ internal Dictionary ActuallySentToClient { get; set; } = new ();
+
+ ///
+ /// Gets or sets the Assembly.
+ ///
+ internal Assembly? Assembly { get; set; }
+
+ ///
+ /// Gets or sets the override settings.
+ ///
+ internal ServerSpecificSettingBase[]? OverrideSettings { get; set; }
+
+ ///
+ /// Checks access for a specific player.
+ ///
+ /// The player to check.
+ /// True if the player should have access otherwise false.
+ public override bool CheckAccess(ReferenceHub hub) =>
+ (this.ActuallySentToClient.TryGetValue(hub, out ServerSpecificSettingBase[] settings) && settings != null && !settings.IsEmpty()) ||
+ (this.OverrideSettings != null && !this.OverrideSettings.IsEmpty());
+}
+
+#pragma warning restore SA1010, SA1011
diff --git a/Features/CompatibilityConfig.cs b/Features/CompatibilityConfig.cs
new file mode 100644
index 0000000..1a9a3d8
--- /dev/null
+++ b/Features/CompatibilityConfig.cs
@@ -0,0 +1,25 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Features;
+
+using System.ComponentModel;
+
+///
+/// The compatibility configs.
+///
+public sealed class CompatibilityConfig
+{
+ ///
+ /// Gets or sets a value indicating whether the compatibility system will be enabled and all plugins that use SSSystem will be registered as menu.
+ ///
+ [Description("If enabled, the comptability system will be enabled and all plugins that use SSSystem will be registered as menu.")]
+ public bool CompatibilityEnabled { get; set; } = true;
+
+ /*[Description("If enabled, all keybinds on compatibility menu will be marked as global (displayed on all screens).")]
+ public bool KeybindsAllGlobal { get; set; } = true;*/
+}
\ No newline at end of file
diff --git a/Features/ComptabilityConfig.cs b/Features/ComptabilityConfig.cs
deleted file mode 100644
index cb20d5f..0000000
--- a/Features/ComptabilityConfig.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.ComponentModel;
-
-namespace SSMenuSystem.Features
-{
- ///
- /// The comptability configs.
- ///
- public class ComptabilityConfig
- {
- ///
- /// If enabled, the comptability system will be enabled and all plugins that use SSSystem will be registered as menu.
- ///
- [Description("If enabled, the comptability system will be enabled and all plugins that use SSSystem will be registered as menu.")]
- public bool ComptabilityEnabled { get; set; } = true;
-
- /*[Description("If enabled, all keybinds on comptability menu will be marked as global (displayed on all screens).")]
- public bool KeybindsAllGlobal { get; set; } = true;*/
- }
-}
\ No newline at end of file
diff --git a/Features/Interfaces/ISetting.cs b/Features/Interfaces/ISetting.cs
index f94fa99..b40f690 100644
--- a/Features/Interfaces/ISetting.cs
+++ b/Features/Interfaces/ISetting.cs
@@ -1,9 +1,21 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+namespace SSMenuSystem.Features.Interfaces;
+
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Features.Interfaces
+///
+/// An interface for defining settings.
+///
+internal interface ISetting
{
- internal interface ISetting
- {
- ServerSpecificSettingBase Base { get; }
- }
+ ///
+ /// Gets the base component for this item.
+ ///
+ ServerSpecificSettingBase Base { get; }
}
\ No newline at end of file
diff --git a/Features/Log.cs b/Features/Log.cs
index a0edb4e..82df2f6 100644
--- a/Features/Log.cs
+++ b/Features/Log.cs
@@ -1,142 +1,161 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+
+// ReSharper disable MemberCanBePrivate.Global
+namespace SSMenuSystem.Features;
+
using System;
+
using Discord;
-namespace SSMenuSystem.Features
+///
+/// An internal logging class.
+///
+internal static class Log
{
- internal static class Log
+ ///
+ /// Sends a level messages to the game console.
+ ///
+ /// The message to be sent.
+ internal static void Info(object message)
{
- ///
- /// Sends a level messages to the game console.
- ///
- /// The message to be sent.
- internal static void Info(object message)
- {
- Send($"[SSMenuSystem] {message}", LogLevel.Info, ConsoleColor.Cyan);
- }
+ Send($"[SSMenuSystem] {message}", LogLevel.Info, ConsoleColor.Cyan);
+ }
- ///
- /// Sends a level messages to the game console.
- ///
- /// The message to be sent.
- internal static void Info(string message)
- {
- Send($"[SSMenuSystem] {message}", LogLevel.Info, ConsoleColor.Cyan);
- }
+ ///
+ /// Sends a level messages to the game console.
+ ///
+ /// The message to be sent.
+ internal static void Info(string message)
+ {
+ Send($"[SSMenuSystem] {message}", LogLevel.Info, ConsoleColor.Cyan);
+ }
- ///
- /// Sends a level messages to the game console.
- /// Server must have exiled_debug config enabled.
- ///
- /// The message to be sent.
- internal static void Debug(object message)
+ ///
+ /// Sends a level messages to the game console.
+ /// Server must have exiled_debug config enabled.
+ ///
+ /// The message to be sent.
+ internal static void Debug(object message)
+ {
+ if (Plugin.Instance?.Config?.Debug ?? false)
{
- if (Plugin.Instance?.Config?.Debug ?? false)
- Send($"[SSMenuSystem] {message}", LogLevel.Debug, ConsoleColor.Green);
+ Send($"[SSMenuSystem] {message}", LogLevel.Debug, ConsoleColor.Green);
}
+ }
- ///
- /// Sends a level messages to the game console.
- /// Server must have exiled_debug config enabled.
- ///
- /// The message to be sent.
- internal static void Debug(string message)
+ ///
+ /// Sends a level messages to the game console.
+ /// Server must have exiled_debug config enabled.
+ ///
+ /// The message to be sent.
+ internal static void Debug(string message)
+ {
+ if (Plugin.Instance?.Config?.Debug ?? false)
{
- if (Plugin.Instance?.Config?.Debug ?? false)
- Send($"[SSMenuSystem] {message}", LogLevel.Debug, ConsoleColor.Green);
+ Send($"[SSMenuSystem] {message}", LogLevel.Debug, ConsoleColor.Green);
}
+ }
- ///
- /// Sends a level messages to the game console.
- ///
- /// The message to be sent.
- internal static void Warn(object message)
- {
- Log.Send($"[SSMenuSystem] {message}", LogLevel.Warn, ConsoleColor.Magenta);
- }
+ ///
+ /// Sends a level messages to the game console.
+ ///
+ /// The message to be sent.
+ internal static void Warn(object message)
+ {
+ Send($"[SSMenuSystem] {message}", LogLevel.Warn, ConsoleColor.Magenta);
+ }
- ///
- /// Sends a level messages to the game console.
- ///
- /// The message to be sent.
- internal static void Warn(string message)
- {
- Log.Send($"[SSMenuSystem] {message}", LogLevel.Warn, ConsoleColor.Magenta);
- }
+ ///
+ /// Sends a level messages to the game console.
+ ///
+ /// The message to be sent.
+ internal static void Warn(string message)
+ {
+ Send($"[SSMenuSystem] {message}", LogLevel.Warn, ConsoleColor.Magenta);
+ }
- ///
- /// Sends a level messages to the game console.
- /// This should be used to send errors only.
- /// It's recommended to send any messages in the catch block of a try/catch as errors with the exception string.
- ///
- /// The message to be sent.
- internal static void Error(object message)
- {
- Log.Send($"[SSMenuSystem] {message}", LogLevel.Error, ConsoleColor.DarkRed);
- }
+ ///
+ /// Sends a level messages to the game console.
+ /// This should be used to send errors only.
+ /// It's recommended to send any messages in the catch block of a try/catch as errors with the exception string.
+ ///
+ /// The message to be sent.
+ internal static void Error(object message)
+ {
+ Send($"[SSMenuSystem] {message}", LogLevel.Error, ConsoleColor.DarkRed);
+ }
- ///
- /// Sends a level messages to the game console.
- /// This should be used to send errors only.
- /// It's recommended to send any messages in the catch block of a try/catch as errors with the exception string.
- ///
- /// The message to be sent.
- internal static void Error(string message)
- {
- Send($"[SSMenuSystem] {message}", LogLevel.Error, ConsoleColor.DarkRed);
- }
+ ///
+ /// Sends a level messages to the game console.
+ /// This should be used to send errors only.
+ /// It's recommended to send any messages in the catch block of a try/catch as errors with the exception string.
+ ///
+ /// The message to be sent.
+ internal static void Error(string message)
+ {
+ Send($"[SSMenuSystem] {message}", LogLevel.Error, ConsoleColor.DarkRed);
+ }
- /// Sends a log message to the game console.
- /// The message to be sent.
- /// The message level of importance.
- /// The message color.
- internal static void Send(object message, LogLevel level, ConsoleColor color = ConsoleColor.Gray)
- {
- SendRaw($"[{level.ToString().ToUpper()}] {message}", color);
- }
+ /// Sends a log message to the game console.
+ /// The message to be sent.
+ /// The message level of importance.
+ /// The message color.
+ internal static void Send(object message, LogLevel level, ConsoleColor color = ConsoleColor.Gray)
+ {
+ SendRaw($"[{level.ToString().ToUpper()}] {message}", color);
+ }
- /// Sends a log message to the game console.
- /// The message to be sent.
- /// The message level of importance.
- /// The message color.
- internal static void Send(string message, LogLevel level, ConsoleColor color = ConsoleColor.Gray)
- {
- SendRaw("[" + level.ToString().ToUpper() + "] " + message, color);
- }
+ /// Sends a log message to the game console.
+ /// The message to be sent.
+ /// The message level of importance.
+ /// The message color.
+ internal static void Send(string message, LogLevel level, ConsoleColor color = ConsoleColor.Gray)
+ {
+ SendRaw("[" + level.ToString().ToUpper() + "] " + message, color);
+ }
- /// Sends a raw log message to the game console.
- /// The message to be sent.
- /// The of the message.
- internal static void SendRaw(object message, ConsoleColor color)
- {
- ServerConsole.AddLog(message.ToString(), color);
- }
+ /// Sends a raw log message to the game console.
+ /// The message to be sent.
+ /// The of the message.
+ internal static void SendRaw(object message, ConsoleColor color)
+ {
+ ServerConsole.AddLog(message.ToString(), color);
+ }
- /// Sends a raw log message to the game console.
- /// The message to be sent.
- /// The of the message.
- internal static void SendRaw(string message, ConsoleColor color)
- {
- ServerConsole.AddLog(message, color);
- }
+ /// Sends a raw log message to the game console.
+ /// The message to be sent.
+ /// The of the message.
+ internal static void SendRaw(string message, ConsoleColor color)
+ {
+ ServerConsole.AddLog(message, color);
+ }
- ///
- /// Sends an with the provided message if the condition is false and stops the execution.
- /// For example:
- ///
- /// Player ply = Player.Get(2);
- /// Log.Assert(ply is not null, "The player with the id 2 is null");
- ///
- /// results in it logging an error if the player is null and not continuing.
- ///
- ///
- /// The conditional expression to evaluate. If the condition is true it will continue.
- /// The information message. The error and exception will show this message.
- /// If the condition is false. It throws an exception stopping the execution.
- internal static void Assert(bool condition, object message)
+ ///
+ /// Sends an with the provided message if the condition is false and stops the execution.
+ /// For example:
+ ///
+ /// Player ply = Player.Get(2);
+ /// Log.Assert(ply is not null, "The player with the id 2 is null");
+ ///
+ /// results in it logging an error if the player is null and not continuing.
+ ///
+ ///
+ /// The conditional expression to evaluate. If the condition is true it will continue.
+ /// The information message. The error and exception will show this message.
+ /// If the condition is false. It throws an exception stopping the execution.
+ internal static void Assert(bool condition, object message)
+ {
+ if (condition)
{
- if (condition) return;
- Error(message);
- throw new Exception(message.ToString());
+ return;
}
+
+ Error(message);
+ throw new Exception(message.ToString());
}
}
\ No newline at end of file
diff --git a/Features/Menu.cs b/Features/Menu.cs
index ac941af..7023958 100644
--- a/Features/Menu.cs
+++ b/Features/Menu.cs
@@ -1,576 +1,714 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Skyfr0676 and Redforce04. All rights reserved.
+// Licensed under the Undetermined license.
+//
+// -----------------------------------------------------------------------
+#pragma warning disable SA1018, SA1124, SA1401 // Nullable symbol should be preceded with a space. Do not use regions. Field should be made private.
+namespace SSMenuSystem.Features;
+
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
+
using MEC;
-using SSMenuSystem.Examples;
-using SSMenuSystem.Features.Interfaces;
-using SSMenuSystem.Features.Wrappers;
+using Examples;
+using Interfaces;
+using JetBrains.Annotations;
+using Wrappers;
using UnityEngine;
using UserSettings.ServerSpecific;
-namespace SSMenuSystem.Features
+///
+/// The Menu system.
+///
+public abstract class Menu
{
+#region Fields
+
+ ///
+ /// All synced parameters for a specified .
+ ///
+ internal readonly Dictionary> InternalSettingsSync = new ();
+
+ ///
+ /// Gets all settings sent to the refHub. (only in the case of one GetSettings is not null or empty).
+ ///
+ internal readonly Dictionary SentSettings = new ();
+
+ private static readonly Dictionary MenuSync = new ();
+ private static readonly List