C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
/* --- Contributor information ---
* Please follow the following set of guidelines when working on this plugin,
* this to help others understand this file more easily.*
* -- Authors --
* Thimo (ThibmoRozier) <thibmorozier@live.nl>
* rfc1920 <no@email.com>
* Mheetu <no@email.com>
*
* -- Naming --
* Avoid using non-alphabetic characters, eg: _
* Avoid using numbers in method and class names (Upgrade methods are allowed to have these, for readability)
* Private constants -------------------- SHOULD start with a uppercase "C" (PascalCase)
* Private readonly fields -------------- SHOULD start with a uppercase "C" (PascalCase)
* Private fields ----------------------- SHOULD start with a uppercase "F" (PascalCase)
* Arguments/Parameters ----------------- SHOULD start with a lowercase "a" (camelCase)
* Classes ------------------------------ SHOULD start with a uppercase character (PascalCase)
* Methods ------------------------------ SHOULD start with a uppercase character (PascalCase)
* Public properties (constants/fields) - SHOULD start with a uppercase character (PascalCase)
* Variables ---------------------------- SHOULD start with a lowercase character (camelCase)
*
* -- Style --
* Single-line comments - // Single-line comment
* Multi-line comments -- Just like this comment block!
*/
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("PlayerAdministration", "ThibmoRozier", "1.3.21")]
[Description("Allows server admins to moderate users using a GUI from within the game.")]
public class PlayerAdministration : RustPlugin
{
#region Plugin References
#pragma warning disable IDE0044, CS0649
[PluginReference]
private Plugin Economics;
[PluginReference]
private Plugin ServerRewards;
[PluginReference]
private Plugin Freeze;
[PluginReference]
private Plugin PermissionsManager;
[PluginReference]
private Plugin DiscordMessages;
[PluginReference]
private Plugin BetterChatMute;
#pragma warning restore IDE0044, CS0649
#endregion Plugin References
#region GUI
#region Types
/// <summary>
/// UI Color object
/// </summary>
private class CuiColor
{
public byte R { get; set; } = 255;
public byte G { get; set; } = 255;
public byte B { get; set; } = 255;
public float A { get; set; } = 1f;
public CuiColor() { }
public CuiColor(byte aRed, byte aGreen, byte aBlue, float aAlpha = 1f)
{
R = aRed;
G = aGreen;
B = aBlue;
A = aAlpha;
}
public override string ToString() =>
$"{(double)R / 255} {(double)G / 255} {(double)B / 255} {A}";
}
/// <summary>
/// Element position object
/// </summary>
private class CuiPoint
{
public float X { get; set; } = 0f;
public float Y { get; set; } = 0f;
public CuiPoint() { }
public CuiPoint(float aX, float aY)
{
X = aX;
Y = aY;
}
public override string ToString() =>
$"{X} {Y}";
}
/// <summary>
/// UI pages to make the switching more humanly readable
/// </summary>
private enum UiPage
{
Main = 0,
PlayersOnline,
PlayersOffline,
PlayersBanned,
PlayerPage,
PlayerPageBanned
}
#endregion Types
#region Defaults
/// <summary>
/// Predefined default color set
/// </summary>
private static class CuiDefaultColors
{
public static readonly CuiColor Background = new CuiColor(240, 240, 240, 0.3f);
public static readonly CuiColor BackgroundMedium = new CuiColor(76, 74, 72, 0.83f);
public static readonly CuiColor BackgroundDark = new CuiColor(42, 42, 42, 0.93f);
public static readonly CuiColor Button = new CuiColor(42, 42, 42, 1f);
public static readonly CuiColor ButtonInactive = new CuiColor(168, 168, 168, 1f);
public static readonly CuiColor ButtonDecline = new CuiColor(192, 0, 0, 1f);
public static readonly CuiColor ButtonDanger = new CuiColor(193, 46, 42, 1f);
public static readonly CuiColor ButtonWarning = new CuiColor(213, 133, 18, 1f);
public static readonly CuiColor ButtonSuccess = new CuiColor(57, 132, 57, 1f);
public static readonly CuiColor Text = new CuiColor(0, 0, 0, 1f);
public static readonly CuiColor TextAlt = new CuiColor(255, 255, 255, 1f);
public static readonly CuiColor TextTitle = new CuiColor(206, 66, 43, 1f);
public static readonly CuiColor None = new CuiColor(0, 0, 0, 0f);
}
#endregion Defaults
#region UI object definitions
/// <summary>
/// Input field object
/// </summary>
private class CuiInputField
{
public CuiInputFieldComponent InputField { get; } = new CuiInputFieldComponent();
public CuiRectTransformComponent RectTransform { get; } = new CuiRectTransformComponent();
public float FadeOut { get; set; }
}
#endregion UI object definitions
#region Component container
/// <summary>
/// Custom version of the CuiElementContainer to add InputFields
/// </summary>
private class CustomCuiElementContainer : CuiElementContainer
{
public string Add(CuiInputField aInputField, string aParent = Cui.ParentHud, string aName = "")
{
if (string.IsNullOrEmpty(aName))
aName = CuiHelper.GetGuid();
if (aInputField == null) {
FPluginInstance.LogError($"CustomCuiElementContainer::Add > Parameter 'aInputField' is null");
return string.Empty;
}
Add(new CuiElement {
Name = aName,
Parent = aParent,
FadeOut = aInputField.FadeOut,
Components = {
aInputField.InputField,
aInputField.RectTransform
}
});
return aName;
}
}
#endregion Component container
/// <summary>
/// Rust UI object
/// </summary>
private class Cui
{
public const string ParentHud = "Hud";
public const string ParentOverlay = "Overlay";
public string MainPanelName { get; set; }
private readonly BasePlayer FPlayer;
private readonly CustomCuiElementContainer FContainer = new CustomCuiElementContainer();
/// <summary>
/// Constructor
/// </summary>
/// <param name="aPlayer">The player this object is meant for</param>
public Cui(BasePlayer aPlayer)
{
if (aPlayer == null) {
FPluginInstance.LogError($"Cui::Cui > Parameter 'aPlayer' is null");
return;
}
FPlayer = aPlayer;
FPluginInstance.LogDebug("Cui instance created");
}
/// <summary>
/// Add a new panel
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aIndCursorEnabled">The panel requires the cursor</param>
/// <param name="aColor">Image color</param>
/// <param name="aName">The object's name</param>
/// <param name="aPng">Image PNG file path</param>
/// <returns>New object name</returns>
public string AddPanel(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, bool aIndCursorEnabled, CuiColor aColor = null,
string aName = "", string aPng = "") =>
AddPanel(aParent, aLeftBottomAnchor, aRightTopAnchor, new CuiPoint(), new CuiPoint(), aIndCursorEnabled, aColor, aName, aPng);
/// <summary>
/// Add a new panel
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aLeftBottomOffset">Left(x)-Bottom(y) relative offset</param>
/// <param name="aRightTopOffset">Right(x)-Top(y) relative offset</param>
/// <param name="aIndCursorEnabled">The panel requires the cursor</param>
/// <param name="aColor">Image color</param>
/// <param name="aName">The object's name</param>
/// <param name="aPng">Image PNG file path</param>
/// <returns>New object name</returns>
public string AddPanel(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiPoint aLeftBottomOffset, CuiPoint aRightTopOffset,
bool aIndCursorEnabled, CuiColor aColor = null, string aName = "", string aPng = "")
{
if (aLeftBottomAnchor == null || aRightTopAnchor == null || aLeftBottomOffset == null || aRightTopOffset == null) {
FPluginInstance.LogError($"Cui::AddPanel > One of the required parameters is null");
return string.Empty;
}
CuiPanel panel = new CuiPanel() {
RectTransform = {
AnchorMin = aLeftBottomAnchor.ToString(),
AnchorMax = aRightTopAnchor.ToString(),
OffsetMin = aLeftBottomOffset.ToString(),
OffsetMax = aRightTopOffset.ToString()
},
CursorEnabled = aIndCursorEnabled
};
if (!string.IsNullOrEmpty(aPng))
panel.Image = new CuiImageComponent() {
Png = aPng
};
if (aColor != null) {
if (panel.Image == null) {
panel.Image = new CuiImageComponent() {
Color = aColor.ToString()
};
} else {
panel.Image.Color = aColor.ToString();
}
}
FPluginInstance.LogDebug("Added panel to container");
return FContainer.Add(panel, aParent, string.IsNullOrEmpty(aName) ? null : aName);
}
/// <summary>
/// Add a new label
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aColor">Text color</param>
/// <param name="aText">Text to show</param>
/// <param name="aName">The object's name</param>
/// <param name="aFontSize">Font size</param>
/// <param name="aAlign">Text alignment</param>
/// <returns>New object name</returns>
public string AddLabel(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiColor aColor, string aText, string aName = "",
int aFontSize = 14, TextAnchor aAlign = TextAnchor.UpperLeft) =>
AddLabel(aParent, aLeftBottomAnchor, aRightTopAnchor, new CuiPoint(), new CuiPoint(), aColor, aText, aName, aFontSize, aAlign);
/// <summary>
/// Add a new label
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aLeftBottomOffset">Left(x)-Bottom(y) relative offset</param>
/// <param name="aRightTopOffset">Right(x)-Top(y) relative offset</param>
/// <param name="aColor">Text color</param>
/// <param name="aText">Text to show</param>
/// <param name="aName">The object's name</param>
/// <param name="aFontSize">Font size</param>
/// <param name="aAlign">Text alignment</param>
/// <returns>New object name</returns>
public string AddLabel(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiPoint aLeftBottomOffset, CuiPoint aRightTopOffset,
CuiColor aColor, string aText, string aName = "", int aFontSize = 14, TextAnchor aAlign = TextAnchor.UpperLeft)
{
if (aLeftBottomAnchor == null || aRightTopAnchor == null || aLeftBottomOffset == null || aRightTopOffset == null || aColor == null) {
FPluginInstance.LogError($"Cui::AddLabel > One of the required parameters is null");
return string.Empty;
}
FPluginInstance.LogDebug("Added label to container");
return FContainer.Add(new CuiLabel() {
Text = {
Text = aText ?? string.Empty,
FontSize = aFontSize,
Align = aAlign,
Color = aColor.ToString()
},
RectTransform = {
AnchorMin = aLeftBottomAnchor.ToString(),
AnchorMax = aRightTopAnchor.ToString(),
OffsetMin = aLeftBottomOffset.ToString(),
OffsetMax = aRightTopOffset.ToString()
}
}, aParent, string.IsNullOrEmpty(aName) ? null : aName);
}
/// <summary>
/// Add a new button
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aButtonColor">Button background color</param>
/// <param name="aTextColor">Text color</param>
/// <param name="aText">Text to show</param>
/// <param name="aCommand">OnClick event callback command</param>
/// <param name="aClose">Panel to close</param>
/// <param name="aName">The object's name</param>
/// <param name="aFontSize">Font size</param>
/// <param name="aAlign">Text alignment</param>
/// <returns>New object name</returns>
public string AddButton(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiColor aButtonColor, CuiColor aTextColor, string aText,
string aCommand = "", string aClose = "", string aName = "", int aFontSize = 14, TextAnchor aAlign = TextAnchor.MiddleCenter) =>
AddButton(aParent, aLeftBottomAnchor, aRightTopAnchor, new CuiPoint(), new CuiPoint(), aButtonColor, aTextColor, aText, aCommand, aClose, aName,
aFontSize, aAlign);
/// <summary>
/// Add a new button
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aLeftBottomOffset">Left(x)-Bottom(y) relative offset</param>
/// <param name="aRightTopOffset">Right(x)-Top(y) relative offset</param>
/// <param name="aButtonColor">Button background color</param>
/// <param name="aTextColor">Text color</param>
/// <param name="aText">Text to show</param>
/// <param name="aCommand">OnClick event callback command</param>
/// <param name="aClose">Panel to close</param>
/// <param name="aName">The object's name</param>
/// <param name="aFontSize">Font size</param>
/// <param name="aAlign">Text alignment</param>
/// <returns>New object name</returns>
public string AddButton(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiPoint aLeftBottomOffset, CuiPoint aRightTopOffset,
CuiColor aButtonColor, CuiColor aTextColor, string aText, string aCommand = "", string aClose = "", string aName = "",
int aFontSize = 14, TextAnchor aAlign = TextAnchor.MiddleCenter)
{
if (aLeftBottomAnchor == null || aRightTopAnchor == null || aLeftBottomOffset == null || aRightTopOffset == null ||
aButtonColor == null || aTextColor == null) {
FPluginInstance.LogError($"Cui::AddButton > One of the required parameters is null");
return string.Empty;
}
FPluginInstance.LogDebug("Added button to container");
return FContainer.Add(new CuiButton() {
Button = {
Command = aCommand ?? string.Empty,
Close = aClose ?? string.Empty,
Color = aButtonColor.ToString()
},
RectTransform = {
AnchorMin = aLeftBottomAnchor.ToString(),
AnchorMax = aRightTopAnchor.ToString(),
OffsetMin = aLeftBottomOffset.ToString(),
OffsetMax = aRightTopOffset.ToString()
},
Text = {
Text = aText ?? string.Empty,
FontSize = aFontSize,
Align = aAlign,
Color = aTextColor.ToString()
}
}, aParent, string.IsNullOrEmpty(aName) ? null : aName);
}
/// <summary>
/// Add a new input field
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aColor">Text color</param>
/// <param name="aText">Text to show</param>
/// <param name="aCharsLimit">Max character count</param>
/// <param name="aCommand">OnChanged event callback command</param>
/// <param name="aIndPassword">Indicates that this input should show password chars</param>
/// <param name="aName">The object's name</param>
/// <param name="aFontSize">Font size</param>
/// <param name="aAlign">Text alignment</param>
/// <returns>New object name</returns>
public string AddInputField(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiColor aColor, string aText = "",
int aCharsLimit = 100, string aCommand = "", bool aIndPassword = false, string aName = "", int aFontSize = 14,
TextAnchor aAlign = TextAnchor.MiddleLeft) =>
AddInputField(aParent, aLeftBottomAnchor, aRightTopAnchor, new CuiPoint(), new CuiPoint(), aColor, aText, aCharsLimit, aCommand, aIndPassword, aName,
aFontSize, aAlign);
/// <summary>
/// Add a new input field
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aLeftBottomAnchor">Left(x)-Bottom(y) relative position</param>
/// <param name="aRightTopAnchor">Right(x)-Top(y) relative position</param>
/// <param name="aLeftBottomOffset">Left(x)-Bottom(y) relative offset</param>
/// <param name="aRightTopOffset">Right(x)-Top(y) relative offset</param>
/// <param name="fadeOut">Fade-out time</param>
/// <param name="aColor">Text color</param>
/// <param name="aText">Text to show</param>
/// <param name="aCharsLimit">Max character count</param>
/// <param name="aCommand">OnChanged event callback command</param>
/// <param name="aIndPassword">Indicates that this input should show password chars</param>
/// <param name="aName">The object's name</param>
/// <param name="aFontSize">Font size</param>
/// <returns>New object name</returns>
public string AddInputField(string aParent, CuiPoint aLeftBottomAnchor, CuiPoint aRightTopAnchor, CuiPoint aLeftBottomOffset, CuiPoint aRightTopOffset,
CuiColor aColor, string aText = "", int aCharsLimit = 100, string aCommand = "", bool aIndPassword = false,
string aName = "", int aFontSize = 14, TextAnchor aAlign = TextAnchor.MiddleLeft)
{
if (aLeftBottomAnchor == null || aRightTopAnchor == null || aLeftBottomOffset == null || aRightTopOffset == null || aColor == null) {
FPluginInstance.LogError($"Cui::AddInputField > One of the required parameters is null");
return string.Empty;
}
FPluginInstance.LogDebug("Added input field to container");
return FContainer.Add(new CuiInputField() {
InputField = {
Text = aText ?? string.Empty,
FontSize = aFontSize,
Align = aAlign,
Color = aColor.ToString(),
CharsLimit = aCharsLimit,
Command = aCommand ?? string.Empty,
IsPassword = aIndPassword
},
RectTransform = {
AnchorMin = aLeftBottomAnchor.ToString(),
AnchorMax = aRightTopAnchor.ToString(),
OffsetMin = aLeftBottomOffset.ToString(),
OffsetMax = aRightTopOffset.ToString()
}
}, aParent, string.IsNullOrEmpty(aName) ? null : aName);
}
/// <summary>
/// Draw the UI to the player's client
/// </summary>
/// <returns></returns>
public bool Draw()
{
if (!string.IsNullOrEmpty(MainPanelName)) {
FPluginInstance.LogDebug("Sent the container for drawing to the client");
return CuiHelper.AddUi(FPlayer, FContainer);
}
return false;
}
/// <summary>
/// Retrieve the userId of the player this GUI is intended for
/// </summary>
/// <returns>Player ID</returns>
public string GetPlayerId() =>
FPlayer.UserIDString;
}
#endregion GUI
#region Utility methods
/// <summary>
/// Get a "page" of entities from a specified list
/// </summary>
/// <param name="aList">List of entities</param>
/// <param name="aPage">Page number (Starting from 0)</param>
/// <param name="aPageSize">Page size</param>
/// <returns>List of entities</returns>
private List<T> GetPage<T>(IList<T> aList, int aPage, int aPageSize) =>
aList.Skip(aPage * aPageSize).Take(aPageSize).ToList();
/// <summary>
/// Add a button to the tab menu
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aParent">Name of the parent object</param>
/// <param name="aCaption">Text to show</param>
/// <param name="aCommand">Button to execute</param>
/// <param name="aPos">Bounds of the button</param>
/// <param name="aIndActive">To indicate whether or not the button is active</param>
private void AddTabMenuBtn(ref Cui aUIObj, string aParent, string aCaption, string aCommand, int aPos, bool aIndActive)
{
Vector2 dimensions = new Vector2(0.096f, 0.75f);
Vector2 offset = new Vector2(0.005f, 0.1f);
CuiColor btnColor = (aIndActive ? CuiDefaultColors.ButtonInactive : CuiDefaultColors.Button);
CuiPoint lbAnchor = new CuiPoint(((dimensions.x + offset.x) * aPos) + offset.x, offset.y);
CuiPoint rtAnchor = new CuiPoint(lbAnchor.X + dimensions.x, offset.y + dimensions.y);
aUIObj.AddButton(aParent, lbAnchor, rtAnchor, btnColor, CuiDefaultColors.TextAlt, aCaption, (aIndActive ? string.Empty : aCommand));
}
/// <summary>
/// Add a set of user buttons to the parent object
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aParent">Name of the parent object</param>
/// <param name="aUserList">List of entities</param>
/// <param name="aCommandFmt">Base format of the command to execute (Will be completed with the user ID</param>
/// <param name="aPage">User list page</param>
private void AddPlayerButtons<T>(ref Cui aUIObj, string aParent, ref List<T> aUserList, string aCommandFmt, int aPage)
{
List<T> userRange = GetPage(aUserList, aPage, CMaxPlayerButtons);
Vector2 dimensions = new Vector2(0.194f, 0.06f);
Vector2 offset = new Vector2(0.005f, 0.01f);
int col = -1;
int row = 0;
float margin = 0.09f;
List<string> addedNames = new List<string>();
foreach (T user in userRange) {
if (++col >= CMaxPlayerCols) {
row++;
col = 0;
}
float calcTop = (1f - margin) - (((dimensions.y + offset.y) * row) + offset.y);
float calcLeft = ((dimensions.x + offset.x) * col) + offset.x;
CuiPoint lbAnchor = new CuiPoint(calcLeft, calcTop - dimensions.y);
CuiPoint rtAnchor = new CuiPoint(calcLeft + dimensions.x, calcTop);
string btnText;
string btnCommand;
int suffix = 0;
if (typeof(T) == typeof(BasePlayer)) {
BasePlayer player = (user as BasePlayer);
btnText = player.displayName;
btnCommand = string.Format(aCommandFmt, player.UserIDString);
while (addedNames.FindIndex(item => string.Equals(btnText, item, StringComparison.OrdinalIgnoreCase)) >= 0)
btnText = $"{player.displayName} {++suffix}";
aUIObj.AddButton(aParent, lbAnchor, rtAnchor, CuiDefaultColors.Button, CuiDefaultColors.TextAlt, btnText, btnCommand, string.Empty, string.Empty, 16);
} else {
ServerUsers.User player = (user as ServerUsers.User);
string btnTextTemp = player.username;
btnCommand = string.Format(aCommandFmt, player.steamid);
if (string.IsNullOrEmpty(btnTextTemp) || CUnknownNameList.Contains(btnTextTemp.ToLower()))
btnTextTemp = player.steamid.ToString();
btnText = btnTextTemp;
while (addedNames.FindIndex(item => string.Equals(btnText, item, StringComparison.OrdinalIgnoreCase)) >= 0)
btnText = $"{btnTextTemp} {++suffix}";
aUIObj.AddButton(aParent, lbAnchor, rtAnchor, CuiDefaultColors.Button, CuiDefaultColors.TextAlt, btnText, btnCommand, string.Empty, string.Empty, 16);
}
addedNames.Add(btnText);
}
LogDebug("Added the player buttons to the container");
}
/// <summary>
/// Get translated message for the specified key
/// </summary>
/// <param name="aKey">Message key</param>
/// <param name="aPlayerId">Player ID</param>
/// <param name="aArgs">Optional args</param>
/// <returns></returns>
private string GetMessage(string aKey, string aPlayerId, params object[] aArgs) =>
string.Format(lang.GetMessage(aKey, this, aPlayerId), aArgs);
/// <summary>
/// Log an error message to the logfile
/// </summary>
/// <param name="aMessage"></param>
private void LogError(string aMessage) =>
LogToFile(string.Empty, $"[{DateTime.Now.ToString("hh:mm:ss")}] ERROR > {aMessage}", this);
/// <summary>
/// Log an informational message to the logfile
/// </summary>
/// <param name="aMessage"></param>
private void LogInfo(string aMessage) =>
LogToFile(string.Empty, $"[{DateTime.Now.ToString("hh:mm:ss")}] INFO > {aMessage}", this);
/// <summary>
/// Log a debugging message to the logfile
/// </summary>
/// <param name="aMessage"></param>
private void LogDebug(string aMessage)
{
if (CDebugEnabled)
LogToFile(string.Empty, $"[{DateTime.Now.ToString("hh:mm:ss")}] DEBUG > {aMessage}", this);
}
/// <summary>
/// Send a message to a specific player
/// </summary>
/// <param name="aPlayer">The player to send the message to</param>
/// <param name="aMessage">The message to send</param>
private void SendMessage(ref BasePlayer aPlayer, string aMessage) =>
rust.SendChatMessage(aPlayer, string.Empty, aMessage);
/// <summary>
/// Verify if a user has the specified permission
/// </summary>
/// <param name="aPlayer">The player</param>
/// <param name="aPermission">Pass <see cref="string.Empty"/> to only verify <see cref="CPermUiShow"/></param>
/// <param name="aIndReport">Indicates that issues should be reported</param>
/// <returns></returns>
private bool VerifyPermission(ref BasePlayer aPlayer, string aPermission, bool aIndReport = false)
{
bool result = permission.UserHasPermission(aPlayer.UserIDString, CPermUiShow);
aPermission = aPermission ?? string.Empty; // We need to get rid of possible null values
if (FConfigData.UsePermSystem && result && aPermission.Length > 0)
result = permission.UserHasPermission(aPlayer.UserIDString, aPermission);
if (aIndReport && !result) {
SendMessage(ref aPlayer, GetMessage("Permission Error Text", aPlayer.UserIDString));
LogError(GetMessage("Permission Error Log Text", aPlayer.UserIDString, aPlayer.displayName, aPermission));
}
return result;
}
/// <summary>
/// Verify if a user has the specified permission
/// </summary>
/// <param name="aPlayerId">The player's ID</param>
/// <param name="aPermission">Pass <see cref="string.Empty"/> to only verify <see cref="CPermUiShow"/></param>
/// <returns></returns>
private bool VerifyPermission(string aPlayerId, string aPermission)
{
BasePlayer player = BasePlayer.Find(aPlayerId);
return VerifyPermission(ref player, aPermission);
}
/// <summary>
/// Retrieve server users
/// </summary>
/// <param name="aIndFiltered">Indicates if the output should be filtered</param>
/// <param name="aUserId">User ID for retrieving filter text</param>
/// <param name="aIndOffline">Retrieve the list of sleepers (offline players)</param>
/// <returns></returns>
private List<BasePlayer> GetServerUserList(bool aIndFiltered, string aUserId, bool aIndOffline = false)
{
List<BasePlayer> result = new List<BasePlayer>();
ulong userId = ulong.Parse(aUserId);
if (aIndOffline) {
Player.Sleepers.ForEach(user => {
ServerUsers.User servUser = ServerUsers.Get(user.userID);
if (servUser == null || servUser?.group != ServerUsers.UserGroup.Banned)
result.Add(user);
});
} else {
Player.Players.ForEach(user => {
ServerUsers.User servUser = ServerUsers.Get(user.userID);
if (servUser == null || servUser?.group != ServerUsers.UserGroup.Banned)
result.Add(user);
});
}
if (aIndFiltered && FUserBtnPageSearchInputText.ContainsKey(userId))
result = result.Where(x => x.displayName.IndexOf(FUserBtnPageSearchInputText[userId], StringComparison.OrdinalIgnoreCase) >= 0 ||
x.UserIDString.IndexOf(FUserBtnPageSearchInputText[userId], StringComparison.OrdinalIgnoreCase) >= 0).ToList();
LogDebug("Retrieved the server user list");
result.Sort((a, b) => {
int diff = string.Compare(a.displayName, b.displayName);
if (diff == 0)
diff = a.userID.CompareTo(b.userID);
return diff;
});
return result;
}
/// <summary>
/// Retrieve server users
/// </summary>
/// <param name="aIndFiltered">Indicates if the output should be filtered</param>
/// <param name="aUserId">User ID for retrieving filter text</param>
/// <returns></returns>
private List<ServerUsers.User> GetBannedUserList(bool aIndFiltered, string aUserId)
{
List<ServerUsers.User> result = ServerUsers.GetAll(ServerUsers.UserGroup.Banned).ToList();
ulong userId = ulong.Parse(aUserId);
if (aIndFiltered && FUserBtnPageSearchInputText.ContainsKey(userId))
result = result.Where(x => x.username.IndexOf(FUserBtnPageSearchInputText[userId], StringComparison.OrdinalIgnoreCase) >= 0 ||
x.steamid.ToString().IndexOf(FUserBtnPageSearchInputText[userId], StringComparison.OrdinalIgnoreCase) >= 0).ToList();
LogDebug("Retrieved the banned user list");
result.Sort((a, b) => {
int diff = string.Compare(a.username, b.username);
if (diff == 0)
diff = a.steamid.CompareTo(b.steamid);
return diff;
});
return result;
}
/// <summary>
/// Retrieve the target player ID from the arguments and report success
/// </summary>
/// <param name="aArg">Argument object</param>
/// <param name="aTarget">Player ID</param>
/// <returns></returns>
private bool GetTargetFromArg(ref ConsoleSystem.Arg aArg, out ulong aTarget)
{
aTarget = 0;
if (!aArg.HasArgs() || !ulong.TryParse(aArg.Args[0], out aTarget))
return false;
return true;
}
/// <summary>
/// Retrieve the target player ID and amount from the arguments and report success
/// </summary>
/// <param name="aArg">Argument object</param>
/// <param name="aTarget">Player ID</param>
/// <param name="aAmount">Amount</param>
/// <returns></returns>
private bool GetTargetAmountFromArg(ref ConsoleSystem.Arg aArg, out ulong aTarget, out float aAmount)
{
aTarget = 0;
aAmount = 0;
if (!aArg.HasArgs(2) || !ulong.TryParse(aArg.Args[0], out aTarget) || !float.TryParse(aArg.Args[1], out aAmount))
return false;
return true;
}
/// <summary>
/// Check if the player has the VoiceMuted flag set
/// </summary>
/// <param name="aPlayer">The player</param>
/// <returns></returns>
private bool GetIsVoiceMuted(ref BasePlayer aPlayer) =>
aPlayer.HasPlayerFlag(BasePlayer.PlayerFlags.VoiceMuted);
/// <summary>
/// Check if the player has the ChatMute flag set
/// </summary>
/// <param name="aPlayer">The player</param>
/// <returns></returns>
private bool GetIsChatMuted(ref BasePlayer aPlayer)
{
bool isServerMuted = aPlayer.HasPlayerFlag(BasePlayer.PlayerFlags.ChatMute);
if (BetterChatMute != null) {
return isServerMuted || (bool)BetterChatMute.Call("API_IsMuted", aPlayer.IPlayer);
} else {
return isServerMuted;
}
}
/// <summary>
/// Check if the player has the freeze.frozen permission
/// </summary>
/// <param name="aPlayerId">The player's ID</param>
/// <returns></returns>
private bool GetIsFrozen(ulong aPlayerId) =>
permission.UserHasPermission(aPlayerId.ToString(), CPermFreezeFrozen);
/// <summary>
/// Send either a kick or a ban message to Discord via the DiscordMessages plugin
/// </summary>
/// <param name="aAdminName">The name of the admin</param>
/// <param name="aAdminId">The ID of the admin</param>
/// <param name="aTargetName">The name of the target player</param>
/// <param name="aTargetId">The ID of the target player</param>
/// <param name="aReason">The reason message</param>
/// <param name="aIndIsBan">If this is true a ban message is sent, else a kick message is sent</param>
private void SendDiscordKickBanMessage(string aAdminName, string aAdminId, string aTargetName, string aTargetId, string aReason, bool aIndIsBan)
{
if (DiscordMessages != null) {
if (CUnknownNameList.Contains(aTargetName.ToLower()))
aTargetName = aTargetId;
object fields = new[] {
new {
name = "Player",
value = $"[{aTargetName}](https://steamcommunity.com/profiles/{aTargetId})",
inline = true
},
new {
name = aIndIsBan ? "Banned by" : "Kicked by",
value = $"[{aAdminName}](https://steamcommunity.com/profiles/{aAdminId})",
inline = true
},
new {
name = "Reason",
value = aReason,
inline = false
}
};
DiscordMessages.Call(
"API_SendFancyMessage",
aIndIsBan ? FConfigData.BanMsgWebhookUrl : FConfigData.KickMsgWebhookUrl,
aIndIsBan ? "Player Ban" : "Player Kick",
3329330,
JsonConvert.SerializeObject(fields)
);
}
}
#endregion Utility methods
#region Upgrade methods
/// <summary>
/// Upgrade the config to 1.3.10 if needed
/// </summary>
/// <returns></returns>
private bool UpgradeTo1310()
{
bool result = false;
Config.Load();
if (Config["Use Permission System"] == null) {
FConfigData.UsePermSystem = true;
result = true;
}
// Remove legacy config items
if (Config["Enable kick action"] != null || Config["Enable ban action"] != null || Config["Enable unban action"] != null ||
Config["Enable kill action"] != null || Config["Enable inventory clear action"] != null || Config["Enable blueprint reset action"] != null ||
Config["Enable metabolism reset action"] != null || Config["Enable hurt action"] != null || Config["Enable heal action"] != null ||
Config["Enable voice mute action"] != null || Config["Enable chat mute action"] != null || Config["Enable perms action"] != null ||
Config["Enable freeze action"] != null)
result = true;
Config.Clear();
if (result)
Config.WriteObject(FConfigData);
return result;
}
/// <summary>
/// Upgrade the config to 1.3.13 if needed
/// </summary>
/// <returns></returns>
private bool UpgradeTo1313()
{
bool result = false;
Config.Load();
if (Config["Discord Webhook url for ban messages"] == null) {
FConfigData.BanMsgWebhookUrl = string.Empty;
result = true;
}
if (Config["Discord Webhook url for kick messages"] == null) {
FConfigData.KickMsgWebhookUrl = string.Empty;
result = true;
}
Config.Clear();
if (result)
Config.WriteObject(FConfigData);
return result;
}
#endregion
#region GUI build methods
/// <summary>
/// Build the tab nav-bar
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aPageType">The active page type</param>
private void BuildTabMenu(ref Cui aUIObj, UiPage aPageType)
{
string uiUserId = aUIObj.GetPlayerId();
// Add the panels and title label
string headerPanel = aUIObj.AddPanel(aUIObj.MainPanelName, CMainMenuHeaderContainerLbAnchor, CMainMenuHeaderContainerRtAnchor, false,
CuiDefaultColors.None);
string tabBtnPanel = aUIObj.AddPanel(aUIObj.MainPanelName, CMainMenuTabBtnContainerLbAnchor, CMainMenuTabBtnContainerRtAnchor, false,
CuiDefaultColors.Background);
aUIObj.AddLabel(headerPanel, CMainMenuHeaderLblLbAnchor, CMainMenuHeaderLblRtAnchor, CuiDefaultColors.TextTitle,
"Player Administration by ThibmoRozier", string.Empty, 22, TextAnchor.MiddleCenter);
aUIObj.AddButton(headerPanel, CMainMenuCloseBtnLbAnchor, CMainMenuCloseBtnRtAnchor, CuiDefaultColors.ButtonDecline, CuiDefaultColors.TextAlt, "X",
CCloseUiCmd, string.Empty, string.Empty, 22);
// Add the tab menu buttons
AddTabMenuBtn(ref aUIObj, tabBtnPanel, GetMessage("Main Tab Text", uiUserId), $"{CSwitchUiCmd} {CCmdArgMain}", 0, (aPageType == UiPage.Main ? true : false));
AddTabMenuBtn(ref aUIObj, tabBtnPanel, GetMessage("Online Player Tab Text", uiUserId), $"{CSwitchUiCmd} {CCmdArgPlayersOnline} 0", 1,
(aPageType == UiPage.PlayersOnline ? true : false));
AddTabMenuBtn(ref aUIObj, tabBtnPanel, GetMessage("Offline Player Tab Text", uiUserId), $"{CSwitchUiCmd} {CCmdArgPlayersOffline} 0", 2,
(aPageType == UiPage.PlayersOffline ? true : false));
AddTabMenuBtn(ref aUIObj, tabBtnPanel, GetMessage("Banned Player Tab Text", uiUserId), $"{CSwitchUiCmd} {CCmdArgPlayersBanned} 0", 3,
(aPageType == UiPage.PlayersBanned ? true : false));
LogDebug("Built the tab menu");
}
/// <summary>
/// Build the main-menu
/// </summary>
/// <param name="aUIObj">Cui object</param>
private void BuildMainPage(ref Cui aUIObj)
{
string uiUserId = aUIObj.GetPlayerId();
// Add the panels and title
string panel = aUIObj.AddPanel(aUIObj.MainPanelName, CMainPanelLbAnchor, CMainPanelRtAnchor, false, CuiDefaultColors.Background);
aUIObj.AddLabel(panel, CMainLblTitleLbAnchor, CMainLblTitleRtAnchor, CuiDefaultColors.TextAlt, "Main", string.Empty, 18, TextAnchor.MiddleLeft);
// Add the ban by ID group
aUIObj.AddLabel(panel, CMainPageLblBanByIdTitleLbAnchor, CMainPageLblBanByIdTitleRtAnchor, CuiDefaultColors.TextTitle,
GetMessage("Ban By ID Title Text", uiUserId), string.Empty, 16, TextAnchor.MiddleLeft);
aUIObj.AddLabel(panel, CMainPageLblBanByIdLbAnchor, CMainPageLblBanByIdRtAnchor, CuiDefaultColors.TextAlt, GetMessage("Ban By ID Label Text", uiUserId),
string.Empty, 14, TextAnchor.MiddleLeft);
string panelBanByIdGroup = aUIObj.AddPanel(panel, CMainPagePanelBanByIdLbAnchor, CMainPagePanelBanByIdRtAnchor, false, CuiDefaultColors.BackgroundDark);
if (VerifyPermission(uiUserId, CPermBan)) {
aUIObj.AddInputField(panelBanByIdGroup, CMainPageEdtBanByIdLbAnchor, CMainPageEdtBanByIdRtAnchor, CuiDefaultColors.TextAlt, string.Empty, 24,
CMainPageBanIdInputTextCmd);
aUIObj.AddButton(panel, CMainPageBtnBanByIdLbAnchor, CMainPageBtnBanByIdRtAnchor, CuiDefaultColors.ButtonDanger, CuiDefaultColors.TextAlt, "Ban",
CMainPageBanByIdCmd);
} else {
aUIObj.AddButton(panel, CMainPageBtnBanByIdLbAnchor, CMainPageBtnBanByIdRtAnchor, CuiDefaultColors.ButtonInactive, CuiDefaultColors.TextAlt, "Ban");
}
LogDebug("Built the main page");
}
/// <summary>
/// Build a page of user buttons
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aPageType">The active page type</param>
/// <param name="aPage">User list page</param>
/// <param name="aIndFiltered">Indicates if the output should be filtered</param>
private void BuildUserBtnPage(ref Cui aUIObj, UiPage aPageType, int aPage, bool aIndFiltered)
{
string uiUserId = aUIObj.GetPlayerId();
ulong uiUserIdInt = ulong.Parse(uiUserId);
string pageTitleLabel = GetMessage("User Button Page Title Text", aUIObj.GetPlayerId());
string npBtnCommandFmt;
int userCount;
string panel = aUIObj.AddPanel(aUIObj.MainPanelName, CMainPanelLbAnchor, CMainPanelRtAnchor, false, CuiDefaultColors.Background);
aUIObj.AddLabel(panel, CUserBtnPageLblTitleLbAnchor, CUserBtnPageLblTitleRtAnchor, CuiDefaultColors.TextAlt, pageTitleLabel, string.Empty, 18,
TextAnchor.MiddleLeft);
// Add search elements
aUIObj.AddLabel(panel, CUserBtnPageLblSearchLbAnchor, CUserBtnPageLblSearchRtAnchor, CuiDefaultColors.TextAlt, GetMessage("Search Label Text", uiUserId),
string.Empty, 16, TextAnchor.MiddleLeft);
string panelSearchGroup = aUIObj.AddPanel(panel, CUserBtnPagePanelSearchInputLbAnchor, CUserBtnPagePanelSearchInputRtAnchor,
// ... [Code preview truncated due to length. Full source in attached file] ...