C#:
// #define TESTING
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Facepunch;
using Facepunch.Extend;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Plugins.ServerPanelExtensionMethods;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using Global = Rust.Global;
using Random = UnityEngine.Random;
using Time = UnityEngine.Time;
#if CARBON
using Carbon.Base;
using Carbon.Modules;
#endif
namespace Oxide.Plugins
{
[Info("ServerPanel", "Mevent", "2.0.9")]
public class ServerPanel : RustPlugin
{
#region Fields
[PluginReference] private Plugin
ImageLibrary = null,
NoEscape = null,
Notify = null,
UINotify = null,
KillRecords = null,
Statistics = null,
UltimateLeaderboard = null,
ServerPanelPopUps = null,
ServerPanelMigrations = null;
private static ServerPanel Instance;
#if CARBON
private ImageDatabaseModule imageDatabase;
#endif
private bool _enabledImageLibrary;
private readonly Dictionary<ulong, float> _lastCommandTime = new();
private Dictionary<int, int> _categoriesByID = new();
private Dictionary<string, (int, int)> _categoriesByCommand = new();
private Dictionary<int, Coroutine> _categoriesActiveCoroutines = new();
private Dictionary<string, Func<BasePlayer, string>> _headerUpdateFields;
private Dictionary<string, List<string>> _headerUpdateFieldsByPlugin = new();
private const string
Perm_Edit = "serverpanel.edit",
CmdMainConsole = "UI_ServerPanel",
CmdExample = "test_command_123",
Layer = "UI.Server.Panel",
ElementsLayer = "UI.Server.Panel.Elements",
LayerHeader = "UI.Server.Panel.Header",
LayerContent = "UI.Server.Panel.Content",
LayerContentElements = "UI.Server.Panel.Content.Elements",
LayerContentElementsStatic = "UI.Server.Panel.Content.Elements.Static",
LayerCategories = "UI.Server.Panel.Categories",
EditingLayerPageEditor = "UI.Server.Panel.Editor.Page",
EditingLayerElementEditor = "UI.Server.Panel.Editor.Element",
EditingLayerModal = "UI.Server.Panel.Editor.Modal",
EditingLayerModalArrayView = EditingLayerModal + ".Content.View",
EditingLayerModalColorSelector = "UI.Server.Panel.Editor.Modal.Color.Selector",
EditingLayerModalTextEditor = "UI.Server.Panel.Editor.Modal.Text.Editor",
EditingLayerModalPreClose = "UI.Server.Panel.Editor.Modal.PreClose",
EditingElementOutline = "UI.Server.Panel.Editor.EditingElement.Outline";
private HashSet<string> _registredCommands = new();
private bool _migrationRequired;
private string _migrationName;
private bool _dataLoaded;
private bool _migrationInProgress;
private bool _waitingForMigrationsPlugin;
#endregion
#region Config
private static Configuration _config;
private class Configuration
{
#region Fields
[JsonProperty(PropertyName = "Work with Notify?")]
public bool UseNotify = true;
[JsonProperty(PropertyName = "Enable Offline Image Mode")]
public bool EnableOfflineImageMode = false;
[JsonProperty(PropertyName = "Cooldown between actions (in seconds)")]
public float CooldownBetweenActions = 0.2f;
[JsonProperty(PropertyName = "Economy Header Fields",
ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<EconomyHeadField> EconomyFields = new()
{
EconomyHeadField.Create(true, EconomyEntry.CreateEconomics(), "{economy_economics}"),
EconomyHeadField.Create(true, EconomyEntry.CreateServerRewards(), "{economy_server_rewards}"),
EconomyHeadField.Create(true, EconomyEntry.CreateBankSystem(), "{economy_bank_system}")
};
[JsonProperty(PropertyName = "Block Settings")]
public BlockSettings Block = new()
{
BlockWhenBuildingBlock = false,
BlockWhenRaidBlock = false,
BlockWhenCombatBlock = false
};
[JsonProperty(PropertyName = "Auto-Open Settings")]
public AutoOpenSettings AutoOpen = new()
{
ShowMenuEveryTime = true
};
public VersionNumber Version;
#endregion Fields
#region Classes
public class AutoOpenSettings
{
[JsonProperty(PropertyName = "Show menu every time player connects to server?")]
public bool ShowMenuEveryTime = true;
}
public class BlockSettings
{
[JsonProperty(PropertyName = "Block the opening during a building block?")]
public bool BlockWhenBuildingBlock;
[JsonProperty(PropertyName = "Block the opening during a raid block?")]
public bool BlockWhenRaidBlock;
[JsonProperty(PropertyName = "Block the opening during a combat block?")]
public bool BlockWhenCombatBlock;
}
public class EconomyHeadField
{
#region Fields
[JsonProperty(PropertyName = "Enabled")]
public bool Enabled;
[JsonProperty(PropertyName = "Economy Settings")]
public EconomyEntry Economy = new();
[JsonProperty(PropertyName = "Update key (MUST BE UNIQUE)")]
public string UpdateKey;
#endregion
#region Constructors
public static EconomyHeadField Create(bool enabled, EconomyEntry economy, string updateKey)
{
return new EconomyHeadField
{
Enabled = enabled,
Economy = economy,
UpdateKey = updateKey
};
}
#endregion
}
public enum EconomyType
{
Plugin,
Item
}
public class EconomyEntry
{
#region Fields
[JsonProperty(PropertyName = "Type (Plugin/Item)")] [JsonConverter(typeof(StringEnumConverter))]
public EconomyType Type;
[JsonProperty(PropertyName = "Plugin name")]
public string Plug;
[JsonProperty(PropertyName = "Balance add hook")]
public string AddHook;
[JsonProperty(PropertyName = "Balance remove hook")]
public string RemoveHook;
[JsonProperty(PropertyName = "Balance show hook")]
public string BalanceHook;
[JsonProperty(PropertyName = "ShortName")]
public string ShortName;
[JsonProperty(PropertyName = "Display Name (empty - default)")]
public string DisplayName;
[JsonProperty(PropertyName = "Skin")] public ulong Skin;
[JsonProperty(PropertyName = "Lang Key (for Title)")]
public string TitleLangKey;
[JsonProperty(PropertyName = "Lang Key (for Balance)")]
public string BalanceLangKey;
#endregion Fields
#region Public Methods
#region Titles
public string GetTitle(BasePlayer player)
{
return Instance.Msg(player, TitleLangKey);
}
public string GetBalanceTitle(BasePlayer player)
{
return Instance.Msg(player, BalanceLangKey, ShowBalance(player).ToString());
}
#endregion Titles
#region Economy
public double ShowBalance(BasePlayer player)
{
switch (Type)
{
case EconomyType.Plugin:
{
var plugin = Instance?.plugins?.Find(Plug);
if (plugin == null)
return 0;
return Convert.ToDouble(plugin.Call(BalanceHook, player.UserIDString));
}
case EconomyType.Item:
{
return PlayerItemsCount(player, ShortName, Skin);
}
default:
return 0;
}
}
public void AddBalance(BasePlayer player, double amount)
{
switch (Type)
{
case EconomyType.Plugin:
{
var plugin = Instance?.plugins.Find(Plug);
if (plugin == null) return;
switch (Plug)
{
case "BankSystem":
case "ServerRewards":
case "IQEconomic":
plugin.Call(AddHook, player.UserIDString, (int) amount);
break;
default:
plugin.Call(AddHook, player.UserIDString, amount);
break;
}
break;
}
case EconomyType.Item:
{
var am = (int) amount;
var item = ToItem(am);
if (item == null) return;
player.GiveItem(item);
break;
}
}
}
public bool RemoveBalance(BasePlayer player, double amount)
{
switch (Type)
{
case EconomyType.Plugin:
{
if (ShowBalance(player) < amount) return false;
var plugin = Instance?.plugins.Find(Plug);
if (plugin == null) return false;
switch (Plug)
{
case "BankSystem":
case "ServerRewards":
case "IQEconomic":
plugin.Call(RemoveHook, player.UserIDString, (int) amount);
break;
default:
plugin.Call(RemoveHook, player.UserIDString, amount);
break;
}
return true;
}
case EconomyType.Item:
{
var playerItems = Pool.Get<List<Item>>();
player.inventory.GetAllItems(playerItems);
var am = (int) amount;
if (ItemCount(playerItems, ShortName, Skin) < am)
{
Pool.Free(ref playerItems);
return false;
}
Take(playerItems, ShortName, Skin, am);
Pool.Free(ref playerItems);
return true;
}
default:
return false;
}
}
#endregion Economy
#endregion
#region Private Methods
private Item ToItem(int amount)
{
var item = ItemManager.CreateByName(ShortName, amount, Skin);
if (item == null)
{
Debug.LogError($"Error creating item with ShortName: '{ShortName}'");
return null;
}
if (!string.IsNullOrEmpty(DisplayName)) item.name = DisplayName;
return item;
}
private static int PlayerItemsCount(BasePlayer player, string shortname, ulong skin)
{
var items = Pool.Get<List<Item>>();
player.inventory.GetAllItems(items);
var result = ItemCount(items, shortname, skin);
Pool.Free(ref items);
return result;
}
private static int ItemCount(List<Item> items, string shortname, ulong skin)
{
return items.FindAll(item =>
item.info.shortname == shortname && !item.isBroken && (skin == 0 || item.skin == skin))
.Sum(item => item.amount);
}
private static void Take(List<Item> itemList, string shortname, ulong skinId, int amountToTake)
{
if (amountToTake == 0) return;
var takenAmount = 0;
var itemsToTake = Pool.Get<List<Item>>();
foreach (var item in itemList)
{
if (item.info.shortname != shortname ||
(skinId != 0 && item.skin != skinId) || item.isBroken) continue;
var remainingAmount = amountToTake - takenAmount;
if (remainingAmount <= 0) break;
if (item.amount > remainingAmount)
{
item.MarkDirty();
item.amount -= remainingAmount;
break;
}
if (item.amount <= remainingAmount)
{
takenAmount += item.amount;
itemsToTake.Add(item);
}
if (takenAmount == amountToTake)
break;
}
foreach (var itemToTake in itemsToTake)
itemToTake.RemoveFromContainer();
Pool.FreeUnmanaged(ref itemsToTake);
}
#endregion Private Methods
#region Constructors
public static EconomyEntry CreateEconomics()
{
return new EconomyEntry
{
Type = EconomyType.Plugin,
Plug = "Economics",
TitleLangKey = "Economy.Economics.Title",
BalanceLangKey = "Economy.Economics.Balance",
AddHook = "Deposit",
BalanceHook = "Balance",
RemoveHook = "Withdraw",
ShortName = string.Empty,
DisplayName = string.Empty,
Skin = 0
};
}
public static EconomyEntry CreateServerRewards()
{
return new EconomyEntry
{
Type = EconomyType.Plugin,
Plug = "ServerRewards",
TitleLangKey = "Economy.ServerRewards.Title",
BalanceLangKey = "Economy.ServerRewards.Balance",
AddHook = "AddPoints",
BalanceHook = "CheckPoints",
RemoveHook = "TakePoints",
ShortName = string.Empty,
DisplayName = string.Empty,
Skin = 0
};
}
public static EconomyEntry CreateBankSystem()
{
return new EconomyEntry
{
Type = EconomyType.Plugin,
Plug = "BankSystem",
TitleLangKey = "Economy.BankSystem.Title",
BalanceLangKey = "Economy.BankSystem.Balance",
AddHook = "API_BankSystemDeposit",
BalanceHook = "API_BankSystemBalance",
RemoveHook = "API_BankSystemWithdraw",
ShortName = string.Empty,
DisplayName = string.Empty,
Skin = 0
};
}
public static EconomyEntry CreateIQEconomic()
{
return new EconomyEntry
{
Type = EconomyType.Plugin,
Plug = "IQEconomic",
TitleLangKey = "Economy.IQEconomic.Title",
BalanceLangKey = "Economy.IQEconomic.Balance",
AddHook = "API_SET_BALANCE",
BalanceHook = "API_GET_BALANCE",
RemoveHook = "API_REMOVE_BALANCE",
ShortName = string.Empty,
DisplayName = string.Empty,
Skin = 0
};
}
public static EconomyEntry CreateScrap()
{
return new EconomyEntry
{
Type = EconomyType.Item,
Plug = string.Empty,
TitleLangKey = "Economy.Scrap.Title",
BalanceLangKey = "Economy.Scrap.Balance",
AddHook = string.Empty,
BalanceHook = string.Empty,
RemoveHook = string.Empty,
ShortName = "scrap",
DisplayName = string.Empty,
Skin = 0
};
}
#endregion Constructors
}
#endregion
}
private bool hasConfigFile = true;
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<Configuration>();
if (_config == null) throw new Exception();
if (_config.Version < Version)
UpdateConfigValues();
SaveConfig();
}
catch (Exception ex)
{
PrintError("Your configuration file contains an error. Using default configuration values.");
LoadDefaultConfig();
Debug.LogException(ex);
}
}
protected override void SaveConfig()
{
Config.WriteObject(_config);
}
protected override void LoadDefaultConfig()
{
hasConfigFile = false;
_config = new Configuration();
}
private void UpdateConfigValues()
{
if (hasConfigFile)
{
if (_config.Version == default) _migrationRequired = true;
if (_config.Version == new VersionNumber(2, 0, 0)) _migrationRequired = true;
if (_config.Version == new VersionNumber(2, 0, 1)) _migrationRequired = true;
if (_migrationRequired)
{
_migrationName = "all";
return;
}
PrintWarning("Config update completed!");
}
_config.Version = Version;
}
#endregion Config
#region Data
#region Data.General
public void SaveData()
{
SaveCategoriesData();
SaveTemplateData();
SaveLocalizationData();
SaveHeaderFieldsData();
SavePlayersData();
}
private void LoadData()
{
LoadCategoriesData();
LoadTemplateData();
LoadLocalizationData();
LoadHeaderFieldsData();
LoadPlayersData();
}
private void LoadDataFromFile<T>(ref T data, string filePath)
{
try
{
data = Interface.Oxide.DataFileSystem.ReadObject<T>(Path.Combine(Name, filePath));
}
catch (Exception e)
{
PrintError(e.ToString());
}
data ??= Activator.CreateInstance<T>();
}
private void SaveDataToFile<T>(T data, string filePath)
{
Interface.Oxide.DataFileSystem.WriteObject(Path.Combine(Name, filePath), data);
}
#endregion Data.General
#region Data.Categories
private bool _isCategoriesLoaded;
private static CategoriesData _categoriesData;
private class CategoriesData
{
[JsonProperty(PropertyName = "Categories", ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<MenuCategory> Categories = new();
}
private void LoadCategoriesData()
{
if (_isCategoriesLoaded) return;
LoadDataFromFile(ref _categoriesData, "Categories");
_isCategoriesLoaded = true;
}
private void SaveCategoriesData()
{
SaveDataToFile(_categoriesData, "Categories");
}
#endregion Data.Categories
#region Data.Template
private static TemplateData _templateData;
private void SaveTemplateData()
{
SaveDataToFile(_templateData, "Template");
}
private void LoadTemplateData()
{
LoadDataFromFile(ref _templateData, "Template");
_templateData?.UI?.LoadAllElements();
}
private class TemplateData
{
#region Fields
[JsonProperty(PropertyName = "Use an expert mod?")]
public bool UseExpertMod = false;
[JsonProperty(PropertyName = "UI Settings")]
public UISettings UI;
#endregion
#region Public Methods
public void ShowEditPageButtonsUI(BasePlayer player, ref List<string> allElements, string parent,
string cmdAddPage = "",
string cmdRemovePage = "",
string cmdClonePage = "",
string cmdMovePage = "")
{
#region Add Page
allElements.Add(CuiJsonFactory.CreateButton(
parent: parent,
anchorMin: "0 0", anchorMax: "0 0", offsetMin: "10 10", offsetMax: "90 35",
color: IColor.Create("#4CAF50").Get(),
textColor: IColor.Create("#E2DBD4").Get(),
command: cmdAddPage,
text: "+ NEW PAGE",
font: "robotocondensed-bold.ttf",
fontSize: 11,
align: TextAnchor.MiddleCenter));
#endregion
#region Remove Page
allElements.Add(CuiJsonFactory.CreateButton(
parent: parent,
anchorMin: "0 0", anchorMax: "0 0", offsetMin: "95 10", offsetMax: "175 35",
color: IColor.Create("#CF432D").Get(),
textColor: IColor.Create("#E2DBD4").Get(),
command: cmdRemovePage,
text: "DELETE PAGE",
font: "robotocondensed-bold.ttf",
fontSize: 11,
align: TextAnchor.MiddleCenter));
#endregion
#region Clone Page
allElements.Add(CuiJsonFactory.CreateButton(
parent: parent,
anchorMin: "0 0", anchorMax: "0 0", offsetMin: "180 10", offsetMax: "260 35",
color: IColor.Create("#005FB7").Get(),
textColor: IColor.Create("#E2DBD4").Get(),
command: cmdClonePage,
text: "CLONE PAGE",
font: "robotocondensed-bold.ttf",
fontSize: 11,
align: TextAnchor.MiddleCenter));
#endregion
#region Move Page
if (!string.IsNullOrEmpty(cmdMovePage))
allElements.Add(CuiJsonFactory.CreateButton(
parent: parent,
anchorMin: "0 0", anchorMax: "0 0", offsetMin: "265 10", offsetMax: "345 35",
color: IColor.Create("#A88600").Get(),
textColor: IColor.Create("#E2DBD4").Get(),
command: cmdMovePage,
text: "MOVE PAGE",
font: "robotocondensed-bold.ttf",
fontSize: 11,
align: TextAnchor.MiddleCenter));
#endregion
}
public void ShowContentUISerialized(BasePlayer player,
ref List<string> allElements,
string cmdPage = "",
Action<string> callback = null,
bool needUpdate = false)
{
if (!TryGetOpenedMenu(player.userID, out var openedMenu)) return;
allElements.Add(
UI.Content.Background.GetSerialized(player, Layer, LayerContent, LayerContent));
allElements.Add(CuiJsonFactory.CreatePanel(parent: LayerContent, name: LayerContentElementsStatic, destroy: LayerContentElementsStatic, color: "0 0 0 0", anchorMin: "0 0", anchorMax: "1 1", offsetMin: "0 0", offsetMax: "0 0"));
allElements.Add(CuiJsonFactory.CreatePanel(parent: LayerContent, name: LayerContentElements, destroy: LayerContentElements, color: "0 0 0 0", anchorMin: "0 0", anchorMax: "1 1", offsetMin: "0 0", offsetMax: "0 0"));
var menuCategory = Instance.GetCategoryById(openedMenu.SelectedCategory);
var page = openedMenu.PageIndex;
var maxPages = menuCategory.Pages.Count;
#region Plugin Page Elements
var categoryPage = menuCategory?.Pages[page];
if (categoryPage != null)
switch (categoryPage.Type)
{
case CategoryPage.PageType.Plugin:
{
var obj = categoryPage.Plugin?.Call(categoryPage.PluginHook, player);
if (obj is CuiElementContainer pluginElements && pluginElements.Count > 0)
allElements.Add(pluginElements.ToJson().RemoveArrayBrackets());
else if (obj is string serializedElements && !string.IsNullOrWhiteSpace(serializedElements))
allElements.Add(serializedElements);
#region Pagination
if (UI.Content.Pagination.Type == UISettings.PaginationUI.PaginationType.SubCategories)
{
#region Pagination
if (menuCategory.ShowPages || (CanPlayerEdit(player) && openedMenu.CanEditContent()) ||
(openedMenu.isEditMode && UI.Content.Pagination.Type ==
UISettings.PaginationUI.PaginationType.SubCategories))
UI.Content.Pagination.ShowPagination(player, ref allElements, LayerContentElements, page,
maxPages,
LayerContentElements + ".Navigation", cmdPage, needUpdate);
#endregion Pagination
}
#endregion Pagination
break;
}
case CategoryPage.PageType.UI:
{
if (categoryPage.Elements != null)
foreach (var element in categoryPage.Elements)
allElements.Add(element.GetSerialized(player,
element.RequiresDynamicLayer() ? LayerContentElements : LayerContentElementsStatic,
ElementsLayer + element.Name,
textFormatter: text => Instance.FormatUpdateField(player, text)));
if (CanPlayerEdit(player) && openedMenu.CanEditContent())
UI.Content.EditButton.ShowEditButtonUI(player, ref allElements, LayerContentElements,
LayerContentElements + ".EditButton",
cmdEdit: $"{CmdMainConsole} edit_page start {menuCategory.ID} {page}");
#region Pagination
if (menuCategory.ShowPages || (CanPlayerEdit(player) && openedMenu.CanEditContent()) ||
(openedMenu.isEditMode && UI.Content.Pagination.Type ==
UISettings.PaginationUI.PaginationType.SubCategories))
UI.Content.Pagination.ShowPagination(player, ref allElements, LayerContentElements, page,
maxPages,
LayerContentElements + ".Navigation", cmdPage, needUpdate);
#endregion Pagination
break;
}
}
#endregion Plugin Page Elements
callback?.Invoke(LayerContentElements);
}
public void ShowPaginationIfNeeded(BasePlayer player, ref List<string> allElements)
{
if (!TryGetOpenedMenu(player.userID, out var openedMenu)) return;
var menuCategory = Instance.GetCategoryById(openedMenu.SelectedCategory);
if (menuCategory == null || !(menuCategory.ShowPages || CanPlayerEdit(player))) return;
var page = openedMenu.PageIndex;
var maxPages = menuCategory.Pages.Count;
UI.Content.Pagination.ShowPagination(player, ref allElements, LayerContentElements, page,
maxPages,
LayerContentElements + ".Navigation", $"{CmdMainConsole} menu page");
}
public void ShowCloseButtonUISerialized(BasePlayer player, ref List<string> allElements,
string parent,
string closeLayer = "",
string command = "")
{
UI.CloseButton.ShowButtonUI(player, ref allElements, parent, parent + ".CloseButton", closeLayer,
command);
}
public void ShowBackgroundUISerialized(BasePlayer player, ref List<string> allElements,
string cmdOnClick = "")
{
allElements.Add(
UI.Background.Background.GetSerialized(player, UI.Background.ParentLayer, Layer, Layer));
if (UI.Background.CloseAfterClick)
allElements.Add(CuiJsonFactory.CreateButton(
parent: Layer,
command: cmdOnClick,
close: Layer));
}
public void ShowHeaderUISerialized(BasePlayer player, ref List<string> allElements)
{
allElements.Add(UI.Header.Background.GetSerialized(player, Layer, LayerHeader, LayerHeader));
foreach (var headerField in _headerFieldsData.Fields)
allElements.Add(headerField.GetSerialized(player, LayerHeader,
ElementsLayer + headerField.Name,
ElementsLayer + headerField.Name,
textFormatter: text => Instance.FormatUpdateField(player, text)));
if (CanPlayerEdit(player) && TryGetOpenedMenu(player.userID, out var openedMenu))
ShowAdminModeButtonsUISerialized(player, ref allElements, openedMenu);
}
public void ShowAdminModeButtonsUISerialized(BasePlayer player, ref List<string> allElements,
OpenedMenu openedMenu)
{
if (openedMenu == null) return;
API_OnServerPanelDestroyAdminModeButtons(player);
if (openedMenu.isEditMode)
UI.Header.EditPagesButton?.ShowEditButtonUI(player, ref allElements, LayerHeader,
LayerHeader + ".EditPagesButton",
cmdEdit: $"{CmdMainConsole} edit_category open_pages");
if (openedMenu.CanEditContent())
{
UI.Header.EditButton?.ShowEditButtonUI(player, ref allElements, LayerHeader,
LayerHeader + ".EditButton",
cmdEdit: $"{CmdMainConsole} edit_header_fields start");
UI.Header.EditPopUpsButton?.ShowEditButtonUI(player, ref allElements, LayerHeader,
LayerHeader + ".EditPopUpsButton",
cmdEdit: $"{CmdMainConsole} start_edit_popups");
}
}
public void UpdateGlobalHeaderUISerialized(BasePlayer player, ref List<string> allElements)
{
foreach (var headerField in _headerFieldsData.Fields)
allElements.Add(headerField.GetSerialized(player, LayerHeader,
ElementsLayer + headerField.Name,
textFormatter: text => Instance.FormatUpdateField(player, text),
needUpdate: true));
}
public void UpdateHeaderUISerialized(BasePlayer player, ref List<string> allElements)
{
foreach (var headerField in _headerFieldsData.elementsToUpdate)
allElements.Add(headerField.GetSerialized(player, LayerHeader,
ElementsLayer + headerField.Name,
ElementsLayer + headerField.Name,
textFormatter: text => Instance.FormatUpdateField(player, text)));
}
public void ShowUpdateHeaderUI(BasePlayer player)
{
UpdateUI(player, (List<string> allElements) => UpdateHeaderUISerialized(player, ref allElements));
}
public void ShowCategoriesUISerialized(BasePlayer player, ref List<string> allElements)
{
allElements.Add(UI.Categories.Background.GetSerialized(player, Layer, LayerCategories,
LayerCategories));
if (UI.Categories.ShowLine)
allElements.Add(UI.Categories.Line?.GetSerialized(player, LayerCategories,
LayerCategories + ".Line", LayerCategories + ".Line"));
ShowCategoriesScrollUISerialized(player, ref allElements);
}
public void ShowCategoriesScrollUISerialized(BasePlayer player, ref List<string> allElements,
bool needUpdate = false)
{
var totalWidth = CalculateTotalCategoriesWidth(player);
if (UI.Categories.UseScrolling)
allElements.Add(UI.Categories.CategoriesScroll.GetScrollViewSerialized(Layer + ".Scroll.View",
Layer + ".Scroll.View", LayerCategories, totalWidth));
else
allElements.Add(UiElement
.CreatePanel(
InterfacePosition.CreatePosition(UI.Categories.CategoriesScroll.GetRectTransform()),
IColor.CreateTransparent())
.GetSerialized(player, LayerCategories, Layer + ".Scroll.View",
Layer + ".Scroll.View"));
ShowCategoriesLoopUISerialized(player, ref allElements);
if (CanPlayerEdit(player) && TryGetOpenedMenu(player.userID, out var openedMenu))
(openedMenu.isEditMode ? UI.Categories.AdminHeaderSelectedButton : UI.Categories.AdminHeaderButton)
?.ShowButtonUI(player, ref allElements, LayerCategories, LayerCategories + ".AdminHeaderButton",
command: $"{CmdMainConsole} edit_menu change_mode");
}
public void ShowCategoriesLoopUISerialized(BasePlayer player, ref List<string> allElements)
{
if (!TryGetOpenedMenu(player.userID, out var openedMenu))
return;
var playerLang = Instance.lang.GetLanguage(player.UserIDString);
var mainOffset = UI.Categories.CategoriesIndent;
var availableCategories = GetAvailableCategories(player.userID);
try
{
for (var i = 0; i < availableCategories.Count; i++)
{
var menuCategory = availableCategories[i];
var isSelected = openedMenu.SelectedCategory == menuCategory.ID;
var targetWidth = UI.Categories.CategoryWidth;
if (UI.Categories.UseAdaptiveWidth &&
(menuCategory.Localizations.TryGetValue(playerLang, out var localizedText) ||
menuCategory.Localizations.TryGetValue("en", out localizedText)))
targetWidth = localizedText.Width;
var btnRect =
CalculateCategoriesPosition(mainOffset, targetWidth, UI.Categories.CategoryHeight);
var categoryButton = Layer + ".Scroll.View" + $".Category.{menuCategory.ID}";
allElements.Add(CuiJsonFactory.CreateButton(anchorMin: btnRect.AnchorMin,
anchorMax: btnRect.AnchorMax, offsetMin: btnRect.OffsetMin, offsetMax: btnRect.OffsetMax,
parent: Layer + ".Scroll.View", name: categoryButton, destroy: categoryButton,
command: menuCategory.ChatBtn && menuCategory.Commands.Length > 0
? $"UI_ServerPanel_Send_Command UI_ServerPanel_Close|{menuCategory.Commands[0]}"
: $"{CmdMainConsole} menu category {menuCategory.ID}"));
var categoryBackground = isSelected
? UI.Categories.CategoryTitle.SelectedBackground
: UI.Categories.CategoryTitle.Background;
if (categoryBackground != null)
allElements.Add(categoryBackground.GetSerialized(player, categoryButton,
categoryButton + ".Background"));
UI.Categories.CategoryTitle.Get(player, ref allElements, categoryButton,
text: Instance.Msg(player, menuCategory.GetTitle(player.UserIDString)),
isSelected: isSelected);
if (UI.Categories.ShowSelectedElement && isSelected)
allElements.Add(UI.Categories.SelectedElement.GetSerialized(player, categoryButton));
if (UI.Categories.CategoryTitle.UseIcon && !string.IsNullOrEmpty(menuCategory.Icon))
allElements.Add(UI.Categories.CategoryTitle.Icon.GetSerialized(player, categoryButton,
textFormatter: menuIcon => menuCategory.Icon));
if (UI.Categories.CategoryTitle.UseOutline)
(isSelected
? UI.Categories.CategoryTitle.SelectedOutline
: UI.Categories.CategoryTitle.Outline)
.ShowOutlineUI(player, ref allElements, categoryButton);
CategoriesLoopPosition(ref mainOffset, i, availableCategories.Count, targetWidth);
}
if (CanPlayerEdit(player) && openedMenu.isEditMode)
{
var margin = 5f;
if (UI.Categories.CategoriesScroll.ScrollType == ScrollType.Horizontal)
mainOffset += margin;
else
mainOffset -= margin;
var btnRect = CalculateCategoriesPosition(mainOffset, UI.Categories.EditCategoryButton.Width,
UI.Categories.CategoryHeight);
var editCategoryButton = Layer + ".Scroll.View" + ".EditCategoryButton";
allElements.Add(CuiJsonFactory.CreatePanel(anchorMin: btnRect.AnchorMin,
anchorMax: btnRect.AnchorMax, offsetMin: btnRect.OffsetMin, offsetMax: btnRect.OffsetMax,
parent: Layer + ".Scroll.View", name: editCategoryButton, destroy: editCategoryButton));
UI.Categories.EditCategoryButton?.ShowButtonUI(player, ref allElements, editCategoryButton,
command: $"{CmdMainConsole} edit_category open");
}
}
finally
{
Pool.FreeUnmanaged(ref availableCategories);
}
}
private void CategoriesLoopPosition(ref float mainOffset, int index, int totalCount, float targetWidth = 0f)
{
if (UI.Categories.CategoriesScroll.ScrollType == ScrollType.Horizontal)
mainOffset += targetWidth > 0f ? targetWidth : UI.Categories.CategoryWidth;
else
mainOffset -= UI.Categories.CategoryHeight;
if (index != totalCount - 1)
{
if (UI.Categories.CategoriesScroll.ScrollType == ScrollType.Horizontal)
mainOffset += UI.Categories.CategoriesMargin;
else
mainOffset -= UI.Categories.CategoriesMargin;
}
}
private CuiRectTransformComponent CalculateCategoriesPosition(float offsetVal, float categoryWidth,
float categoryHeight)
{
CuiRectTransformComponent cuiRect;
if (UI.Categories.CategoriesScroll.ScrollType == ScrollType.Horizontal)
cuiRect = new CuiRectTransformComponent
{
AnchorMin = "0 1", AnchorMax = "0 1",
OffsetMin = $"{offsetVal} -{categoryHeight}",
OffsetMax = $"{offsetVal + categoryWidth} 0"
};
else
cuiRect = new CuiRectTransformComponent
{
AnchorMin = "0 1", AnchorMax = "0 1",
OffsetMin = $"0 {offsetVal - categoryHeight}",
OffsetMax = $"{categoryWidth} {offsetVal}"
};
return cuiRect;
}
private float CalculateTotalCategoriesWidth(BasePlayer player)
{
var playerLang = Instance.lang?.GetLanguage(player.UserIDString);
var categories = GetAvailableCategories(player.userID);
try
{
if (categories == null || categories.Count == 0)
return 0f;
var totalWidth = 0f;
for (var i = 0; i < categories.Count; i++)
{
var category = categories[i];
var categoryWidth = UI.Categories.CategoryWidth;
if (UI.Categories.UseAdaptiveWidth &&
(category.Localizations.TryGetValue(playerLang, out var localizedText) ||
category.Localizations.TryGetValue("en", out localizedText)))
categoryWidth = localizedText.Width;
totalWidth += categoryWidth;
if (i != categories.Count - 1)
totalWidth += UI.Categories.CategoriesMargin;
}
if (CanPlayerEdit(player) && TryGetOpenedMenu(player.userID, out var openedMenu) &&
openedMenu.isEditMode) totalWidth += 5f + UI.Categories.EditCategoryButton.Width;
return Mathf.Max(totalWidth, UI.Categories.CategoriesScroll.ScrollSize);
}
finally
{
Pool.FreeUnmanaged(ref categories);
}
}
#endregion
}
#endregion Data.Template
#region Data.Localization
private static LocalizationData _localizationData;
private void SaveLocalizationData()
{
SaveDataToFile(_localizationData, "Localization");
}
private void LoadLocalizationData()
{
LoadDataFromFile(ref _localizationData, "Localization");
}
private class LocalizationData
{
[JsonProperty(PropertyName = "Localization Settings")]
public LocalizationSettings Localization = new();
}
#endregion Data.Localization
#region Data.Header
private static HeaderFieldsData _headerFieldsData;
private void SaveHeaderFieldsData()
{
SaveDataToFile(_headerFieldsData, "HeaderFields");
}
private void LoadHeaderFieldsData()
{
LoadDataFromFile(ref _headerFieldsData, "HeaderFields");
LoadHeaderFieldsDataCache();
}
private void LoadHeaderFieldsDataCache()
{
_headerFields
// ... [Code preview truncated due to length. Full source in attached file] ...