C#:
using System;
using System.Collections.Generic;
using System.Linq;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Server Info Panel", "0killrest", "1.0.0")]
[Description("Beautiful UI panel with server info and customizable buttons")]
internal class ServerInfoPanel : RustPlugin
{
#region Configuration
private static ConfigData config;
private class ConfigData
{
public bool Enabled { get; set; } = true;
public PanelPosition Position { get; set; } = PanelPosition.TopRight;
public PanelSize Size { get; set; } = new PanelSize { Width = 0.18f, Height = 0.12f };
public Colors Colors { get; set; } = new Colors();
public List<ButtonConfig> Buttons { get; set; }
public bool ShowPlayerCount { get; set; } = true;
public bool ShowServerName { get; set; } = true;
public bool ShowTime { get; set; } = true;
public bool ShowSleepers { get; set; } = true;
public int UpdateIntervalSeconds { get; set; } = 5;
public ConfigData()
{
Colors = new Colors
{
Background = "#1a1a1a",
BackgroundAlpha = 0.85f,
Text = "#ffffff",
Accent = "#00ff00",
ButtonBackground = "#2d2d2d",
ButtonHover = "#3d3d3d"
};
Buttons = new List<ButtonConfig>
{
new ButtonConfig { Name = "kits", Icon = "🎒", Command = "kitgui.open", Tooltip = "Open Kits", Enabled = true, Order = 1 },
new ButtonConfig { Name = "rules", Icon = "📜", Command = "rules.show", Tooltip = "Server Rules", Enabled = true, Order = 2 },
new ButtonConfig { Name = "shop", Icon = "🛒", Command = "shop.open", Tooltip = "Item Shop", Enabled = true, Order = 3 },
new ButtonConfig { Name = "donate", Icon = "💎", Command = "donate.open", Tooltip = "Donate", Enabled = true, Order = 4 },
new ButtonConfig { Name = "discord", Icon = "💬", Command = "discord.open", Tooltip = "Join Discord", Enabled = true, Order = 5 },
new ButtonConfig { Name = "vip", Icon = "👑", Command = "vip.open", Tooltip = "VIP Info", Enabled = true, Order = 6 }
};
}
}
public enum PanelPosition
{
TopLeft,
TopRight,
BottomLeft,
BottomRight,
TopCenter,
BottomCenter
}
public class PanelSize
{
public float Width { get; set; }
public float Height { get; set; }
}
public class Colors
{
public string Background { get; set; }
public float BackgroundAlpha { get; set; }
public string Text { get; set; }
public string Accent { get; set; }
public string ButtonBackground { get; set; }
public string ButtonHover { get; set; }
}
public class ButtonConfig
{
public string Name { get; set; }
public string Icon { get; set; }
public string Command { get; set; }
public string Tooltip { get; set; }
public bool Enabled { get; set; }
public int Order { get; set; }
}
protected override void LoadConfig()
{
base.LoadConfig();
config = Config.ReadObject<ConfigData>();
if (config == null)
{
LoadDefaultConfig();
}
}
protected override void LoadDefaultConfig()
{
config = new ConfigData();
PrintWarning("Default configuration created.Edit it in oxide/config/ServerInfoPanel.json");
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
#endregion
#region Data
private Dictionary<ulong, Timer> updateTimers = new Dictionary<ulong, Timer>();
private Dictionary<ulong, bool> panelVisible = new Dictionary<ulong, bool>();
private const string PanelName = "ServerInfoPanel";
private const string ButtonPanelName = "ServerInfoButtons";
private void LoadData()
{
// No persistent data needed
}
#endregion
#region Hooks
private void Init()
{
LoadData();
permission.RegisterPermission("serverinfopanel.admin", this);
permission.RegisterPermission("serverinfopanel.see", this);
}
private void OnServerInitialized()
{
if (!config.Enabled) return;
// Show panel to all existing players
foreach (var player in BasePlayer.activePlayerList)
{
if (HasPermission(player))
{
ShowPanel(player);
StartUpdateTimer(player);
}
}
}
private void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
CuiHelper.DestroyUi(player, PanelName);
CuiHelper.DestroyUi(player, ButtonPanelName);
if (updateTimers.ContainsKey(player.userID))
{
updateTimers[player.userID]?.Destroy();
updateTimers.Remove(player.userID);
}
}
}
private void OnPlayerInit(BasePlayer player)
{
if (!config.Enabled) return;
if (!HasPermission(player)) return;
timer.Once(1f, () =>
{
ShowPanel(player);
StartUpdateTimer(player);
});
}
private void OnPlayerDisconnected(BasePlayer player)
{
if (updateTimers.ContainsKey(player.userID))
{
updateTimers[player.userID]?.Destroy();
updateTimers.Remove(player.userID);
}
}
private bool HasPermission(BasePlayer player)
{
return permission.UserHasPermission(player.UserIDString, "serverinfopanel.see") || player.IsAdmin;
}
#endregion
#region UI Creation
private void ShowPanel(BasePlayer player)
{
CuiHelper.DestroyUi(player, PanelName);
CuiHelper.DestroyUi(player, ButtonPanelName);
// Get position based on config
string anchorMin, anchorMax;
GetPanelAnchors(out anchorMin, out anchorMax);
CuiElementContainer container = new CuiElementContainer();
// Main panel
string mainPanel = container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.Colors.Background, config.Colors.BackgroundAlpha) },
RectTransform = { AnchorMin = anchorMin, AnchorMax = anchorMax }
}, "Overlay", PanelName);
// Inner panel with border
string innerPanel = container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.Colors.Background, 1f) },
RectTransform = { AnchorMin = "0.02 0.02", AnchorMax = "0.98 0.98" }
}, mainPanel);
// Accent line (top)
container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.Colors.Accent, 1f) },
RectTransform = { AnchorMin = "0 0.9", AnchorMax = "1 1" }
}, innerPanel);
float currentY = 0.85f;
float lineHeight = 0.12f;
// Server name
if (config.ShowServerName)
{
string serverName = ConVar.Server.hostname;
if (serverName.Length > 30) serverName = serverName.Substring(0, 27) + "...";
container.Add(new CuiText
{
Text = { Text = $"🏠 {serverName}", FontSize = 14, Align = TextAnchor.MiddleCenter, Color = HexToRGBA(config.Colors.Text, 1f) },
RectTransform = { AnchorMin = $"0.05 {currentY - lineHeight}", AnchorMax = $"0.95 {currentY}" }
}, innerPanel);
currentY -= lineHeight;
}
// Stats line (players + sleepers + time)
string statsText = "";
if (config.ShowPlayerCount)
{
int onlinePlayers = BasePlayer.activePlayerList.Count;
statsText += $"👥 {onlinePlayers}";
}
if (config.ShowSleepers)
{
int sleepers = BasePlayer.sleepingPlayerList.Count;
statsText += $" 💤 {sleepers}";
}
if (config.ShowTime)
{
statsText += $" 🕐 {DateTime.Now:HH:mm}";
}
if (!string.IsNullOrEmpty(statsText))
{
container.Add(new CuiText
{
Text = { Text = statsText, FontSize = 12, Align = TextAnchor.MiddleCenter, Color = HexToRGBA(config.Colors.Text, 0.8f) },
RectTransform = { AnchorMin = $"0.05 {currentY - lineHeight}", AnchorMax = $"0.95 {currentY}" }
}, innerPanel);
currentY -= lineHeight;
}
// Buttons panel (separate, below main panel)
ShowButtonsPanel(player);
}
private void ShowButtonsPanel(BasePlayer player)
{
CuiElementContainer container = new CuiElementContainer();
// Get position for buttons (right below main panel)
string anchorMin, anchorMax;
GetPanelAnchors(out anchorMin, out anchorMax);
// Parse anchor strings to floats and adjust
string[] minParts = anchorMin.Split(' ');
string[] maxParts = anchorMax.Split(' ');
float minX = float.Parse(minParts[0]);
float minY = float.Parse(minParts[1]);
float maxX = float.Parse(maxParts[0]);
float maxY = float.Parse(maxParts[1]);
float panelHeight = config.Size.Height;
float buttonPanelHeight = 0.05f;
float newMinY = minY - buttonPanelHeight;
float newMaxY = minY;
string buttonAnchorMin = $"{minX} {newMinY}";
string buttonAnchorMax = $"{maxX} {newMaxY}";
// Buttons panel background
string buttonsPanel = container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.Colors.Background, config.Colors.BackgroundAlpha) },
RectTransform = { AnchorMin = buttonAnchorMin, AnchorMax = buttonAnchorMax }
}, "Overlay", ButtonPanelName);
// Calculate button layout
var enabledButtons = config.Buttons.Where(b => b.Enabled).OrderBy(b => b.Order).ToList();
if (enabledButtons.Count == 0) return;
float buttonWidth = 1f / enabledButtons.Count;
for (int i = 0; i < enabledButtons.Count; i++)
{
var button = enabledButtons[i];
float buttonMinX = i * buttonWidth;
float buttonMaxX = (i + 1) * buttonWidth;
// Add margin between buttons
buttonMinX += 0.01f;
buttonMaxX -= 0.01f;
string buttonPanel = container.Add(new CuiPanel
{
Image = { Color = HexToRGBA(config.Colors.ButtonBackground, 1f) },
RectTransform = { AnchorMin = $"{buttonMinX} 0.05", AnchorMax = $"{buttonMaxX} 0.95" }
}, buttonsPanel);
// Button with hover effect
string buttonElement = container.Add(new CuiButton
{
Button = { Color = HexToRGBA(config.Colors.ButtonBackground, 1f), Command = $"serverpanel.button {button.Command}" },
RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
Text = { Text = $"{button.Icon}", FontSize = 16, Align = TextAnchor.MiddleCenter, Color = HexToRGBA(config.Colors.Text, 1f) }
}, buttonPanel);
// Tooltip text on hover (optional - simple for now)
}
CuiHelper.AddUi(player, container);
}
private void GetPanelAnchors(out string anchorMin, out string anchorMax)
{
float width = config.Size.Width;
float height = config.Size.Height;
switch (config.Position)
{
case PanelPosition.TopLeft:
anchorMin = $"0.01 0.86";
anchorMax = $"{0.01 + width} 0.99";
break;
case PanelPosition.TopRight:
anchorMin = $"{1 - width - 0.01} 0.86";
anchorMax = $"0.99 0.99";
break;
case PanelPosition.BottomLeft:
anchorMin = $"0.01 0.01";
anchorMax = $"{0.01 + width} {0.01 + height}";
break;
case PanelPosition.BottomRight:
anchorMin = $"{1 - width - 0.01} 0.01";
anchorMax = $"0.99 {0.01 + height}";
break;
case PanelPosition.TopCenter:
anchorMin = $"{0.5 - width/2} 0.86";
anchorMax = $"{0.5 + width/2} 0.99";
break;
case PanelPosition.BottomCenter:
anchorMin = $"{0.5 - width/2} 0.01";
anchorMax = $"{0.5 + width/2} {0.01 + height}";
break;
default:
anchorMin = $"0.81 0.86";
anchorMax = $"0.99 0.99";
break;
}
}
private void UpdatePanelData(BasePlayer player)
{
if (!config.Enabled) return;
if (!HasPermission(player)) return;
CuiHelper.DestroyUi(player, PanelName);
ShowPanel(player);
}
private void StartUpdateTimer(BasePlayer player)
{
if (updateTimers.ContainsKey(player.userID))
{
updateTimers[player.userID]?.Destroy();
updateTimers.Remove(player.userID);
}
updateTimers[player.userID] = timer.Repeat(config.UpdateIntervalSeconds, 0, () =>
{
if (player == null || !player.IsConnected)
{
updateTimers[player.userID]?.Destroy();
updateTimers.Remove(player.userID);
return;
}
UpdatePanelData(player);
});
}
private string HexToRGBA(string hex, float alpha = 1f)
{
if (string.IsNullOrEmpty(hex)) hex = "#ffffff";
hex = hex.TrimStart('#');
float r = int.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float g = int.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
float b = int.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber) / 255f;
return $"{r} {g} {b} {alpha}";
}
#endregion
#region Commands
[ConsoleCommand("serverpanel.button")]
private void cmdButtonPress(ConsoleSystem.Arg args)
{
BasePlayer player = args.Connection?.player as BasePlayer;
if (player == null) return;
string command = args.GetString(0);
if (string.IsNullOrEmpty(command)) return;
Puts($"Player {player.displayName} pressed button: {command}");
// Execute the command
player.SendConsoleCommand(command);
}
[ChatCommand("panel")]
private void cmdTogglePanel(BasePlayer player, string command, string[] args)
{
if (!HasPermission(player))
{
SendReply(player, "You don't have permission to use this command.");
return;
}
if (!panelVisible.ContainsKey(player.userID))
panelVisible[player.userID] = true;
if (panelVisible[player.userID])
{
CuiHelper.DestroyUi(player, PanelName);
CuiHelper.DestroyUi(player, ButtonPanelName);
SendReply(player, "Info panel hidden.Type /panel to show again.");
panelVisible[player.userID] = false;
}
else
{
ShowPanel(player);
SendReply(player, "Info panel shown.");
panelVisible[player.userID] = true;
}
}
[ChatCommand("panelreload")]
private void cmdReloadPanel(BasePlayer player, string command, string[] args)
{
if (!player.IsAdmin && !permission.UserHasPermission(player.UserIDString, "serverinfopanel.admin"))
{
SendReply(player, "You don't have permission to use this command.");
return;
}
LoadConfig();
foreach (var p in BasePlayer.activePlayerList)
{
if (HasPermission(p))
{
CuiHelper.DestroyUi(p, PanelName);
CuiHelper.DestroyUi(p, ButtonPanelName);
ShowPanel(p);
}
}
SendReply(player, "ServerInfoPanel reloaded.");
}
[ConsoleCommand("serverinfopanel.reload")]
private void cmdConsoleReload(ConsoleSystem.Arg args)
{
if (args.Connection != null && !permission.UserHasPermission(args.Connection.UserId.ToString(), "serverinfopanel.admin"))
{
args.ReplyWith("No permission");
return;
}
LoadConfig();
foreach (var player in BasePlayer.activePlayerList)
{
if (HasPermission(player))
{
CuiHelper.DestroyUi(player, PanelName);
CuiHelper.DestroyUi(player, ButtonPanelName);
ShowPanel(player);
}
}
args.ReplyWith("ServerInfoPanel reloaded");
Puts("ServerInfoPanel configuration reloaded");
}
#endregion
}
}