I've updated script for dynamically generating buttons for gui from config file. Also I've changed a buttons grid to make them same-sized. Maybe you interested about that update. Or I can make all buttons to be generic from config file. That thing can remove empty slots from gui when user doesn't have an permission for this button or an appropriate plugin isn't installed
Can I send you a pull-request?
sure, you can submit the patch here on umod by clicking the Patch button.
Well, I tryed to see everywhere.. And I can't find such button)) I think it's only developer of this plugin can do patch) Maybe you have a .git repository?
Merged post
using Newtonsoft.Json; using Oxide.Core; using Oxide.Core.Plugins; using Oxide.Core.Libraries.Covalence; using Oxide.Game.Rust; using Oxide.Game.Rust.Cui; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; /*======================================================================================================================= * * * 20th november 2018 * * THANKS TO THE OXIDE/UMOD TEAM for coding quality, ideas, and time spent for the community * * 1.3.0 20181120 New maintainer (BuzZ) added GUI button for set new tp pos (config for color, and bool for on/off) * 1.4.0 20190609 toggle system FIX * 1.5.0 20221019 UI buttons are fully genereted by config file ********************************************* * Original author : DaBludger on versions <1.3.0 * Maintainer(s) : BuzZ since 20181116 from v1.3.0 * nivex since 20190609 from v1.4.0 * thNel since 20221020 from v1.5.0 ********************************************* * *=======================================================================================================================*/ /* * 1.5.0: * New configuration format * Edited UI elements sizes to make them unify * UI buttons now fully genereted by config file * Added permissions for AdminTP and NewTP * * 1.4.7: * Added FontSize (8) * New configuration format * Moved UI position above new map buttons * * 1.4.6: * Added Player Administration button * Added missing localization to language API * Added adminpanel.autotoggle.admin permission - toggles panel when added/removed from the admin group * Added command `adminpanel settp` - sets the custom teleport * Added command `adminpanel settp all` - sets the teleport location for all admins without a custom location * Added command `adminpanel removetp` - removes the custom teleport location * Removed ToggleMode requirement to use `adminpanel toggle` console command * * 1.4.5: * UI updates panel when player toggles godmode/vanish/radar * Requires AdminRadar 5.0.8+ * * 1.4.4: * Added support for AdminRadar 5.0+ * * 1.4.3: * Fixed `adminpanel toggle` * * 1.4.2: * Fixed issue with GUI on server startup @atope * * 1.4.1: * Fixed vanish permission * Fixed NullReferenceException in console command: adminpanel * Renamed deprecated hook OnPlayerDie to OnPlayerDeath * Added unsubcribing and subscribing of hooks * Fixed `/adminpanel show` glitching the game when AdminPanelToggleMode is true * Fixed `/adminpanel show` not showing the GUI */ // https://umod.org/community/admin-panel/28638-ability-to-create-custom-buttons namespace Oxide.Plugins { [Info("Admin Panel", "nivex", "1.4.7")] [Description("GUI admin panel with command buttons")] class AdminPanel: RustPlugin { [PluginReference] private Plugin AdminRadar, Godmode, Vanish, PlayerAdministration, AdminMenu, FauxAdmin; private const string permAdminPanel = "adminpanel.allowed"; private const string permAdminTeleport = "adminpanel.tp"; private const string permSetTeleport = "adminpanel.tp.set"; private const string permAutoToggle = "adminpanel.autotoggle.admin"; private const string permAdminRadar = "adminradar.allowed"; private const string permGodmode = "godmode.toggle"; private const string permVanish = "vanish.allow"; private const string permPlayerAdministration = "playeradministration.access.show"; private const string permAdminMenu = "adminmenu.use"; private const string permFauxAdmin = "fauxadmin.allowed"; // Minimal proportions: private const double minGuiButWidth = 0.0423; private const double minGuiButHeight = 0.0324; private const double minGuiMarginHorizontal = 0.0018; private const double minGuiMarginVertical = 0.0036; private Dictionary<string, string> colorStatus = new Dictionary<string, string>(); public Dictionary<BasePlayer, string> playerCUI = new Dictionary<BasePlayer, string>(); public class ButtonState { [JsonProperty(PropertyName = "Button Enabled")] public bool enabled { get; set; } [JsonProperty(PropertyName = "Button Color")] public string color { get; set; } } public class Button { [JsonProperty(PropertyName = "Title")] public string title { get; set; } [JsonProperty(PropertyName = "Console Command")] public string command { get; set; } [JsonProperty(PropertyName = "Permission")] public string permission { get; set; } [JsonProperty(PropertyName = "Plugin Shortname")] public string shortname { get; set; } [JsonProperty(PropertyName = "Button State")] public ButtonState state { get; set; } } #region Integrations public class StoredData { public Dictionary<string, string> TP = new Dictionary<string, string>(); public List plugins = new List(); public StoredData() { } } public StoredData data = new StoredData(); #region Player Administration private List _playerAdministration = new List(); private bool IsPlayerAdministration(string UserID) { return PlayerAdministration != null && _playerAdministration.Contains(UserID); } private void TogglePlayerAdministration(BasePlayer player) { if (PlayerAdministration == null || !PlayerAdministration.IsLoaded) return; if (!IsPlayerAdministration(player.UserIDString)) { player.Command("padmin", player, "padmin", new string[0]); } else { player.Command("playeradministration.closeui", player, "playeradministration.closeui", new string[0]); } } private void OnPlayerCommand(BasePlayer player, string command, string[] args) { if (player.IsValid() && permission.UserHasPermission(player.UserIDString, permPlayerAdministration)) { if (command.Equals("padmin", StringComparison.OrdinalIgnoreCase) && !_playerAdministration.Contains(player.UserIDString)) { _playerAdministration.Add(player.UserIDString); colorStatus["adminpanel action pa"] = config.btnActColor; AdminGui(player); } else if (command.Equals("playeradministration.closeui", StringComparison.OrdinalIgnoreCase) && _playerAdministration.Contains(player.UserIDString)) { _playerAdministration.Remove(player.UserIDString); colorStatus["adminpanel action pa"] = config.btnInactColor; AdminGui(player); } } } private void OnServerCommand(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player.IsValid() && permission.UserHasPermission(player.UserIDString, permPlayerAdministration)) { string command = arg.cmd.FullName.Replace("/", string.Empty); if (command.Equals("padmin", StringComparison.OrdinalIgnoreCase) && !_playerAdministration.Contains(player.UserIDString)) { _playerAdministration.Add(player.UserIDString); colorStatus["adminpanel action pa"] = config.btnActColor; AdminGui(player); } else if (command.Equals("playeradministration.closeui", StringComparison.OrdinalIgnoreCase) && _playerAdministration.Contains(player.UserIDString)) { _playerAdministration.Remove(player.UserIDString); colorStatus["adminpanel action pa"] = config.btnInactColor; AdminGui(player); } } } #endregion Player Administration #region Godmode private bool IsGod(string UserID) { return Godmode != null && Convert.ToBoolean(Godmode?.Call("IsGod", UserID)); } private void ToggleGodmode(BasePlayer player) { if (Godmode == null || !Godmode.IsLoaded) return; if (IsGod(player.UserIDString)) { colorStatus["adminpanel action god"] = config.btnInactColor; Godmode.Call("DisableGodmode", player.IPlayer); } else { colorStatus["adminpanel action god"] = config.btnActColor; Godmode.Call("EnableGodmode", player.IPlayer); } AdminGui(player); } private void OnGodmodeToggle(string playerId, bool state) { var player = RustCore.FindPlayerByIdString(playerId); if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel)) { AdminGui(player); } } #endregion Godmode #region Vanish private bool IsInvisible(BasePlayer player) { return Vanish != null && Convert.ToBoolean(Vanish?.Call("IsInvisible", player)); } private void ToggleVanish(BasePlayer player) { if (Vanish == null || !Vanish.IsLoaded) return; if (!IsInvisible(player)) { colorStatus["adminpanel action vanish"] = config.btnActColor; Vanish.Call("Disappear", player); } else { colorStatus["adminpanel action vanish"] = config.btnInactColor; Vanish.Call("Reappear", player); } AdminGui(player); } private void OnVanishDisappear(BasePlayer player) { if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel)) { AdminGui(player); } } private void OnVanishReappear(BasePlayer player) { if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel)) { AdminGui(player); } } #endregion Vanish #region Admin Radar private bool IsRadar(string id) { return AdminRadar != null && Convert.ToBoolean(AdminRadar?.Call("IsRadar", id)); } private void ToggleRadar(BasePlayer player) { if (AdminRadar == null || !AdminRadar.IsLoaded) return; if (AdminRadar.Version < new Core.VersionNumber(5, 0, 0)) AdminRadar.Call("cmdESP", player, "radar", new string[0]); else if (player.IPlayer != null) AdminRadar.Call("RadarCommand", player.IPlayer, "radar", new string[0]); AdminGui(player); } private void OnRadarActivated(BasePlayer player) { if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel)) { colorStatus["adminpanel action radar"] = config.btnActColor; AdminGui(player); } } private void OnRadarDeactivated(BasePlayer player) { if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel)) { colorStatus["adminpanel action radar"] = config.btnInactColor; AdminGui(player); } } #endregion Admin Radar #endregion Integrations private void Init() { permission.RegisterPermission(permAdminPanel, this); permission.RegisterPermission(permAutoToggle, this); permission.RegisterPermission(permAdminTeleport, this); permission.RegisterPermission(permSetTeleport, this); Unsubscribe(nameof(OnPlayerSleepEnded)); Unsubscribe(nameof(OnPlayerDeath)); } private void OnUserGroupRemoved(string id, string group) { if (group != "admin" || !permission.UserHasPermission(id, permAutoToggle)) { return; } var player = BasePlayer.FindAwakeOrSleeping(id); if (player == null || !player.IsConnected) { return; } DestroyUI(player); } private void OnUserGroupAdded(string id, string group) { if (group != "admin" || !permission.UserHasPermission(id, permAutoToggle)) { return; } var player = BasePlayer.FindAwakeOrSleeping(id); if (player == null || !player.IsConnected || !IsAllowed(player, permAdminPanel)) { return; } AdminGui(player); } #region Localization private new void LoadDefaultMessages() { // English lang.RegisterMessages(new Dictionary<string, string> { ["AdminTP"] = "Teleport", ["Godmode"] = "God", ["Radar"] = "Radar", ["Vanish"] = "Vanish", ["NewTP"] = "NewTP", ["PA"] = "Player Administration", ["Syntax"] = "Invalid syntax: /{0} {1}", ["No Custom Location Set"] = "You do not have a custom location set.", ["Removed Custom Location"] = "Removed your custom TP coordinates. You will teleport to the admin location instead.", ["Set Custom TP Coordinates"] = "Your TP coordinates set to current position {0}", ["Set Admin TP Coordinates"] = "Admin zone coordinates set to current position {0}", ["Panel Shown"] = "Admin panel refreshed/shown", ["Panel Hidden"] = "Admin panel hidden", ["Usage"] = "Usage: /{0} show/hide/settp/removetp", }, this); // Spanish lang.RegisterMessages(new Dictionary<string, string> { ["AdminTP"] = "Teleport", ["Godmode"] = "Dios", ["Radar"] = "Radar", ["Vanish"] = "Desaparecer", ["NewTP"] = "NewTP", }, this, "es"); // French lang.RegisterMessages(new Dictionary<string, string> { ["AdminTP"] = "Teleport", ["Godmode"] = "Dieu", ["Radar"] = "Radar", ["Vanish"] = "Invisible", ["NewTP"] = "NewTP", }, this, "fr"); } #endregion Localization #region Hooks private int calcButtons(BasePlayer player, Dictionary<string, Plugin> pluginList) { int counter = 0; foreach(Button btn in config.buttons) { if ((btn.shortname == null || pluginList[btn.shortname] != null) && (btn.permission == null || permission.UserHasPermission(player.UserIDString, btn.permission)) && btn.state.enabled) counter++; } return counter; } private List getPluginList() { List array = new List(); foreach(Button btn in config.buttons) { if (btn.state.enabled) array.Add(btn.shortname); } return array; } private void OnPlayerSleepEnded(BasePlayer player) { if (IsAllowed(player, permAdminPanel)) { AdminGui(player); } } private void OnPlayerDeath(BasePlayer player) { DestroyUI(player); } private void OnPluginLoaded(Plugin plugin) { try { data = Interface.Oxide.DataFileSystem.ReadObject(Name); } catch { } if (data == null) { data = new StoredData(); data.plugins = getPluginList(); SaveData(); SaveData(); } if (data.plugins.Contains(plugin.Name)) { RefreshAllUI(); } } private void OnPluginUnloaded(Plugin plugin) { try { data = Interface.Oxide.DataFileSystem.ReadObject(Name); } catch { } if (data == null) { data = new StoredData(); data.plugins = getPluginList(); SaveData(); } if (data.plugins.Contains(plugin.Name)) { RefreshAllUI(); } } private void OnServerInitialized() { try { data = Interface.Oxide.DataFileSystem.ReadObject(Name); } catch { } if (data == null) { data = new StoredData(); data.plugins = getPluginList(); SaveData(); } Subscribe(nameof(OnPlayerDeath)); if (!config.ToggleMode) { Subscribe(nameof(OnPlayerSleepEnded)); RefreshAllUI(); } } private void SaveData() { Interface.Oxide.DataFileSystem.WriteObject(Name, data, true); } private void RefreshAllUI() { foreach (var player in BasePlayer.activePlayerList) { if (IsAllowed(player, permAdminPanel)) { AdminGui(player); } } } #endregion Hooks #region Command Structure [ConsoleCommand("adminpanel")] private void ccmdAdminPanel(ConsoleSystem.Arg arg) { var player = arg.Player(); if (player == null || !IsAllowed(player, permAdminPanel) || !arg.HasArgs()) return; switch (arg.Args[0].ToLower()) { case "action": { if (arg.Args.Length >= 2) { switch (arg.Args[1].ToLower()) { case "vanish": ToggleVanish(player); break; case "radar": ToggleRadar(player); break; case "god": ToggleGodmode(player); break; case "pa": TogglePlayerAdministration(player); break; case "admintp": if (data.TP.ContainsKey(player.UserIDString)) { var pos = data.TP[player.UserIDString].ToVector3(); player.Teleport(pos); } else { player.Teleport(config.adminZoneCords); } break; case "newtp": var newtpBtn = Array.Find(config.buttons, p => p.title == "NewTP"); if (newtpBtn.state.enabled == true) { string[] argu = new string[1]; argu[0] = "settp"; ccmdAdminPanel(player, null, argu); } break; default: arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action vanish/admintp/radar/god/newtp")); break; } } else { arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action vanish/admintp/radar/god/newtp")); } break; } case "toggle": { if (IsAllowed(player, permAdminPanel)) { if (playerCUI.ContainsKey(player)) { DestroyUI(player); } else { AdminGui(player); } } break; } default: { arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action/toggle")); break; } } } [ChatCommand("adminpanel")] private void ccmdAdminPanel(BasePlayer player, string command, string[] args) // TODO: Make universal command { if (!IsAllowed(player, permAdminPanel)) { SendReply(player, $"Unknown command: {command}"); return; } if (args.Length == 0) { Message(player, "Usage", command); return; } switch (args[0].ToLower()) { case "hide": DestroyUI(player); Message(player, "Panel Hidden"); break; case "show": AdminGui(player); Message(player, "Panel Shown"); break; case "settp": Vector3 coord = player.transform.position; if (args.Any(arg => arg.ToLower() == "all")) { config.adminZoneCords = coord; SaveConfig(); Message(player, "Set Admin TP Coordinates", coord); } else { data.TP[player.UserIDString] = coord.ToString(); SaveData(); Message(player, "Set Custom TP Coordinates", coord); } break; case "removetp": if (data.TP.Remove(player.UserIDString)) { Message(player, "Removed Custom Location"); SaveData(); } else Message(player, "No Custom Location Set"); break; default: Message(player, "Syntax", command, args[0]); break; } } #endregion Command Structure #region GUI Panel private void AdminGui(BasePlayer player) { Dictionary<string, Plugin> pluginList = new Dictionary<string, Plugin> { ["AdminRadar"] = AdminRadar, ["Godmode"] = Godmode, ["Vanish"] = Vanish, ["PlayerAdministration"] = PlayerAdministration, ["AdminMenu"] = AdminMenu, ["FauxAdmin"] = FauxAdmin }; int buttonsCount = calcButtons(player, pluginList); foreach(Button btn in config.buttons) { if (btn.state.color == "standard (only for supported plugins)" && !colorStatus.ContainsKey(btn.command)) colorStatus.Add(btn.command, config.btnInactColor); } NextTick(() => { // Destroy existing UI DestroyUI(player); var GUIElement = new CuiElementContainer(); string[] RightTopCorner = config.RightTopPosition.Split(' '); double rightAnchor = double.Parse(RightTopCorner[0]); double topAnchor = double.Parse(RightTopCorner[1]); int ghostButton = buttonsCount > 0 ? 0 : 1; double backgroundWidth = minGuiMarginHorizontal + ((buttonsCount + ghostButton) > 1 ? 2 : 1) * (minGuiButWidth + minGuiMarginHorizontal); double backgroundHeight = minGuiMarginVertical + (((buttonsCount + ghostButton) / 2) + ((buttonsCount + ghostButton) % 2)) * (minGuiButHeight + minGuiMarginVertical); double relativeButtonWidth = minGuiButWidth / backgroundWidth; double relativeButtonHeight = minGuiButHeight / backgroundHeight; double relativeMarginHorizontal = minGuiMarginHorizontal / backgroundWidth; double relativeMarginVertical = minGuiMarginVertical / backgroundHeight; var GUIPanel = GUIElement.Add(new CuiPanel { Image = { Color = config.backgroundColor }, RectTransform = { AnchorMin = $"{rightAnchor - backgroundWidth} {topAnchor - backgroundHeight}", AnchorMax = config.RightTopPosition }, CursorEnabled = false }, "Hud", Name); if (buttonsCount != 0) { int counter = 0; foreach(Button btn in config.buttons) { if ((btn.permission == null || permission.UserHasPermission(player.UserIDString, btn.permission)) && btn.state.enabled) { counter++; double left = 1 - ((counter % 2 == 1 ? 1 : 2) * (relativeMarginHorizontal + relativeButtonWidth)); double bottom = 1 - (relativeMarginVertical + relativeButtonHeight + (counter % 2 == 1 ? (counter / 2) : ((counter / 2) - 1)) * (relativeMarginVertical + relativeButtonHeight)); double right = 1 - (counter % 2 == 1 ? relativeMarginHorizontal : (2 * relativeMarginHorizontal + relativeButtonWidth)); double top = 1 - (relativeMarginVertical + (counter % 2 == 1 ? (counter / 2) : ((counter / 2) - 1)) * (relativeMarginVertical + relativeButtonHeight)); GUIElement.Add(new CuiButton { Button = { Command = btn.command, Color = btn.state.color != "standard (only for supported plugins)" ? btn.state.color : colorStatus[btn.command] }, Text = { Text = _(btn.title, player.UserIDString), FontSize = config.fontSize, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" }, RectTransform = { AnchorMin = $"{left} {bottom}", AnchorMax = $"{right} {top}" } }, GUIPanel); } } } else { GUIElement.Add(new CuiButton { Button = { Command = "adminpanel show", Color = "0 0 0 1" }, Text = { Text = _("No Buttons Avaliable", player.UserIDString), FontSize = config.fontSize, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1" }, RectTransform = { AnchorMin = $"{1 - (relativeMarginHorizontal + relativeButtonWidth)} {1 - (relativeMarginVertical + relativeButtonHeight)}", AnchorMax = $"{1-relativeMarginHorizontal} {1-relativeMarginVertical}" } }, GUIPanel); } CuiHelper.AddUi(player, GUIElement); playerCUI.Add(player, GUIPanel); }); } private void Unload() { foreach (var player in BasePlayer.activePlayerList) { DestroyUI(player); } } private void DestroyUI(BasePlayer player) { string cuiElement; if (playerCUI.TryGetValue(player, out cuiElement)) { CuiHelper.DestroyUi(player, cuiElement); playerCUI.Remove(player); } } #endregion GUI Panel #region Helpers private bool IsAllowed(BasePlayer player, string perm) => player != null && permission.UserHasPermission(player.UserIDString, perm); private string _(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args); private void Message(BasePlayer player, string key, params object[] args) => Player.Message(player, _(key, player.UserIDString, args)); #endregion Helpers #region Configuration private Configuration config; public class Configuration { [JsonProperty(PropertyName = "Buttons")] public Button[] buttons { get; set; } = { new Button { title = "NewTP", command = "adminpanel action newtp", permission = permSetTeleport, shortname = null, state = new ButtonState { enabled = true, color = "1.0 0.65 0.85 0.5" } }, new Button { title = "AdminTP", command = "adminpanel action admintp", permission = permAdminTeleport, shortname = null, state = new ButtonState { enabled = true, color = "1 0 1 0.5" } }, new Button { title = "PA", command = "adminpanel action pa", permission = permPlayerAdministration, shortname = "PlayerAdministration", state = new ButtonState { enabled = true, color = "standard (only for supported plugins)" } }, new Button { title = "Radar", command = "adminpanel action radar", permission = permAdminRadar, shortname = "AdminRadar", state = new ButtonState { enabled = true, color = "standard (only for supported plugins)" } }, new Button { title = "Vanish", command = "adminpanel action vanish", permission = permVanish, shortname = "Vanish", state = new ButtonState { enabled = true, color = "standard (only for supported plugins)" } }, new Button { title = "Godmode", command = "adminpanel action god", permission = permGodmode, shortname = "Godmode", state = new ButtonState { enabled = true, color = "standard (only for supported plugins)" } } }; [JsonProperty(PropertyName = "Admin Zone Coordinates")] public Vector3 adminZoneCords { get; set; } [JsonProperty(PropertyName = "Button Active Color")] public string btnActColor { get; set; } = "0 2.55 0 0.5"; [JsonProperty(PropertyName = "Button Inactive Color")] public string btnInactColor { get; set; } = "2.55 0 0 0.5"; [JsonProperty(PropertyName = "Background Color")] public string backgroundColor { get; set; } = "0 0 0 0.8"; [JsonProperty(PropertyName = "Font Size")] public int fontSize { get; set; } = 8; [JsonProperty(PropertyName = "GUI Right Top Position")] public string RightTopPosition { get; set; } = "0.99 0.88"; [JsonProperty(PropertyName = "Toggle Mode")] public bool ToggleMode { get; set; } } protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); } catch { } if (config == null) { LoadDefaultConfig(); } SaveConfig(); } protected override void SaveConfig() => Config.WriteObject(config); protected override void LoadDefaultConfig() => config = new Configuration(); #endregion } }
Merged post
Sorry, I thought "Source code" option in reply can show it pretty. Still maybe git?
Merged post
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
/*=======================================================================================================================
*
*
* 20th november 2018
*
* THANKS TO THE OXIDE/UMOD TEAM for coding quality, ideas, and time spent for the community
*
* 1.3.0 20181120 New maintainer (BuzZ) added GUI button for set new tp pos (config for color, and bool for on/off)
* 1.4.0 20190609 toggle system FIX
* 1.5.0 20221019 UI buttons are fully genereted by config file
*********************************************
* Original author : DaBludger on versions <1.3.0
* Maintainer(s) : BuzZ since 20181116 from v1.3.0
* nivex since 20190609 from v1.4.0
* thNel since 20221020 from v1.5.0
*********************************************
*
*=======================================================================================================================*/
/*
* 1.5.0:
* New configuration format
* Edited UI elements sizes to make them unify
* UI buttons now fully genereted by config file
* Added permissions for AdminTP and NewTP
*
* 1.4.7:
* Added FontSize (8)
* New configuration format
* Moved UI position above new map buttons
*
* 1.4.6:
* Added Player Administration button
* Added missing localization to language API
* Added adminpanel.autotoggle.admin permission - toggles panel when added/removed from the admin group
* Added command `adminpanel settp` - sets the custom teleport
* Added command `adminpanel settp all` - sets the teleport location for all admins without a custom location
* Added command `adminpanel removetp` - removes the custom teleport location
* Removed ToggleMode requirement to use `adminpanel toggle` console command
*
* 1.4.5:
* UI updates panel when player toggles godmode/vanish/radar
* Requires AdminRadar 5.0.8+
*
* 1.4.4:
* Added support for AdminRadar 5.0+
*
* 1.4.3:
* Fixed `adminpanel toggle`
*
* 1.4.2:
* Fixed issue with GUI on server startup @atope
*
* 1.4.1:
* Fixed vanish permission
* Fixed NullReferenceException in console command: adminpanel
* Renamed deprecated hook OnPlayerDie to OnPlayerDeath
* Added unsubcribing and subscribing of hooks
* Fixed `/adminpanel show` glitching the game when AdminPanelToggleMode is true
* Fixed `/adminpanel show` not showing the GUI
*/
// https://umod.org/community/admin-panel/28638-ability-to-create-custom-buttons
namespace Oxide.Plugins
{
[Info("Admin Panel", "thNel", "1.5.0")]
[Description("GUI admin panel with command buttons")]
class AdminPanel: RustPlugin
{
[PluginReference]
private Plugin AdminRadar, Godmode, Vanish, PlayerAdministration, AdminMenu, FauxAdmin;
private const string permAdminPanel = "adminpanel.allowed";
private const string permAdminTeleport = "adminpanel.tp";
private const string permSetTeleport = "adminpanel.tp.set";
private const string permAutoToggle = "adminpanel.autotoggle.admin";
private const string permAdminRadar = "adminradar.allowed";
private const string permGodmode = "godmode.toggle";
private const string permVanish = "vanish.allow";
private const string permPlayerAdministration = "playeradministration.access.show";
private const string permAdminMenu = "adminmenu.use";
private const string permFauxAdmin = "fauxadmin.allowed";
// Minimal proportions:
private const double minGuiButWidth = 0.0423;
private const double minGuiButHeight = 0.0324;
private const double minGuiMarginHorizontal = 0.0018;
private const double minGuiMarginVertical = 0.0036;
private Dictionary<string, string> colorStatus = new Dictionary<string, string>();
public Dictionary<BasePlayer, string> playerCUI = new Dictionary<BasePlayer, string>();
public class ButtonState
{
[JsonProperty(PropertyName = "Button Enabled")]
public bool enabled { get; set; }
[JsonProperty(PropertyName = "Button Color")]
public string color { get; set; }
}
public class Button {
[JsonProperty(PropertyName = "Title")]
public string title { get; set; }
[JsonProperty(PropertyName = "Console Command")]
public string command { get; set; }
[JsonProperty(PropertyName = "Permission")]
public string permission { get; set; }
[JsonProperty(PropertyName = "Plugin Shortname")]
public string shortname { get; set; }
[JsonProperty(PropertyName = "Button State")]
public ButtonState state { get; set; }
}
#region Integrations
public class StoredData
{
public Dictionary<string, string> TP = new Dictionary<string, string>();
public List<string> plugins = new List<string>();
public StoredData() { }
}
public StoredData data = new StoredData();
#region Player Administration
private List<string> _playerAdministration = new List<string>();
private bool IsPlayerAdministration(string UserID)
{
return PlayerAdministration != null && _playerAdministration.Contains(UserID);
}
private void TogglePlayerAdministration(BasePlayer player)
{
if (PlayerAdministration == null || !PlayerAdministration.IsLoaded) return;
if (!IsPlayerAdministration(player.UserIDString))
{
player.Command("padmin", player, "padmin", new string[0]);
}
else
{
player.Command("playeradministration.closeui", player, "playeradministration.closeui", new string[0]);
}
}
private void OnPlayerCommand(BasePlayer player, string command, string[] args)
{
if (player.IsValid() && permission.UserHasPermission(player.UserIDString, permPlayerAdministration))
{
if (command.Equals("padmin", StringComparison.OrdinalIgnoreCase) && !_playerAdministration.Contains(player.UserIDString))
{
_playerAdministration.Add(player.UserIDString);
colorStatus["adminpanel action pa"] = config.btnActColor;
AdminGui(player);
}
else if (command.Equals("playeradministration.closeui", StringComparison.OrdinalIgnoreCase) && _playerAdministration.Contains(player.UserIDString))
{
_playerAdministration.Remove(player.UserIDString);
colorStatus["adminpanel action pa"] = config.btnInactColor;
AdminGui(player);
}
}
}
private void OnServerCommand(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player.IsValid() && permission.UserHasPermission(player.UserIDString, permPlayerAdministration))
{
string command = arg.cmd.FullName.Replace("/", string.Empty);
if (command.Equals("padmin", StringComparison.OrdinalIgnoreCase) && !_playerAdministration.Contains(player.UserIDString))
{
_playerAdministration.Add(player.UserIDString);
colorStatus["adminpanel action pa"] = config.btnActColor;
AdminGui(player);
}
else if (command.Equals("playeradministration.closeui", StringComparison.OrdinalIgnoreCase) && _playerAdministration.Contains(player.UserIDString))
{
_playerAdministration.Remove(player.UserIDString);
colorStatus["adminpanel action pa"] = config.btnInactColor;
AdminGui(player);
}
}
}
#endregion Player Administration
#region Godmode
private bool IsGod(string UserID)
{
return Godmode != null && Convert.ToBoolean(Godmode?.Call("IsGod", UserID));
}
private void ToggleGodmode(BasePlayer player)
{
if (Godmode == null || !Godmode.IsLoaded) return;
if (IsGod(player.UserIDString))
{
colorStatus["adminpanel action god"] = config.btnInactColor;
Godmode.Call("DisableGodmode", player.IPlayer);
}
else
{
colorStatus["adminpanel action god"] = config.btnActColor;
Godmode.Call("EnableGodmode", player.IPlayer);
}
AdminGui(player);
}
private void OnGodmodeToggle(string playerId, bool state)
{
var player = RustCore.FindPlayerByIdString(playerId);
if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel))
{
AdminGui(player);
}
}
#endregion Godmode
#region Vanish
private bool IsInvisible(BasePlayer player)
{
return Vanish != null && Convert.ToBoolean(Vanish?.Call("IsInvisible", player));
}
private void ToggleVanish(BasePlayer player)
{
if (Vanish == null || !Vanish.IsLoaded) return;
if (!IsInvisible(player))
{
colorStatus["adminpanel action vanish"] = config.btnActColor;
Vanish.Call("Disappear", player);
}
else
{
colorStatus["adminpanel action vanish"] = config.btnInactColor;
Vanish.Call("Reappear", player);
}
AdminGui(player);
}
private void OnVanishDisappear(BasePlayer player)
{
if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel))
{
AdminGui(player);
}
}
private void OnVanishReappear(BasePlayer player)
{
if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel))
{
AdminGui(player);
}
}
#endregion Vanish
#region Admin Radar
private bool IsRadar(string id)
{
return AdminRadar != null && Convert.ToBoolean(AdminRadar?.Call("IsRadar", id));
}
private void ToggleRadar(BasePlayer player)
{
if (AdminRadar == null || !AdminRadar.IsLoaded) return;
if (AdminRadar.Version < new Core.VersionNumber(5, 0, 0)) AdminRadar.Call("cmdESP", player, "radar", new string[0]);
else if (player.IPlayer != null) AdminRadar.Call("RadarCommand", player.IPlayer, "radar", new string[0]);
AdminGui(player);
}
private void OnRadarActivated(BasePlayer player)
{
if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel))
{
colorStatus["adminpanel action radar"] = config.btnActColor;
AdminGui(player);
}
}
private void OnRadarDeactivated(BasePlayer player)
{
if (player.IsValid() && player.IsConnected && IsAllowed(player, permAdminPanel))
{
colorStatus["adminpanel action radar"] = config.btnInactColor;
AdminGui(player);
}
}
#endregion Admin Radar
#endregion Integrations
private void Init()
{
permission.RegisterPermission(permAdminPanel, this);
permission.RegisterPermission(permAutoToggle, this);
permission.RegisterPermission(permAdminTeleport, this);
permission.RegisterPermission(permSetTeleport, this);
Unsubscribe(nameof(OnPlayerSleepEnded));
Unsubscribe(nameof(OnPlayerDeath));
}
private void OnUserGroupRemoved(string id, string group)
{
if (group != "admin" || !permission.UserHasPermission(id, permAutoToggle))
{
return;
}
var player = BasePlayer.FindAwakeOrSleeping(id);
if (player == null || !player.IsConnected)
{
return;
}
DestroyUI(player);
}
private void OnUserGroupAdded(string id, string group)
{
if (group != "admin" || !permission.UserHasPermission(id, permAutoToggle))
{
return;
}
var player = BasePlayer.FindAwakeOrSleeping(id);
if (player == null || !player.IsConnected || !IsAllowed(player, permAdminPanel))
{
return;
}
AdminGui(player);
}
#region Localization
private new void LoadDefaultMessages()
{
// English
lang.RegisterMessages(new Dictionary<string, string>
{
["AdminTP"] = "Teleport",
["Godmode"] = "God",
["Radar"] = "Radar",
["Vanish"] = "Vanish",
["NewTP"] = "NewTP",
["PA"] = "Player Administration",
["Syntax"] = "Invalid syntax: /{0} {1}",
["No Custom Location Set"] = "You do not have a custom location set.",
["Removed Custom Location"] = "Removed your custom TP coordinates. You will teleport to the admin location instead.",
["Set Custom TP Coordinates"] = "Your TP coordinates set to current position {0}",
["Set Admin TP Coordinates"] = "Admin zone coordinates set to current position {0}",
["Panel Shown"] = "Admin panel refreshed/shown",
["Panel Hidden"] = "Admin panel hidden",
["Usage"] = "Usage: /{0} show/hide/settp/removetp",
}, this);
// Spanish
lang.RegisterMessages(new Dictionary<string, string>
{
["AdminTP"] = "Teleport",
["Godmode"] = "Dios",
["Radar"] = "Radar",
["Vanish"] = "Desaparecer",
["NewTP"] = "NewTP",
}, this, "es");
// French
lang.RegisterMessages(new Dictionary<string, string>
{
["AdminTP"] = "Teleport",
["Godmode"] = "Dieu",
["Radar"] = "Radar",
["Vanish"] = "Invisible",
["NewTP"] = "NewTP",
}, this, "fr");
}
#endregion Localization
#region Hooks
private int calcButtons(BasePlayer player, Dictionary<string, Plugin> pluginList)
{
int counter = 0;
foreach(Button btn in config.buttons)
{
if ((btn.shortname == null || pluginList[btn.shortname] != null) && (btn.permission == null || permission.UserHasPermission(player.UserIDString, btn.permission)) && btn.state.enabled) counter++;
}
return counter;
}
private string startupColor (string pluginShortname, BasePlayer player)
{
if (pluginShortname == "AdminRadar")
return IsRadar(player.UserIDString) ? config.btnActColor : config.btnInactColor;
if (pluginShortname == "Godmode")
return IsGod(player.UserIDString) ? config.btnActColor : config.btnInactColor;
if (pluginShortname == "Vanish")
return IsInvisible(player) ? config.btnActColor : config.btnInactColor;
return config.btnInactColor;
}
private List<string> getPluginList()
{
List<string> array = new List<string>();
foreach(Button btn in config.buttons)
{
if (btn.state.enabled) array.Add(btn.shortname);
}
return array;
}
private void OnPlayerSleepEnded(BasePlayer player)
{
if (IsAllowed(player, permAdminPanel))
{
AdminGui(player);
}
}
private void OnPlayerDeath(BasePlayer player)
{
DestroyUI(player);
}
private void OnPluginLoaded(Plugin plugin)
{
try
{
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
}
catch
{
}
if (data == null)
{
data = new StoredData();
data.plugins = getPluginList();
SaveData();
SaveData();
}
if (data.plugins.Contains(plugin.Name))
{
RefreshAllUI();
}
}
private void OnPluginUnloaded(Plugin plugin)
{
try
{
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
}
catch
{
}
if (data == null)
{
data = new StoredData();
data.plugins = getPluginList();
SaveData();
}
if (data.plugins.Contains(plugin.Name))
{
RefreshAllUI();
}
}
private void OnServerInitialized()
{
try
{
data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
}
catch
{
}
if (data == null)
{
data = new StoredData();
data.plugins = getPluginList();
SaveData();
}
Subscribe(nameof(OnPlayerDeath));
if (!config.ToggleMode)
{
Subscribe(nameof(OnPlayerSleepEnded));
RefreshAllUI();
}
}
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject(Name, data, true);
}
private void RefreshAllUI()
{
foreach (var player in BasePlayer.activePlayerList)
{
if (IsAllowed(player, permAdminPanel))
{
AdminGui(player);
}
}
}
#endregion Hooks
#region Command Structure
[ConsoleCommand("adminpanel")]
private void ccmdAdminPanel(ConsoleSystem.Arg arg)
{
var player = arg.Player();
if (player == null || !IsAllowed(player, permAdminPanel) || !arg.HasArgs()) return;
switch (arg.Args[0].ToLower())
{
case "action":
{
if (arg.Args.Length >= 2)
{
switch (arg.Args[1].ToLower())
{
case "vanish":
ToggleVanish(player);
break;
case "radar":
ToggleRadar(player);
break;
case "god":
ToggleGodmode(player);
break;
case "pa":
TogglePlayerAdministration(player);
break;
case "admintp":
if (data.TP.ContainsKey(player.UserIDString))
{
var pos = data.TP[player.UserIDString].ToVector3();
player.Teleport(pos);
}
else
{
player.Teleport(config.adminZoneCords);
}
break;
case "newtp":
var newtpBtn = Array.Find(config.buttons, p => p.title == "NewTP");
if (newtpBtn.state.enabled == true)
{
string[] argu = new string[1];
argu[0] = "settp";
ccmdAdminPanel(player, null, argu);
}
break;
default:
arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action vanish/admintp/radar/god/newtp"));
break;
}
}
else
{
arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action vanish/admintp/radar/god/newtp"));
}
break;
}
case "toggle":
{
if (IsAllowed(player, permAdminPanel))
{
if (playerCUI.ContainsKey(player))
{
DestroyUI(player);
}
else
{
AdminGui(player);
}
}
break;
}
default:
{
arg.ReplyWith(_("Syntax", player.UserIDString, "adminpanel", "action/toggle"));
break;
}
}
}
[ChatCommand("adminpanel")]
private void ccmdAdminPanel(BasePlayer player, string command, string[] args) // TODO: Make universal command
{
if (!IsAllowed(player, permAdminPanel))
{
SendReply(player, $"Unknown command: {command}");
return;
}
if (args.Length == 0)
{
Message(player, "Usage", command);
return;
}
switch (args[0].ToLower())
{
case "hide":
DestroyUI(player);
Message(player, "Panel Hidden");
break;
case "show":
AdminGui(player);
Message(player, "Panel Shown");
break;
case "settp":
Vector3 coord = player.transform.position;
if (args.Any(arg => arg.ToLower() == "all"))
{
config.adminZoneCords = coord;
SaveConfig();
Message(player, "Set Admin TP Coordinates", coord);
}
else
{
data.TP[player.UserIDString] = coord.ToString();
SaveData();
Message(player, "Set Custom TP Coordinates", coord);
}
break;
case "removetp":
if (data.TP.Remove(player.UserIDString))
{
Message(player, "Removed Custom Location");
SaveData();
}
else Message(player, "No Custom Location Set");
break;
default:
Message(player, "Syntax", command, args[0]);
break;
}
}
#endregion Command Structure
#region GUI Panel
private void AdminGui(BasePlayer player)
{
Dictionary<string, Plugin> pluginList = new Dictionary<string, Plugin> {
["AdminRadar"] = AdminRadar,
["Godmode"] = Godmode,
["Vanish"] = Vanish,
["PlayerAdministration"] = PlayerAdministration,
["AdminMenu"] = AdminMenu,
["FauxAdmin"] = FauxAdmin
};
int buttonsCount = calcButtons(player, pluginList);
foreach(Button btn in config.buttons)
{
if (btn.state.color == "standard (only for supported plugins)" && !colorStatus.ContainsKey(btn.command)) colorStatus.Add(btn.command, startupColor(btn.shortname, player));
}
NextTick(() =>
{
// Destroy existing UI
DestroyUI(player);
var GUIElement = new CuiElementContainer();
string[] RightTopCorner = config.RightTopPosition.Split(' ');
double rightAnchor = double.Parse(RightTopCorner[0]);
double topAnchor = double.Parse(RightTopCorner[1]);
int ghostButton = buttonsCount > 0 ? 0 : 1;
double backgroundWidth = minGuiMarginHorizontal + ((buttonsCount + ghostButton) > 1 ? 2 : 1) * (minGuiButWidth + minGuiMarginHorizontal);
double backgroundHeight = minGuiMarginVertical + (((buttonsCount + ghostButton) / 2) + ((buttonsCount + ghostButton) % 2)) * (minGuiButHeight + minGuiMarginVertical);
double relativeButtonWidth = minGuiButWidth / backgroundWidth;
double relativeButtonHeight = minGuiButHeight / backgroundHeight;
double relativeMarginHorizontal = minGuiMarginHorizontal / backgroundWidth;
double relativeMarginVertical = minGuiMarginVertical / backgroundHeight;
var GUIPanel = GUIElement.Add(new CuiPanel
{
Image =
{
Color = config.backgroundColor
},
RectTransform =
{
AnchorMin = $"{rightAnchor - backgroundWidth} {topAnchor - backgroundHeight}",
AnchorMax = config.RightTopPosition
},
CursorEnabled = false
}, "Hud", Name);
if (buttonsCount != 0)
{
int counter = 0;
foreach(Button btn in config.buttons)
{
if ((btn.permission == null || permission.UserHasPermission(player.UserIDString, btn.permission)) && btn.state.enabled)
{
counter++;
double left = 1 - ((counter % 2 == 1 ? 1 : 2) * (relativeMarginHorizontal + relativeButtonWidth));
double bottom = 1 - (relativeMarginVertical + relativeButtonHeight + (counter % 2 == 1 ? (counter / 2) : ((counter / 2) - 1)) * (relativeMarginVertical + relativeButtonHeight));
double right = 1 - (counter % 2 == 1 ? relativeMarginHorizontal : (2 * relativeMarginHorizontal + relativeButtonWidth));
double top = 1 - (relativeMarginVertical + (counter % 2 == 1 ? (counter / 2) : ((counter / 2) - 1)) * (relativeMarginVertical + relativeButtonHeight));
GUIElement.Add(new CuiButton
{
Button =
{
Command = btn.command,
Color = btn.state.color != "standard (only for supported plugins)" ? btn.state.color : colorStatus[btn.command]
},
Text =
{
Text = _(btn.title, player.UserIDString),
FontSize = config.fontSize,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 1"
},
RectTransform =
{
AnchorMin = $"{left} {bottom}",
AnchorMax = $"{right} {top}"
}
}, GUIPanel);
}
}
}
else
{
GUIElement.Add(new CuiButton
{
Button =
{
Command = "adminpanel show",
Color = "0 0 0 1"
},
Text =
{
Text = _("No Buttons Avaliable", player.UserIDString),
FontSize = config.fontSize,
Align = TextAnchor.MiddleCenter,
Color = "1 1 1 1"
},
RectTransform =
{
AnchorMin = $"{1 - (relativeMarginHorizontal + relativeButtonWidth)} {1 - (relativeMarginVertical + relativeButtonHeight)}",
AnchorMax = $"{1-relativeMarginHorizontal} {1-relativeMarginVertical}"
}
}, GUIPanel);
}
CuiHelper.AddUi(player, GUIElement);
playerCUI.Add(player, GUIPanel);
});
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
DestroyUI(player);
}
}
private void DestroyUI(BasePlayer player)
{
string cuiElement;
if (playerCUI.TryGetValue(player, out cuiElement))
{
CuiHelper.DestroyUi(player, cuiElement);
playerCUI.Remove(player);
}
}
#endregion GUI Panel
#region Helpers
private bool IsAllowed(BasePlayer player, string perm) => player != null && permission.UserHasPermission(player.UserIDString, perm);
private string _(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
private void Message(BasePlayer player, string key, params object[] args) => Player.Message(player, _(key, player.UserIDString, args));
#endregion Helpers
#region Configuration
private Configuration config;
public class Configuration {
[JsonProperty(PropertyName = "Buttons")]
public Button[] buttons { get; set; } =
{
new Button {
title = "NewTP",
command = "adminpanel action newtp",
permission = permSetTeleport,
shortname = null,
state = new ButtonState {
enabled = true,
color = "1.0 0.65 0.85 0.5"
}
},
new Button {
title = "PA",
command = "adminpanel action pa",
permission = permPlayerAdministration,
shortname = "PlayerAdministration",
state = new ButtonState {
enabled = true,
color = "standard (only for supported plugins)"
}
},
new Button {
title = "AdminTP",
command = "adminpanel action admintp",
permission = permAdminTeleport,
shortname = null,
state = new ButtonState {
enabled = true,
color = "1 0 1 0.5"
}
},
new Button {
title = "Radar",
command = "adminpanel action radar",
permission = permAdminRadar,
shortname = "AdminRadar",
state = new ButtonState {
enabled = true,
color = "standard (only for supported plugins)"
}
},
new Button {
title = "Godmode",
command = "adminpanel action god",
permission = permGodmode,
shortname = "Godmode",
state = new ButtonState {
enabled = true,
color = "standard (only for supported plugins)"
}
},
new Button {
title = "Vanish",
command = "adminpanel action vanish",
permission = permVanish,
shortname = "Vanish",
state = new ButtonState {
enabled = true,
color = "standard (only for supported plugins)"
}
}
};
[JsonProperty(PropertyName = "Admin Zone Coordinates")]
public Vector3 adminZoneCords { get; set; }
[JsonProperty(PropertyName = "Button Active Color")]
public string btnActColor { get; set; } = "0 2.55 0 0.5";
[JsonProperty(PropertyName = "Button Inactive Color")]
public string btnInactColor { get; set; } = "2.55 0 0 0.5";
[JsonProperty(PropertyName = "Background Color")]
public string backgroundColor { get; set; } = "0 0 0 0.8";
[JsonProperty(PropertyName = "Font Size")]
public int fontSize { get; set; } = 8;
[JsonProperty(PropertyName = "GUI Right Top Position")]
public string RightTopPosition { get; set; } = "0.99 0.88";
[JsonProperty(PropertyName = "Toggle Mode")]
public bool ToggleMode { get; set; }
}
protected override void LoadConfig() {
base.LoadConfig();
try
{
config = Config.ReadObject<Configuration>();
}
catch
{
}
if (config == null)
{
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultConfig() => config = new Configuration();
#endregion
}
}
will take a look at this when i can. thanks!
You're welcome. If there will be any questions - I'll glad to answer it. Or maybe you'll see any problems in code. It's my first experience with Rust plugins and C# at all. Actually very pretty language unlike C++ or C. I'm an a JS Node/React/Vue developer with a small knowleges in Python (django), PHP, ADA, Assembler, C, C++ and C# now