Иконка ресурса

FREE AdminButtons : 1.0.1 1.0.1

Нет прав для скачивания
C#:
// ============================================================================
//  AdminButtons  (c) Fullwiped  -  https://fullwiped.com
// ----------------------------------------------------------------------------
//  Free to use and modify on your own servers.
//  Redistribution, resale, or re-publishing this file (original or modified)
//  is prohibited without written permission from Fullwiped.
//  Support and updates are provided at Fullwiped's discretion.
// ============================================================================

using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;

namespace Oxide.Plugins
{
    [Info("AdminButtons", "Fullwiped", "1.0.1")]
    [Description("Admin quick-action HUD tray.")]
    public class AdminButtons : RustPlugin
    {
        // perm: adminbuttons.admin
        // cmd:  /ab
        // images: oxide/data/Fullwiped/Images/AdminButtons/<IconKey>.png (128x128 PNG)

        #region Constants

        private const string Perm           = "adminbuttons.admin";
        private const string PanelName      = "AdminButtons.Root";
        private const string DataSubdir     = "Fullwiped/Images/AdminButtons";
        private const int    MaxButtons     = 8;

        // Base dimensions at 100% scale
        private const float  CellW          = 44f;
        private const float  CellH          = 48f;
        private const float  CellGap        = 3f;
        private const float  PadX           = 7f;
        private const float  PadY           = 4f;
        private const float  CloseSize      = 18f;
        private const float  CloseGap       = 7f;
        private const float  ScreenMargin   = 18f;
        private const int    LabelFontSize  = 7;
        private const int    CloseFontSize  = 13;

        private const string ClickSfx = "assets/bundled/prefabs/fx/notice/item.select.fx.prefab";

        // client-side console cmds
        private static readonly HashSet<string> ClientOnlyCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            "noclip", "global.noclip",
            "respawn", "global.respawn",
            "respawn_sleepingbag", "global.respawn_sleepingbag",
            "suicide", "global.suicide",
            "hud", "global.hud",
            "combatlog", "global.combatlog",
            "debugcamera", "global.debugcamera",
            "consoletoggle", "global.consoletoggle",
        };

        #endregion

        #region Runtime state

        private readonly HashSet<ulong> _closedThisSession = new HashSet<ulong>();
        private readonly Dictionary<string, string> _iconCrc
            = new Dictionary<string, string>();
        private readonly Dictionary<ulong, Dictionary<int, Timer>> _flashTimers
            = new Dictionary<ulong, Dictionary<int, Timer>>();

        #endregion

        #region Config

        private Configuration _cfg;

        private class ButtonConfig
        {
            [JsonProperty("Label")]
            public string Label = "";

            [JsonProperty("Icon")]
            public string Icon = "";

            [JsonProperty("Commands")]
            public List<string> Commands = new List<string>();
        }

        private class Configuration
        {
            [JsonProperty("Chat command")]
            public string ChatCommand = "ab";

            [JsonProperty("Panel position (0 = BottomLeft, 1 = TopLeft, 2 = TopRight, 3 = BottomRight)")]
            public int PanelPosition = 0;

            [JsonProperty("UI Scale (%) — 100 = default, 75 = -25%, 50 = -50%, 150 = +50%")]
            public int Scale = 100;

            [JsonProperty("Auto-open on connect")]
            public bool AutoOpenOnConnect = true;

            [JsonProperty("Play click sound")]
            public bool PlayClickSound = true;

            [JsonProperty("Use local images (false = use Web URLs below)")]
            public bool UseLocalImages = true;

            [JsonProperty("Web image URLs (recommended 128x128 transparent PNG, used only when 'Use local images' = false)")]
            public Dictionary<string, string> WebImages = new Dictionary<string, string>();

            [JsonProperty("Buttons (left to right, max 8)")]
            public List<ButtonConfig> Buttons = new List<ButtonConfig>();
        }

        protected override void LoadDefaultConfig()
        {
            _cfg = new Configuration
            {
                Buttons = new List<ButtonConfig>
                {
                    new ButtonConfig { Label = "ADMIN",    Icon = "ADMIN",    Commands = new List<string> { "chat.say \"/admin\"" } },
                    new ButtonConfig { Label = "RADAR",    Icon = "RADAR",    Commands = new List<string> { "chat.say \"/radar\"" } },
                    new ButtonConfig { Label = "NOCLIP",   Icon = "NOCLIP",   Commands = new List<string> { "noclip" } },
                    new ButtonConfig { Label = "VANISH",   Icon = "VANISH",   Commands = new List<string> { "chat.say \"/vanish\"" } },
                    new ButtonConfig { Label = "OUTPOST",  Icon = "OUTPOST",  Commands = new List<string> { "outpost" } },
                    new ButtonConfig { Label = "BANDIT",   Icon = "BANDIT",   Commands = new List<string> { "bandit" } },
                    new ButtonConfig { Label = "TPMARKER", Icon = "TPMARKER", Commands = new List<string> { "teleport2marker" } },
                    new ButtonConfig { Label = "HEAL",     Icon = "HEAL",     Commands = new List<string> { "heal 100" } },
                },
                WebImages = new Dictionary<string, string>
                {
                    ["ADMIN"]    = "https://i.ibb.co/TBBS73X2/ADMIN.png",
                    ["RADAR"]    = "https://i.ibb.co/213xSyb1/RADAR.png",
                    ["NOCLIP"]   = "https://i.ibb.co/Q7TvJZTP/NOCLIP.png",
                    ["VANISH"]   = "https://i.ibb.co/qFyV9P6g/VANISH.png",
                    ["OUTPOST"]  = "https://i.ibb.co/4gjrwZ25/OUTPOST.png",
                    ["BANDIT"]   = "https://i.ibb.co/84NZhjkp/BANDIT.png",
                    ["TPMARKER"] = "https://i.ibb.co/F4JF9NP2/TPMARKER.png",
                    ["HEAL"]     = "https://i.ibb.co/vvwVb83C/HEAL.png",
                    ["CLOSE"]    = "https://i.ibb.co/ZRG7JtKr/CLOSE.png",
                }
            };
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try { _cfg = Config.ReadObject<Configuration>(); }
            catch { _cfg = null; }
            if (_cfg == null) LoadDefaultConfig();
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(_cfg);

        // Returns scale factor clamped to [25, 400] so nobody breaks the UI accidentally.
        private float ScaleFactor() => Mathf.Clamp(_cfg.Scale <= 0 ? 100 : _cfg.Scale, 25, 400) / 100f;

        #endregion

        #region Lifecycle

        private void Init()
        {
            permission.RegisterPermission(Perm, this);
            string chatCmd = string.IsNullOrWhiteSpace(_cfg.ChatCommand) ? "ab" : _cfg.ChatCommand;
            cmd.AddChatCommand(chatCmd, this, nameof(CmdToggle));
        }

        private void OnServerInitialized()
        {
            LoadAllIcons();
            if (!_cfg.AutoOpenOnConnect) return;
            foreach (var player in BasePlayer.activePlayerList)
                if (HasPerm(player)) DrawPanel(player);
        }

        private void Unload()
        {
            foreach (var player in BasePlayer.activePlayerList)
                CuiHelper.DestroyUi(player, PanelName);
            foreach (var inner in _flashTimers.Values)
                foreach (var t in inner.Values) t?.Destroy();
            _flashTimers.Clear();
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            if (player == null || !HasPerm(player)) return;
            if (!_cfg.AutoOpenOnConnect) return;
            timer.Once(2f, () =>
            {
                if (player != null && player.IsConnected && !_closedThisSession.Contains(player.userID))
                    DrawPanel(player);
            });
        }

        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            if (player == null) return;
            _closedThisSession.Remove(player.userID);
            CancelAllFlashes(player.userID);
            CuiHelper.DestroyUi(player, PanelName);
        }

        #endregion

        #region Icon loading

        private void LoadAllIcons()
        {
            _iconCrc.Clear();
            var keys = new HashSet<string>();
            foreach (var b in _cfg.Buttons.Take(MaxButtons))
                if (!string.IsNullOrEmpty(b.Icon)) keys.Add(b.Icon);
            keys.Add("CLOSE");

            foreach (var key in keys)
            {
                if (_cfg.UseLocalImages) LoadLocal(key);
                else LoadWeb(key);
            }
        }

        private void LoadLocal(string key)
        {
            string path = Path.Combine(Interface.Oxide.DataDirectory, DataSubdir, key + ".png");
            if (!File.Exists(path))
            {
                PrintWarning($"[AdminButtons] Missing local image: {path}");
                return;
            }
            try { StoreImage(key, File.ReadAllBytes(path)); }
            catch (Exception e) { PrintWarning($"[AdminButtons] Read failed {path}: {e.Message}"); }
        }

        private void LoadWeb(string key)
        {
            string url;
            if (!_cfg.WebImages.TryGetValue(key, out url) || string.IsNullOrWhiteSpace(url))
            {
                PrintWarning($"[AdminButtons] Missing WebImages URL for icon '{key}'");
                return;
            }
            ServerMgr.Instance.StartCoroutine(DownloadAndStore(key, url));
        }

        private System.Collections.IEnumerator DownloadAndStore(string key, string url)
        {
            using (var req = UnityWebRequest.Get(url))
            {
                req.downloadHandler = new DownloadHandlerBuffer();
                yield return req.SendWebRequest();
                if (req.isNetworkError || req.isHttpError)
                {
                    PrintWarning($"[AdminButtons] Download failed {url}: {req.error}");
                    yield break;
                }
                StoreImage(key, req.downloadHandler.data);
            }
        }

        private void StoreImage(string key, byte[] data)
        {
            try
            {
                uint crc = FileStorage.server.Store(data, FileStorage.Type.png, CommunityEntity.ServerInstance.net.ID);
                _iconCrc[key] = crc.ToString();
            }
            catch (Exception e) { PrintWarning($"[AdminButtons] FileStorage error {key}: {e.Message}"); }
        }

        #endregion

        #region UI

        private float ComputePanelWidth(float s)
        {
            int count = Math.Min(_cfg.Buttons.Count, MaxButtons);
            if (count <= 0) count = 1;
            float gridW = count * CellW * s + (count - 1) * CellGap * s;
            return PadX * s + gridW + CloseGap * s + CloseSize * s + PadX * s;
        }

        private float PanelHeight(float s) => (PadY + CellH + PadY) * s;

        private void DrawPanel(BasePlayer player)
        {
            CuiHelper.DestroyUi(player, PanelName);
            CancelAllFlashes(player.userID);

            float s = ScaleFactor();
            float panelW = ComputePanelWidth(s);
            float panelH = PanelHeight(s);

            var e = new CuiElementContainer();
            string aMin, aMax, oMin, oMax;
            GetPanelAnchors(_cfg.PanelPosition, panelW, panelH, s, out aMin, out aMax, out oMin, out oMax);

            e.Add(new CuiPanel
            {
                Image = { Color = "0.05 0.06 0.08 0.78", Material = "assets/content/ui/uibackgroundblur.mat" },
                RectTransform = { AnchorMin = aMin, AnchorMax = aMax, OffsetMin = oMin, OffsetMax = oMax },
                CursorEnabled = false,
            }, "Overlay", PanelName);

            // top accent
            e.Add(new CuiPanel
            {
                Image = { Color = "1 0.78 0.28 0.7" },
                RectTransform = { AnchorMin = "0 1", AnchorMax = "1 1", OffsetMin = "0 -1", OffsetMax = "0 0" }
            }, PanelName);

            // bottom line
            e.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0.5" },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 0", OffsetMin = "0 0", OffsetMax = "0 1" }
            }, PanelName);

            var btns = _cfg.Buttons.Take(MaxButtons).ToList();
            for (int i = 0; i < btns.Count; i++)
            {
                float x0 = PadX * s + i * (CellW + CellGap) * s;
                float x1 = x0 + CellW * s;
                float y0 = PadY * s;
                float y1 = y0 + CellH * s;
                DrawCell(e, btns[i], i, player, x0, y0, x1, y1, flashOverride: false, s: s);
            }

            float closeX1 = panelW - PadX * s;
            float closeX0 = closeX1 - CloseSize * s;
            float closeY0 = (panelH - CloseSize * s) / 2f;
            float closeY1 = closeY0 + CloseSize * s;
            DrawCloseButton(e, closeX0, closeY0, closeX1, closeY1, s);

            CuiHelper.AddUi(player, e);
        }

        private void DrawCloseButton(CuiElementContainer e, float x0, float y0, float x1, float y1, float s)
        {
            string name = PanelName + ".Close";

            e.Add(new CuiPanel
            {
                Image = { Color = "0 0 0 0.0" },
                RectTransform = {
                    AnchorMin = "0 0", AnchorMax = "0 0",
                    OffsetMin = $"{x0} {y0}", OffsetMax = $"{x1} {y1}"
                }
            }, PanelName, name);

            string crc;
            if (_iconCrc.TryGetValue("CLOSE", out crc) && !string.IsNullOrEmpty(crc))
            {
                e.Add(new CuiElement
                {
                    Parent = name,
                    Components =
                    {
                        new CuiRawImageComponent { Png = crc, Color = "1 0.78 0.28 0.95" },
                        new CuiRectTransformComponent { AnchorMin = "0.18 0.18", AnchorMax = "0.82 0.82" }
                    }
                });
            }
            else
            {
                e.Add(new CuiLabel
                {
                    Text = { Text = "X", FontSize = Mathf.Max(1, Mathf.RoundToInt(CloseFontSize * s)), Align = TextAnchor.MiddleCenter, Color = "1 0.78 0.28 1", Font = "robotocondensed-bold.ttf" },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" }
                }, name);
            }

            e.Add(new CuiButton
            {
                Button = { Color = "1 1 1 0.01", Command = "adminbuttons.close" },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                Text = { Text = "" }
            }, name, name + ".Hit");
        }

        private void DrawCell(CuiElementContainer e, ButtonConfig btn, int idx, BasePlayer player,
                              float x0, float y0, float x1, float y1, bool flashOverride, float s)
        {
            string cellName = $"{PanelName}.Btn{idx}";

            string bgColor = flashOverride ? "1 0.78 0.28 0.55" : "0.085 0.10 0.13 0.78";

            e.Add(new CuiPanel
            {
                Image = { Color = bgColor },
                RectTransform = {
                    AnchorMin = "0 0", AnchorMax = "0 0",
                    OffsetMin = $"{x0} {y0}", OffsetMax = $"{x1} {y1}"
                }
            }, PanelName, cellName);

            e.Add(new CuiPanel
            {
                Image = { Color = flashOverride ? "1 0.78 0.28 0.95" : "0.22 0.26 0.32 0.45" },
                RectTransform = { AnchorMin = "0 1", AnchorMax = "1 1", OffsetMin = "0 -1", OffsetMax = "0 0" }
            }, cellName);

            string crc;
            if (!string.IsNullOrEmpty(btn.Icon) && _iconCrc.TryGetValue(btn.Icon, out crc) && !string.IsNullOrEmpty(crc))
            {
                string iconColor = flashOverride ? "1 1 1 1" : "0.92 0.92 0.92 0.92";

                bool hasLabel = !string.IsNullOrEmpty(btn.Label);
                string iconAnchorMin = hasLabel ? "0.12 0.32" : "0.08 0.08";
                string iconAnchorMax = hasLabel ? "0.88 0.96" : "0.92 0.92";

                e.Add(new CuiElement
                {
                    Parent = cellName,
                    Components =
                    {
                        new CuiRawImageComponent { Png = crc, Color = iconColor },
                        new CuiRectTransformComponent { AnchorMin = iconAnchorMin, AnchorMax = iconAnchorMax }
                    }
                });
            }

            if (!string.IsNullOrEmpty(btn.Label))
            {
                string labelColor = flashOverride ? "1 1 1 1" : "0.78 0.82 0.86 0.92";

                e.Add(new CuiLabel
                {
                    Text = {
                        Text = btn.Label.ToUpperInvariant(),
                        FontSize = Mathf.Max(1, Mathf.RoundToInt(LabelFontSize * s)),
                        Align = TextAnchor.MiddleCenter,
                        Font = "robotocondensed-bold.ttf",
                        Color = labelColor
                    },
                    RectTransform = { AnchorMin = "0 0.04", AnchorMax = "1 0.30" }
                }, cellName);
            }

            e.Add(new CuiButton
            {
                Button = { Color = "1 1 1 0.01", Command = $"adminbuttons.click {idx}" },
                RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                Text = { Text = "" }
            }, cellName, cellName + ".Hit");
        }

        private void RedrawCell(BasePlayer player, int idx, bool flash)
        {
            if (player == null || !player.IsConnected) return;
            if (idx < 0 || idx >= _cfg.Buttons.Count || idx >= MaxButtons) return;

            string cellName = $"{PanelName}.Btn{idx}";
            CuiHelper.DestroyUi(player, cellName);

            float s = ScaleFactor();
            float x0 = PadX * s + idx * (CellW + CellGap) * s;
            float x1 = x0 + CellW * s;
            float y0 = PadY * s;
            float y1 = y0 + CellH * s;

            var e = new CuiElementContainer();
            DrawCell(e, _cfg.Buttons[idx], idx, player, x0, y0, x1, y1, flashOverride: flash, s: s);
            CuiHelper.AddUi(player, e);
        }

        private void GetPanelAnchors(int pos, float w, float h, float s, out string aMin, out string aMax, out string oMin, out string oMax)
        {
            float m = ScreenMargin * s;
            switch (pos)
            {
                case 1: aMin = "0 1"; aMax = "0 1"; oMin = $"{m} {-h - m}";       oMax = $"{m + w} {-m}"; break;
                case 2: aMin = "1 1"; aMax = "1 1"; oMin = $"{-w - m} {-h - m}";  oMax = $"{-m} {-m}"; break;
                case 3: aMin = "1 0"; aMax = "1 0"; oMin = $"{-w - m} {m}";       oMax = $"{-m} {m + h}"; break;
                default:aMin = "0 0"; aMax = "0 0"; oMin = $"{m} {m}";            oMax = $"{m + w} {m + h}"; break;
            }
        }

        #endregion

        #region Click feedback

        private void PlayClick(BasePlayer player)
        {
            if (!_cfg.PlayClickSound) return;
            try
            {
                var effect = new Effect(ClickSfx, player.transform.position, Vector3.zero);
                EffectNetwork.Send(effect, player.net.connection);
            }
            catch (Exception e) { PrintWarning($"[AdminButtons] Sound error: {e.Message}"); }
        }

        private void FlashCell(BasePlayer player, int idx)
        {
            if (player == null) return;

            Dictionary<int, Timer> inner;
            if (_flashTimers.TryGetValue(player.userID, out inner))
            {
                Timer existing;
                if (inner.TryGetValue(idx, out existing)) { existing?.Destroy(); inner.Remove(idx); }
            }

            RedrawCell(player, idx, flash: true);

            var t = timer.Once(0.22f, () =>
            {
                Dictionary<int, Timer> map2;
                if (_flashTimers.TryGetValue(player.userID, out map2)) map2.Remove(idx);
                if (player != null && player.IsConnected && !_closedThisSession.Contains(player.userID))
                    RedrawCell(player, idx, flash: false);
            });

            if (!_flashTimers.TryGetValue(player.userID, out inner))
            {
                inner = new Dictionary<int, Timer>();
                _flashTimers[player.userID] = inner;
            }
            inner[idx] = t;
        }

        private void CancelAllFlashes(ulong userId)
        {
            Dictionary<int, Timer> inner;
            if (!_flashTimers.TryGetValue(userId, out inner)) return;
            foreach (var t in inner.Values) t?.Destroy();
            _flashTimers.Remove(userId);
        }

        #endregion

        #region Commands

        private void CmdToggle(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!HasPerm(player))
            {
                player.ChatMessage("You don't have permission.");
                return;
            }
            if (_closedThisSession.Contains(player.userID))
            {
                _closedThisSession.Remove(player.userID);
                DrawPanel(player);
            }
            else
            {
                CuiHelper.DestroyUi(player, PanelName);
                _closedThisSession.Add(player.userID);
            }
        }

        [ConsoleCommand("adminbuttons.close")]
        private void CmdClose(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null || !HasPerm(player)) return;
            CuiHelper.DestroyUi(player, PanelName);
            CancelAllFlashes(player.userID);
            _closedThisSession.Add(player.userID);
        }

        [ConsoleCommand("adminbuttons.click")]
        private void CmdClick(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player == null) return;
            if (!HasPerm(player))
            {
                player.ChatMessage("You don't have permission.");
                return;
            }
            if (!arg.HasArgs(1)) return;
            int idx;
            if (!int.TryParse(arg.Args[0], out idx)) return;
            if (idx < 0 || idx >= _cfg.Buttons.Count || idx >= MaxButtons) return;

            var btn = _cfg.Buttons[idx];

            PlayClick(player);
            FlashCell(player, idx);

            if (btn.Commands == null) return;
            foreach (var raw in btn.Commands)
            {
                if (string.IsNullOrWhiteSpace(raw)) continue;
                string cmd = raw
                    .Replace("{steamid}", player.UserIDString)
                    .Replace("{name}", player.displayName ?? "");

                // kill via API (console can be no-op under god/protection)
                string trimmed = cmd.Trim().ToLowerInvariant();
                if (trimmed == "kill" || trimmed == "global.kill")
                {
                    try { player.Die(); }
                    catch (Exception e) { PrintWarning($"[AdminButtons] Die() error: {e.Message}"); }
                    continue;
                }

                try
                {
                    string firstToken = cmd.TrimStart().Split(new[] { ' ', '\t' }, 2)[0];
                    if (ClientOnlyCommands.Contains(firstToken))
                        player.SendConsoleCommand(cmd);
                    else
                        ConsoleSystem.Run(
                            ConsoleSystem.Option.Server.FromConnection(player.net.connection),
                            cmd);
                }
                catch (Exception ex)
                {
                    PrintWarning($"[AdminButtons] Run('{cmd}') failed: {ex.Message}");
                }
            }
        }

        [ConsoleCommand("adminbuttons.reload")]
        private void CmdReloadImages(ConsoleSystem.Arg arg)
        {
            var player = arg.Player();
            if (player != null && !HasPerm(player)) return;
            LoadAllIcons();
            foreach (var p in BasePlayer.activePlayerList)
                if (HasPerm(p) && !_closedThisSession.Contains(p.userID))
                    DrawPanel(p);
            PrintWarning("[AdminButtons] Icons reloaded.");
        }

        #endregion

        #region Helpers

        private bool HasPerm(BasePlayer player)
            => player != null && permission.UserHasPermission(player.UserIDString, Perm);

        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху