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 --
* Max-line-width ------- 160
* Single-line comments - // Single-line comment
* Multi-line comments -- Just like this comment block!
*/
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using Oxide.Game.Rust.Libraries;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using UnityEngine;
using RustLib = Oxide.Game.Rust.Libraries.Rust;
namespace Oxide.Plugins {
[Info("PlayerAdministration", "ThibmoRozier", "1.5.26")]
[Description("Allows server admins to moderate users using a GUI from within the game.")]
public class PlayerAdministration : CovalencePlugin {
#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;
[PluginReference]
private Plugin Backpacks;
[PluginReference]
private Plugin InventoryViewer;
[PluginReference]
private Plugin Vanish;
#pragma warning restore IDE0044, CS0649
#endregion Plugin References
#region Library Imports
private readonly RustLib rust = Interface.Oxide.GetLibrary<RustLib>();
private readonly Player Player = Interface.Oxide.GetLibrary<Player>();
#endregion Library Imports
#region GUI
#region Types
/// <summary>
/// UI Color object
/// </summary>
private class CuiColor {
public byte R {
get;
set;
}
public byte G {
get;
set;
}
public byte B {
get;
set;
}
public float A {
get;
set;
}
public CuiColor(byte aRed = 255, byte aGreen = 255, byte aBlue = 255, 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}";
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);
}
/// <summary>
/// Element position object
/// </summary>
private class CuiPoint {
public float X {
get;
set;
}
public float Y {
get;
set;
}
public CuiPoint(float aX = 0f, float aY = 0f) {
X = aX;
Y = aY;
}
public override string ToString() => $"{X} {Y}";
public static readonly CuiPoint Zero = new CuiPoint();
}
/// <summary>
/// UI pages to make the switching more humanly readable
/// </summary>
private enum UiPage {
Main = 0, PlayersOnline, PlayersOffline, PlayersBanned, PlayerPage, PlayerPageBanned
}
#endregion Types
#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";
private readonly CustomCuiElementContainer FContainer = new CustomCuiElementContainer();
private readonly BasePlayer FPlayer;
public readonly ulong PlayerId;
public readonly string PlayerIdString;
public readonly float StartTime = Time.realtimeSinceStartup;
/// <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;
PlayerId = aPlayer.userID;
PlayerIdString = aPlayer.UserIDString;
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, CuiPoint.Zero, CuiPoint.Zero, 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, CuiPoint.Zero, CuiPoint.Zero, 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, CuiPoint.Zero, CuiPoint.Zero, 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, CuiPoint.Zero, CuiPoint.Zero, 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>
/// Add a new element
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aElement">The object itself</param>
/// <param name="aName">The object's name</param>
/// <returns>New object name</returns>
public string AddElement(string aParent, CuiPanel aElement, string aName = "") => FContainer.Add(aElement, aParent, string.IsNullOrEmpty(aName) ? null : aName);
/// <summary>
/// Add a new element
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aElement">The object itself</param>
/// <param name="aName">The object's name</param>
/// <returns>New object name</returns>
public string AddElement(string aParent, CuiLabel aElement, string aName = "") => FContainer.Add(aElement, aParent, string.IsNullOrEmpty(aName) ? null : aName);
/// <summary>
/// Add a new element
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aElement">The object itself</param>
/// <param name="aName">The object's name</param>
/// <returns>New object name</returns>
public string AddElement(string aParent, CuiButton aElement, string aName = "") => FContainer.Add(aElement, aParent, string.IsNullOrEmpty(aName) ? null : aName);
/// <summary>
/// Add a new element
/// </summary>
/// <param name="aParent">The parent object name</param>
/// <param name="aElement">The object itself</param>
/// <param name="aName">The object's name</param>
/// <returns>New object name</returns>
public string AddElement(string aParent, CuiInputField aElement, string aName = "") => FContainer.Add(aElement, aParent, string.IsNullOrEmpty(aName) ? null : aName);
/// <summary>
/// Draw the UI to the player's client
/// </summary>
/// <returns></returns>
public bool Draw() => CuiHelper.AddUi(FPlayer, CuiHelper.ToJson(FContainer));
public string JSON {
get {
return CuiHelper.ToJson(FContainer, true);
}
}
}
#endregion GUI
#region Utility methods
/// <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 ? CuiColor.ButtonInactive : CuiColor.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, CuiColor.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(ref Cui aUIObj, string aParent, IEnumerable<KeyValuePair<ulong, string>> aUserList, string aCommandFmt, int aPage) {
IEnumerable<KeyValuePair<ulong, string>> userRange = aUserList.Skip(aPage * CMaxPlayerButtons).Take(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 (KeyValuePair<ulong, string> 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);
int suffix = 0;
string btnTextTemp = EscapeString(user.Value ?? "");
string btnCommand = string.Format(aCommandFmt, user.Key);
if (string.IsNullOrEmpty(btnTextTemp) || CUnknownNameList.Contains(btnTextTemp.ToLower())) btnTextTemp = user.Key.ToString();
string btnText = btnTextTemp;
while (addedNames.FindIndex(item => btnText.Equals(item, StringComparison.OrdinalIgnoreCase)) >= 0) {
btnText = $"{btnTextTemp} {++suffix}";
}
aUIObj.AddButton(aParent, lbAnchor, rtAnchor, CuiColor.Button, CuiColor.TextAlt, btnText, btnCommand, string.Empty, string.Empty, 16);
addedNames.Add(btnText);
}
LogDebug("Added the player buttons to the container");
}
/// <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>
/// 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) {
rust.SendChatMessage(aPlayer, string.Empty, lang.GetMessage("Permission Error Text", this, aPlayer.UserIDString));
LogError(string.Format(lang.GetMessage("Permission Error Log Text", this, 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 IEnumerable<BasePlayer> GetServerUserList(bool aIndFiltered, string aUserId, bool aIndOffline = false) {
IEnumerable<BasePlayer> result;
if (aIndOffline) {
result = Player.Sleepers.Where(X => !X.IsNpc && !Player.IsBanned(X.userID));
}
else {
result = Player.Players.Where(X => !X.IsNpc && !Player.IsBanned(X.userID));
}
if (aIndFiltered && FUserBtnPageSearchInputText.ContainsKey(aUserId)) result = result.Where(x => x.UserIDString.IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0 || x.displayName.IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0 );
LogDebug("Retrieved the server user list");
return result.OrderBy(x => x.displayName).ThenBy(x => x.userID);
}
/// <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 IEnumerable<ServerUsers.User> GetBannedUserList(bool aIndFiltered, string aUserId) {
IEnumerable<ServerUsers.User> result = ServerUsers.GetAll(ServerUsers.UserGroup.Banned);
if (aIndFiltered && FUserBtnPageSearchInputText.ContainsKey(aUserId)) result = result.Where(x => x.username.IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0 || x.steamid.ToString().IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0 );
LogDebug("Retrieved the banned user list");
return result.OrderBy(x => x.username).ThenBy(x => x.steamid);
}
/// <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(string[] aArgs, out ulong aTarget) {
aTarget = 0;
return aArgs.Count() > 0 && ulong.TryParse(aArgs[0], out aTarget);
}
/// <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(string[] aArgs, out ulong aTarget, out float aAmount) {
aTarget = 0;
aAmount = 0;
return aArgs.Count() >= 2 && ulong.TryParse(aArgs[0], out aTarget) && float.TryParse(aArgs[1], out aAmount);
}
/// <summary>
/// Check if the player has the ChatMute flag set
/// </summary>
/// <param name="aPlayer">The player</param>
/// <returns></returns>
private bool GetIsMuted(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) );
}
}
/// <summary>
/// Custom internal version of Assert
/// </summary>
/// <param name="aEval"></param>
/// <param name="aTrueMsg"></param>
/// <param name="aFalseMsg"></param>
/// <returns></returns>
private string InternalAssert(bool aEval, string aTrueMsg = "True", string aFalseMsg = "False") => aEval ? aTrueMsg : aFalseMsg;
/// <summary>
/// Transform a string array into a printable string./// </summary>
/// <param name="aObj"></param>
/// <returns></returns>
private string StringArrToString(ref string[] aObj) => $"[ {string.Join(", ", aObj)} ]";
/// <summary>
/// Transform a dictionary of strings into a printable string./// </summary>
/// <param name="aObj"></param>
/// <returns></returns>
private string StringDictToString(ref Dictionary<string, string> aObj) {
StringBuilder result = new StringBuilder("{\n");
foreach (KeyValuePair<string, string> item in aObj) {
result.Append($"'{item.Key}': '{item.Value}'\n");
}
result.Append("}");
return result.ToString();
}
/// <summary>
/// Escape strings to make them usable in the UI./// </summary>
/// <param name="aStr"></param>
/// <returns></returns>
private string EscapeString(string aStr) => aStr.Replace("\0", "").Replace("\a", "").Replace("\b", "").Replace("\f", "").Replace("\r", "").Replace("\n", " ").Replace("\t", " ").Replace("\v", "").Replace("\"", "ˮ").Replace("/", "∕").Replace("\\", "∖");
/// <summary>
/// Teleport a player to the given potition.Kindly taken from NTeleportation
/// </summary>
/// <param name="aPlayer">The player to teleport</param>
/// <param name="aTarget">The target position</param>
private void Teleport(BasePlayer aPlayer, Vector3 aTarget) {
if (!aPlayer.IsValid() || Vector3.Distance(aTarget, default(Vector3)) < 5f) return;
try {
aPlayer.EnsureDismounted();
if (aPlayer.HasParent()) {
aPlayer.SetParent(null, true, true);
}
if (aPlayer.IsConnected) {
aPlayer.EndLooting();
if (!aPlayer.IsSleeping()) {
Interface.CallHook("OnPlayerSleep", aPlayer);
aPlayer.SetPlayerFlag(BasePlayer.PlayerFlags.Sleeping, true);
aPlayer.sleepStartTime = Time.time;
BasePlayer.sleepingPlayerList.Add(aPlayer);
BasePlayer.bots.Remove(aPlayer);
aPlayer.CancelInvoke("InventoryUpdate");
aPlayer.CancelInvoke("TeamUpdate");
}
}
aPlayer.RemoveFromTriggers();
// Recommended to use vanilla method due to an issue with triggers
aPlayer.EnableServerFall(true);
// Redundant, in OnEntityTakeDamage hook
aPlayer.Teleport(aTarget);
if (aPlayer.IsConnected && !Network.Net.sv.visibility.IsInside(aPlayer.net.group, aTarget)) {
aPlayer.SetPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot, true);
aPlayer.ClientRPCPlayer(null, aPlayer, "StartLoading");
aPlayer.SendEntityUpdate();
if (!(Vanish?.Call<bool>("IsInvisible", aPlayer) ?? false)) // fix for becoming networked briefly with vanish while teleporting
{
aPlayer.UpdateNetworkGroup();
// Building fix
aPlayer.SendNetworkUpdateImmediate(false);
}
}
}
finally {
aPlayer.EnableServerFall(false);
aPlayer.ForceUpdateTriggers();
// Exploit fix for looting sleepers in safe zones
}
}
#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 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;
}
/// <summary>
/// Upgrade the config to 1.5.6 if needed
/// </summary>
/// <returns></returns>
private bool UpgradeTo156() {
bool result = false;
Dictionary<string, string> oldPerms = new Dictionary<string, string>() {
{
"playeradministration.show", CPermUiShow
}
, {
"playeradministration.kick", CPermKick
}
, {
"playeradministration.ban", CPermBan
}
, {
"playeradministration.kill", CPermKill
}
, {
"playeradministration.perms", CPermPerms
}
, {
"playeradministration.voicemute", CPermMute
}
, {
"playeradministration.chatmute", CPermMute
}
, {
"playeradministration.freeze", CPermFreeze
}
, {
"playeradministration.clearinventory", CPermClearInventory
}
, {
"playeradministration.resetblueprint", CPermResetBP
}
, {
"playeradministration.resetmetabolism", CPermResetMetabolism
}
, {
"playeradministration.recovermetabolism", CPermRecoverMetabolism
}
, {
"playeradministration.hurt", CPermHurt
}
, {
"playeradministration.heal", CPermHeal
}
, {
"playeradministration.teleport", CPermTeleport
}
, {
"playeradministration.spectate", CPermSpectate
}
}
;
LogDebug($"Old Perms: {StringDictToString(ref oldPerms)}");
foreach (KeyValuePair<string, string> item in oldPerms) {
string[] groups = permission.GetPermissionGroups(item.Key);
LogDebug($"Groups: {StringArrToString(ref groups)}");
string[] users = permission.GetPermissionUsers(item.Key);
LogDebug($"Users: {StringArrToString(ref users)}");
if (groups.Count() <= 0 && users.Count() <= 0) {
LogDebug("Counts are zero");
continue;
}
result = true;
foreach (string group in groups) {
permission.RevokeGroupPermission(group, item.Key);
permission.GrantGroupPermission(group, item.Value, this);
LogInfo($"Fixed group permission: {group} (OLD) {item.Key} -> (NEW) {item.Value}");
}
foreach (string user in users) {
string uid = user.Substring(0, user.IndexOf('('));
permission.RevokeUserPermission(uid, item.Key);
permission.GrantUserPermission(uid, item.Value, this);
LogInfo($"Fixed user permission: {user} (OLD) {item.Key} -> (NEW) {item.Value}");
}
}
permission.SaveData();
return result;
}
/// <summary>
/// Upgrade the config to 1.5.19 if needed
/// </summary>
/// <returns></returns>
private bool UpgradeTo1519() {
bool result = false;
Dictionary<string, string> oldPerms = new Dictionary<string, string>() {
{
"playeradministration.access.voicemute", CPermMute
}
, {
"playeradministration.access.chatmute", CPermMute
// ... [Code preview truncated due to length. Full source in attached file] ...