Error while compiling PlayerAdministration: 'BasePlayer' does not contain a definition for 'lastAdminCheatTime' and no accessible extension method 'lastAdminCheatTime' accepting a first argument of type 'BasePlayer' could be found (are you missing a using directive or an assembly reference?) | Line: 1292, Pos: 25
Error compiling after today's update
Failed compiling 'PlayerAdministration.cs':
1. 'BasePlayer' does not contain a definition for 'lastAdminCheatTime' and no accessible extension method 'lastAdminCheatTime' accepting a first argument of type 'BasePlayer' could be found (are you missing a using directive or an assembly reference?) [CS1061]
(PlayerAdministration 25 line 1292)
Merged post
Same here
Same issue for me as well:
Error while compiling PlayerAdministration: 'BasePlayer' does not contain a definition for 'lastAdminCheatTime' and no accessible extension method 'lastAdminCheatTime' accepting a first argument of type 'BasePlayer' could be found (are you missing a using directive or an assembly reference?) | Line: 1292, Pos: 25
As a quick fix you can go to line 1292 and comment out these lines. The plugin will compile and we can wait for a proper fix.
// Pre-calc last admin cheat
//if (aPlayer.lastAdminCheatTime > 0f) {
// TimeSpan lastCheatSinceStart = new TimeSpan(0, 0, (int)(Time.realtimeSinceStartup - aPlayer.lastAdminCheatTime));
// lastCheatStr = $"{DateTime.UtcNow.Subtract(lastCheatSinceStart):yyyy-MM-dd HH:mm:ss} UTC";
//} A Fix with AI assistance full code corrected below
/* --- Contributor information ---
* Please follow the following set of guidelines when working on this plugin,
* this to help others understand this file more easily.
*
* NOTE: On Authors, new entries go BELOW the existing entries. As with any other software header comment.
*
* -- Authors --
* Thimo (ThibmoRozier) <[email protected]> 2018-03-27 +
* rfc1920 <[email protected]>
* Mheetu <[email protected]>
* Pho3niX90 <[email protected]> 2020-06 +
* Gabriel Vergara Ezcurdia -AI asisted code fixes. for rust Update 2026.08.06.00
*
* -- 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.6.9")]
[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 ServerArmour;
[PluginReference]
private Plugin Godmode;
#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
{
private readonly Action<string> LogError;
/// <summary>
/// Constructor
/// </summary>
/// <param name="aLogErrorFunc">Error logging procedure</param>
/// <returns></returns>
public CustomCuiElementContainer(Action<string> aLogErrorFunc) : base() {
LogError = aLogErrorFunc;
}
public string Add(CuiInputField aInputField, string aParent = Cui.ParentHud, string aName = "") {
if (string.IsNullOrEmpty(aName))
aName = CuiHelper.GetGuid();
if (aInputField == null) {
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 Action<string> LogDebug;
private readonly Action<string> LogError;
private readonly CustomCuiElementContainer FContainer;
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>
/// <param name="aLogDebugFunc">Debug logging procedure</param>
/// <param name="aLogInfoFunc">Info logging procedure</param>
/// <param name="aLogErrorFunc">Error logging procedure</param>
/// <returns></returns>
public Cui(BasePlayer aPlayer, Action<string> aLogDebugFunc, Action<string> aLogErrorFunc) {
LogDebug = aLogDebugFunc;
LogError = aLogErrorFunc;
if (aPlayer == null) {
LogError("Cui::Cui > Parameter 'aPlayer' is null");
return;
}
FContainer = new CustomCuiElementContainer(aLogErrorFunc);
FPlayer = aPlayer;
PlayerId = aPlayer.userID;
PlayerIdString = aPlayer.UserIDString;
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) {
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();
}
}
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) {
LogError($"Cui::AddLabel > One of the required parameters is null");
return string.Empty;
}
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
) {
LogError($"Cui::AddButton > One of the required parameters is null");
return string.Empty;
}
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) {
LogError($"Cui::AddInputField > One of the required parameters is null");
return string.Empty;
}
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, ref 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.Empty);
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: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:hh:mm:ss}] INFO > {aMessage}", this);
/// <summary>
/// Log a debugging message to the logfile
/// </summary>
/// <param name="aMessage"></param>
private void LogDebug(string aMessage) {
#pragma warning disable CS0162
if (CDebugEnabled)
LogToFile(string.Empty, $"[{DateTime.Now:hh:mm:ss}] DEBUG > {aMessage}", this);
#pragma warning restore CS0162
}
/// <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
/// Note that base-type comparison is WAY faster then String comparison
/// </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(ulong aPlayerId, string aPermission) {
BasePlayer player = BasePlayer.FindByID(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<KeyValuePair<ulong, string>> GetServerUserList(bool aIndFiltered, ulong aUserId, bool aIndOffline = false) {
IEnumerable<KeyValuePair<ulong, string>> result = (aIndOffline ? FOfflineUserList : FOnlineUserList).AsEnumerable();
if (aIndFiltered && FUserBtnPageSearchInputText.ContainsKey(aUserId))
result = result.Where(x =>
x.Value.IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0 ||
x.Key.ToString().IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0
);
LogDebug("Retrieved the server user list");
return result.OrderBy(x => x.Value).ThenBy(x => x.Key);
}
/// <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<KeyValuePair<ulong, string>> GetBannedUserList(bool aIndFiltered, ulong aUserId) {
IEnumerable<KeyValuePair<ulong, string>> result = ServerUsers.GetAll(ServerUsers.UserGroup.Banned).Select(
x => new KeyValuePair<ulong, string>(x.steamid, x.username)
);
if (aIndFiltered && FUserBtnPageSearchInputText.ContainsKey(aUserId))
result = result.Where(x =>
x.Value.IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0 ||
x.Key.ToString().IndexOf(FUserBtnPageSearchInputText[aUserId], StringComparison.OrdinalIgnoreCase) >= 0
).ToList();
LogDebug("Retrieved the banned user list");
return result.OrderBy(x => x.Value).ThenBy(x => x.Key);
}
/// <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.Length > 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.Length >= 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, ulong aAdminId, string aTargetName, ulong aTargetId, string aReason, bool aIndIsBan) {
if (DiscordMessages != null) {
if (CUnknownNameList.Contains(aTargetName.ToLower()))
aTargetName = aTargetId.ToString();
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>
/// 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", string.Empty)
.Replace("\a", string.Empty)
.Replace("\b", string.Empty)
.Replace("\f", string.Empty)
.Replace("\r", string.Empty)
.Replace('\n', ' ')
.Replace('\t', ' ')
.Replace("\v", string.Empty)
.Replace('"', '\u02EE')
.Replace('/', '\u2215')
.Replace('\\', '\u2216');
/// <summary>
/// Gets the reason from the input box on the players screen.
/// </summary>
/// <param name="playerId">The player ID that entered the reason.</param>
/// <param name="targetId">The target ID the reason is for.</param>
/// <returns></returns>
private string GetReason(ulong playerId, string targetId = "", bool indIsKick = false) {
string reasonMsg;
if (FUserPageReasonInputText.ContainsKey(playerId)) {
reasonMsg = FUserPageReasonInputText[playerId].Trim();
if (string.IsNullOrEmpty(reasonMsg))
reasonMsg = lang.GetMessage(indIsKick ? "Kick Reason Message Text" : "Ban Reason Message Text", this, targetId);
} else {
reasonMsg = lang.GetMessage(indIsKick ? "Kick Reason Message Text" : "Ban Reason Message Text", this, targetId);
}
return reasonMsg;
}
private void BroadcastKickBan(string aAdminId, ulong aTargetId, string aReason, bool aIndIsKick) {
string broadcastMessage = aIndIsKick
? lang.GetMessage("Kick Broadcast Message Format", this, aAdminId)
: lang.GetMessage("Ban Broadcast Message Format", this, aAdminId);
string targetName = ServerUsers.Get(aTargetId)?.username;
if (string.IsNullOrEmpty(targetName) || CUnknownNameList.Contains(targetName.ToLower()))
targetName = aTargetId.ToString();
rust.BroadcastChat(string.Empty, String.Format(broadcastMessage, targetName, aReason));
}
#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.Length + users.Length <= 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 }
};
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.Length + users.Length <= 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.6.4 if needed
/// </summary>
/// <returns></returns>
private bool UpgradeTo164() {
bool result = false;
Config.Load();
if (Config["Broadcast Kicks"] == null) {
FConfigData.BroadcastKicks = true;
result = true;
}
if (Config["Broadcast Bans"] == null) {
FConfigData.BroadcastBans = true;
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) {
// Add the panels and title label
string headerPanel = aUIObj.AddElement(CMainPanelName, CTabHeaderPanel);
string tabBtnPanel = aUIObj.AddElement(CMainPanelName, CTabTabBtnPanel);
aUIObj.AddElement(headerPanel, CTabMenuHeaderLbl);
aUIObj.AddElement(headerPanel, CTabMenuCloseBtn);
// Add the tab menu buttons
AddTabMenuBtn(
ref aUIObj, tabBtnPanel, lang.GetMessage("Main Tab Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgMain}", 0,
aPageType == UiPage.Main
);
AddTabMenuBtn(
ref aUIObj, tabBtnPanel, lang.GetMessage("Online Player Tab Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgPlayersOnline} 0", 1,
aPageType == UiPage.PlayersOnline
);
AddTabMenuBtn(
ref aUIObj, tabBtnPanel, lang.GetMessage("Offline Player Tab Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgPlayersOffline} 0",
2, aPageType == UiPage.PlayersOffline
);
AddTabMenuBtn(
ref aUIObj, tabBtnPanel, lang.GetMessage("Banned Player Tab Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgPlayersBanned} 0", 3,
aPageType == UiPage.PlayersBanned
);
LogDebug("Built the tab menu");
}
/// <summary>
/// Build the main-menu
/// </summary>
/// <param name="aUIObj">Cui object</param>
private void BuildMainPage(ref Cui aUIObj) {
// Add the panels and title
string panel = aUIObj.AddElement(CMainPanelName, CMainPagePanel);
aUIObj.AddElement(panel, CMainPageTitleLbl);
// Add the ban by ID group
aUIObj.AddLabel(
panel, CMainPageLblBanByIdTitleLbAnchor, CMainPageLblBanByIdTitleRtAnchor, CuiColor.TextTitle,
lang.GetMessage("Ban By ID Title Text", this, aUIObj.PlayerIdString), string.Empty, 16, TextAnchor.MiddleLeft
);
aUIObj.AddLabel(
panel, CMainPageLblBanByIdLbAnchor, CMainPageLblBanByIdRtAnchor, CuiColor.TextAlt,
lang.GetMessage("Ban By ID Label Text", this, aUIObj.PlayerIdString), string.Empty, 14, TextAnchor.MiddleLeft
);
string panelBanByIdGroup = aUIObj.AddElement(panel, CBanByIdGroupPanel);
if (VerifyPermission(aUIObj.PlayerId, CPermBan)) {
aUIObj.AddElement(panelBanByIdGroup, CBanByIdEdt);
aUIObj.AddElement(panel, CBanByIdActiveBtn);
} else {
aUIObj.AddElement(panel, CBanByIdInactiveBtn);
}
LogDebug("Built the main page");
LogDebug($"Elapsed time (BuildMainPage): {Time.realtimeSinceStartup - aUIObj.StartTime:F8}");
}
/// <summary>
/// Build the current user buttons
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aParent">The active page type</param>
/// <param name="aPageType">The active page type</param>
/// <param name="aPage">User list page</param>
/// <param name="aBtnCommandFmt">Command format for the buttons</param>
/// <param name="aUserCount">Total user count</param>
/// <param name="aIndFiltered">Indicates if the output should be filtered</param>
private void BuildUserButtons(
ref Cui aUIObj, string aParent, UiPage aPageType, ref int aPage, out string aBtnCommandFmt, out int aUserCount, bool aIndFiltered
) {
string commandFmt = $"{CSwitchUiCmd} {CCmdArgPlayerPage} {{0}}";
IEnumerable<KeyValuePair<ulong, string>> userList = GetServerUserList(aIndFiltered, aUIObj.PlayerId, aPageType == UiPage.PlayersOffline);
aBtnCommandFmt = aPageType == UiPage.PlayersOnline
? $"{CSwitchUiCmd} {(aIndFiltered ? CCmdArgPlayersOnlineSearch : CCmdArgPlayersOnline)} {{0}}"
: $"{CSwitchUiCmd} {(aIndFiltered ? CCmdArgPlayersOfflineSearch : CCmdArgPlayersOffline)} {{0}}";
aUserCount = userList.Count();
if ((aPage != 0) && (aUserCount <= CMaxPlayerButtons))
aPage = 0; // Reset page to 0 if user count is lower or equal to max button count
AddPlayerButtons(ref aUIObj, aParent, ref userList, commandFmt, aPage);
LogDebug("Built the current page of user buttons");
}
/// <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 npBtnCommandFmt;
int userCount;
string panel = aUIObj.AddElement(CMainPanelName, CMainPagePanel);
aUIObj.AddLabel(
panel, CUserBtnPageLblTitleLbAnchor, CUserBtnPageLblTitleRtAnchor, CuiColor.TextAlt,
lang.GetMessage("User Button Page Title Text", this, aUIObj.PlayerIdString), string.Empty, 18, TextAnchor.MiddleLeft
);
// Add search elements
aUIObj.AddLabel(
panel, CUserBtnPageLblSearchLbAnchor, CUserBtnPageLblSearchRtAnchor, CuiColor.TextAlt,
lang.GetMessage("Search Label Text", this, aUIObj.PlayerIdString), string.Empty, 16, TextAnchor.MiddleLeft
);
string panelSearchGroup = aUIObj.AddElement(panel, CUserBtnPageSearchInputPanel);
aUIObj.AddInputField(
panelSearchGroup, CUserBtnPageEdtSearchInputLbAnchor, CUserBtnPageEdtSearchInputRtAnchor, CuiColor.TextAlt,
(FUserBtnPageSearchInputText.ContainsKey(aUIObj.PlayerId) ? FUserBtnPageSearchInputText[aUIObj.PlayerId] : string.Empty), 100,
CUserBtnPageSearchInputTextCmd, false, string.Empty, 16
);
switch (aPageType) {
case UiPage.PlayersOnline: {
aUIObj.AddButton(
panel, CUserBtnPageBtnSearchLbAnchor, CUserBtnPageBtnSearchRtAnchor, CuiColor.Button, CuiColor.TextAlt,
lang.GetMessage("Go Button Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgPlayersOnlineSearch} 0", string.Empty,
string.Empty, 16
);
BuildUserButtons(ref aUIObj, panel, aPageType, ref aPage, out npBtnCommandFmt, out userCount, aIndFiltered);
break;
}
case UiPage.PlayersOffline: {
aUIObj.AddButton(
panel, CUserBtnPageBtnSearchLbAnchor, CUserBtnPageBtnSearchRtAnchor, CuiColor.Button, CuiColor.TextAlt,
lang.GetMessage("Go Button Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgPlayersOfflineSearch} 0", string.Empty,
string.Empty, 16
);
BuildUserButtons(ref aUIObj, panel, aPageType, ref aPage, out npBtnCommandFmt, out userCount, aIndFiltered);
break;
}
default: {
aUIObj.AddButton(
panel, CUserBtnPageBtnSearchLbAnchor, CUserBtnPageBtnSearchRtAnchor, CuiColor.Button, CuiColor.TextAlt,
lang.GetMessage("Go Button Text", this, aUIObj.PlayerIdString), $"{CSwitchUiCmd} {CCmdArgPlayersBannedSearch} 0", string.Empty,
string.Empty, 16
);
string commandFmt = $"{CSwitchUiCmd} {CCmdArgPlayerPageBanned} {{0}}";
IEnumerable<KeyValuePair<ulong, string>> userList = GetBannedUserList(aIndFiltered, aUIObj.PlayerId);
npBtnCommandFmt = $"{CSwitchUiCmd} {(aIndFiltered ? CCmdArgPlayersBannedSearch : CCmdArgPlayersBanned)} {{0}}";
userCount = userList.Count();
if ((aPage != 0) && (userCount <= CMaxPlayerButtons))
aPage = 0; // Reset page to 0 if user count is lower or equal to max button count
AddPlayerButtons(ref aUIObj, panel, ref userList, commandFmt, aPage);
LogDebug("Built the current page of banned user buttons");
break;
}
}
// Decide whether or not to activate the "previous" button
if (aPage == 0) {
aUIObj.AddElement(panel, CUserBtnPagePreviousInactiveBtn);
} else {
aUIObj.AddButton(
panel, CUserBtnPageBtnPreviousLbAnchor, CUserBtnPageBtnPreviousRtAnchor, CuiColor.Button, CuiColor.TextAlt, "<<",
string.Format(npBtnCommandFmt, aPage - 1), string.Empty, string.Empty, 18
);
}
// Decide whether or not to activate the "next" button
if (userCount > CMaxPlayerButtons * (aPage + 1)) {
aUIObj.AddButton(
panel, CUserBtnPageBtnNextLbAnchor, CUserBtnPageBtnNextRtAnchor, CuiColor.Button, CuiColor.TextAlt, ">>",
string.Format(npBtnCommandFmt, aPage + 1), string.Empty, string.Empty, 18
);
} else {
aUIObj.AddElement(panel, CUserBtnPageNextInactiveBtn);
}
LogDebug("Built the user button page");
LogDebug($"Elapsed time (BuildUserBtnPage): {Time.realtimeSinceStartup - aUIObj.StartTime:F8}");
}
/// <summary>
/// Add the user information labels to the parent element
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aParent">Parent panel name</param>
/// <param name="aPlayerId">Player ID (SteamId64)</param>
/// <param name="aPlayer">Player who's information we need to display</param>
private void AddUserPageInfoLabels(ref Cui aUIObj, string aParent, ulong aPlayerId, ref BasePlayer aPlayer) {
string lastCheatStr = lang.GetMessage("Never Label Text", this, aUIObj.PlayerIdString);
string authLevel = ServerUsers.Get(aPlayerId)?.group.ToString() ?? "None";
string CpAddress = lang.GetMessage("OffLine Label Text", this, aUIObj.PlayerIdString);
string CpPing = lang.GetMessag Its cut off...
snaplatack
Its cut off...
i did a quick fix: "
Just sharing this in case it helps anyone.
I've updated PlayerAdministration to fix the following compile error after the latest Rust update:
Error while compiling PlayerAdministration: 'BasePlayer' does not contain a definition for 'lastAdminCheatTime' and no accessible extension method 'lastAdminCheatTime' accepting a first argument of type 'BasePlayer' could be found (are you missing a using directive or an assembly reference?) | Line: 1292, Pos: 25
You can grab the updated PlayerAdministration.cs here:
Hopefully this saves a few people some time until an official update is available.
If you run into any issues or find anything I've missed, let me know and I'll take a look."
Not doing that. I sent a patch
Heres the parts to change
LINE 1271 - 1295: Copy paste and should work
/// <summary>
/// Add the user information labels to the parent element
/// </summary>
/// <param name="aUIObj">Cui object</param>
/// <param name="aParent">Parent panel name</param>
/// <param name="aPlayerId">Player ID (SteamId64)</param>
/// <param name="aPlayer">Player who's information we need to display</param>
private void AddUserPageInfoLabels(ref Cui aUIObj, string aParent, ulong aPlayerId, ref BasePlayer aPlayer) {
string lastCheatStr = lang.GetMessage("Never Label Text", this, aUIObj.PlayerIdString);
string authLevel = ServerUsers.Get(aPlayerId)?.group.ToString() ?? "None";
string CpAddress = lang.GetMessage("OffLine Label Text", this, aUIObj.PlayerIdString);
string CpPing = lang.GetMessage("OffLine Label Text", this, aUIObj.PlayerIdString);
float cheatTime = AntiHack.PlayerStates[aPlayer.ActivePlayerInd].LastAdminCheatTime;
// Recover & pre-process user connection data(player should be connected so calls are safe)
if (aPlayer.IsConnected)
{
CpAddress = aPlayer.net.connection.ipaddress.Split(':')[0];
CpPing = Network.Net.sv.GetAveragePing(aPlayer.net.connection).ToString();
}
// Pre-calc last admin cheat
if (cheatTime > 0f) {
TimeSpan lastCheatSinceStart = new TimeSpan(0, 0, (int)(Time.realtimeSinceStartup - cheatTime));
lastCheatStr = $"{DateTime.UtcNow.Subtract(lastCheatSinceStart):yyyy-MM-dd HH:mm:ss} UTC";
} snaplatack
Not doing that. I sent a patch
Heres the parts to change
LINE 1271 - 1295: Copy paste and should work
/// <summary> /// Add the user information labels to the parent element /// </summary> /// <param name="aUIObj">Cui object</param> /// <param name="aParent">Parent panel name</param> /// <param name="aPlayerId">Player ID (SteamId64)</param> /// <param name="aPlayer">Player who's information we need to display</param> private void AddUserPageInfoLabels(ref Cui aUIObj, string aParent, ulong aPlayerId, ref BasePlayer aPlayer) { string lastCheatStr = lang.GetMessage("Never Label Text", this, aUIObj.PlayerIdString); string authLevel = ServerUsers.Get(aPlayerId)?.group.ToString() ?? "None"; string CpAddress = lang.GetMessage("OffLine Label Text", this, aUIObj.PlayerIdString); string CpPing = lang.GetMessage("OffLine Label Text", this, aUIObj.PlayerIdString); float cheatTime = AntiHack.PlayerStates[aPlayer.ActivePlayerInd].LastAdminCheatTime; // Recover & pre-process user connection data(player should be connected so calls are safe) if (aPlayer.IsConnected) { CpAddress = aPlayer.net.connection.ipaddress.Split(':')[0]; CpPing = Network.Net.sv.GetAveragePing(aPlayer.net.connection).ToString(); } // Pre-calc last admin cheat if (cheatTime > 0f) { TimeSpan lastCheatSinceStart = new TimeSpan(0, 0, (int)(Time.realtimeSinceStartup - cheatTime)); lastCheatStr = $"{DateTime.UtcNow.Subtract(lastCheatSinceStart):yyyy-MM-dd HH:mm:ss} UTC"; }
This here is the only fix matters. Thanks Bud!
Sorry guys but id did not noticed the copy paste is truncated here , nordic.gabriel-vergara.com/modsfix/PlayerAdministration.cs.txt
Well here is my fix fully