* Fix usages of TryIndex()
Most usages of TryIndex() were using it incorrectly. Checking whether prototype IDs specified in prototypes actually existed before using them. This is not appropriate as it's just hiding bugs that should be getting caught by the YAML linter and other tools. (#39115)
This then resulted in TryIndex() getting modified to log errors (94f98073b0), which is incorrect as it causes false-positive errors in proper uses of the API: external data validation. (#39098)
This commit goes through and checks every call site of TryIndex() to see whether they were correct. Most call sites were replaced with the new Resolve(), which is suitable for these "defensive programming" use cases.
Fixes #39115
Breaking change: while doing this I noticed IdCardComponent and related systems were erroneously using ProtoId<AccessLevelPrototype> for job prototypes. This has been corrected.
* fix tests
---------
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
620 lines
22 KiB
C#
620 lines
22 KiB
C#
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Numerics;
|
|
using Content.Client.Administration.UI.CustomControls;
|
|
using Content.Shared.Administration;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.Roles;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.CustomControls;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Client.Administration.UI.BanPanel;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class BanPanel : DefaultWindow
|
|
{
|
|
public event Action<string?, (IPAddress, int)?, bool, ImmutableTypedHwid?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
|
public event Action<string>? PlayerChanged;
|
|
private string? PlayerUsername { get; set; }
|
|
private (IPAddress, int)? IpAddress { get; set; }
|
|
private ImmutableTypedHwid? Hwid { get; set; }
|
|
private double TimeEntered { get; set; }
|
|
private uint Multiplier { get; set; }
|
|
private bool HasBanFlag { get; set; }
|
|
private TimeSpan? ButtonResetOn { get; set; }
|
|
|
|
// This is less efficient than just holding a reference to the root control and enumerating children, but you
|
|
// have to know how the controls are nested, which makes the code more complicated.
|
|
// Role group name -> the role buttons themselves.
|
|
private readonly Dictionary<string, List<Button>> _roleCheckboxes = new();
|
|
private readonly ISawmill _banpanelSawmill;
|
|
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
[Dependency] private readonly ILogManager _logManager = default!;
|
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
|
|
|
private const string ExpandedArrow = "▼";
|
|
private const string ContractedArrow = "▶";
|
|
|
|
private enum TabNumbers
|
|
{
|
|
BasicInfo,
|
|
//Text,
|
|
Players,
|
|
Roles
|
|
}
|
|
|
|
private enum Multipliers
|
|
{
|
|
Minutes,
|
|
Hours,
|
|
Days,
|
|
Weeks,
|
|
Months,
|
|
Years,
|
|
Permanent
|
|
}
|
|
|
|
private enum Types
|
|
{
|
|
None,
|
|
Server,
|
|
Role
|
|
}
|
|
|
|
public BanPanel()
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
IoCManager.InjectDependencies(this);
|
|
_banpanelSawmill = _logManager.GetSawmill("admin.banpanel");
|
|
PlayerList.OnSelectionChanged += OnPlayerSelectionChanged;
|
|
PlayerNameLine.OnFocusExit += _ => OnPlayerNameChanged();
|
|
PlayerCheckbox.OnPressed += _ =>
|
|
{
|
|
PlayerNameLine.Editable = PlayerCheckbox.Pressed;
|
|
PlayerNameLine.ModulateSelfOverride = null;
|
|
};
|
|
TimeLine.OnTextChanged += OnMinutesChanged;
|
|
MultiplierOption.OnItemSelected += args =>
|
|
{
|
|
MultiplierOption.SelectId(args.Id);
|
|
OnMultiplierChanged();
|
|
};
|
|
IpLine.OnFocusExit += _ => OnIpChanged();
|
|
IpCheckbox.OnPressed += _ =>
|
|
{
|
|
IpLine.Editable = IpCheckbox.Pressed;
|
|
OnIpChanged();
|
|
};
|
|
HwidLine.OnFocusExit += _ => OnHwidChanged();
|
|
HwidCheckbox.OnPressed += _ =>
|
|
{
|
|
HwidLine.Editable = HwidCheckbox.Pressed;
|
|
OnHwidChanged();
|
|
};
|
|
TypeOption.OnItemSelected += args =>
|
|
{
|
|
TypeOption.SelectId(args.Id);
|
|
OnTypeChanged();
|
|
};
|
|
LastConnCheckbox.OnPressed += args =>
|
|
{
|
|
IpLine.ModulateSelfOverride = null;
|
|
HwidLine.ModulateSelfOverride = null;
|
|
OnIpChanged();
|
|
OnHwidChanged();
|
|
};
|
|
SubmitButton.OnPressed += SubmitButtonOnOnPressed;
|
|
|
|
IpCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanIpBanDefault);
|
|
HwidCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanHwidBanDefault);
|
|
LastConnCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanUseLastDetails);
|
|
EraseCheckbox.Pressed = _cfg.GetCVar(CCVars.ServerBanErasePlayer);
|
|
|
|
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-none"), (int) NoteSeverity.None);
|
|
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-low"), (int) NoteSeverity.Minor);
|
|
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-medium"), (int) NoteSeverity.Medium);
|
|
SeverityOption.AddItem(Loc.GetString("admin-note-editor-severity-high"), (int) NoteSeverity.High);
|
|
SeverityOption.SelectId((int) NoteSeverity.Medium);
|
|
SeverityOption.OnItemSelected += args => SeverityOption.SelectId(args.Id);
|
|
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-minutes"), (int) Multipliers.Minutes);
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-hours"), (int) Multipliers.Hours);
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-days"), (int) Multipliers.Days);
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-weeks"), (int) Multipliers.Weeks);
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-months"), (int) Multipliers.Months);
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-years"), (int) Multipliers.Years);
|
|
MultiplierOption.AddItem(Loc.GetString("ban-panel-permanent"), (int) Multipliers.Permanent);
|
|
MultiplierOption.SelectId((int) Multipliers.Minutes);
|
|
OnMultiplierChanged();
|
|
|
|
Tabs.SetTabTitle((int) TabNumbers.BasicInfo, Loc.GetString("ban-panel-tabs-basic"));
|
|
//Tabs.SetTabTitle((int) TabNumbers.Text, Loc.GetString("ban-panel-tabs-reason"));
|
|
Tabs.SetTabTitle((int) TabNumbers.Players, Loc.GetString("ban-panel-tabs-players"));
|
|
Tabs.SetTabTitle((int) TabNumbers.Roles, Loc.GetString("ban-panel-tabs-role"));
|
|
Tabs.SetTabVisible((int) TabNumbers.Roles, false);
|
|
|
|
TypeOption.AddItem(Loc.GetString("ban-panel-select"), (int) Types.None);
|
|
TypeOption.AddItem(Loc.GetString("ban-panel-server"), (int) Types.Server);
|
|
TypeOption.AddItem(Loc.GetString("ban-panel-role"), (int) Types.Role);
|
|
|
|
ReasonTextEdit.Placeholder = new Rope.Leaf(Loc.GetString("ban-panel-reason"));
|
|
|
|
var departmentJobs = _protoMan.EnumeratePrototypes<DepartmentPrototype>()
|
|
.OrderBy(x => x.Weight);
|
|
foreach (var proto in departmentJobs)
|
|
{
|
|
var roles = proto.Roles.Select(x => _protoMan.Index(x))
|
|
.OrderBy(x => x.ID);
|
|
CreateRoleGroup(proto.ID, proto.Color, roles);
|
|
}
|
|
|
|
var antagRoles = _protoMan.EnumeratePrototypes<AntagPrototype>()
|
|
.OrderBy(x => x.ID);
|
|
CreateRoleGroup("Antagonist", Color.Red, antagRoles);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a "Role group" which stores information and logic for one "group" of roll bans.
|
|
/// For example, all antags are one group, logi is a group, medical is a group, etc...
|
|
/// </summary>
|
|
private void CreateRoleGroup<T>(string groupName, Color color, IEnumerable<T> roles) where T : class, IPrototype
|
|
{
|
|
var outerContainer = new BoxContainer
|
|
{
|
|
Name = $"{groupName}GroupOuterBox",
|
|
HorizontalExpand = true,
|
|
VerticalExpand = true,
|
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
|
Margin = new Thickness(4),
|
|
};
|
|
|
|
// Stores stuff like ban all and expand buttons.
|
|
var roleGroupHeader = new BoxContainer
|
|
{
|
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
|
};
|
|
|
|
// Stores the role checkboxes themselves.
|
|
var innerContainer = new GridContainer
|
|
{
|
|
Name = $"{groupName}GroupInnerBox",
|
|
HorizontalExpand = true,
|
|
Columns = 2,
|
|
Visible = false,
|
|
Margin = new Thickness(15, 5, 0, 5),
|
|
};
|
|
|
|
var roleGroupCheckbox = CreateRoleGroupHeader(groupName, roleGroupHeader, color, innerContainer);
|
|
|
|
outerContainer.AddChild(roleGroupHeader);
|
|
|
|
// Add the roles themselves
|
|
foreach (var role in roles)
|
|
{
|
|
AddRoleCheckbox(groupName, role.ID, innerContainer, roleGroupCheckbox);
|
|
}
|
|
|
|
outerContainer.AddChild(innerContainer);
|
|
|
|
RolesContainer.AddChild(new PanelContainer
|
|
{
|
|
PanelOverride = new StyleBoxFlat
|
|
{
|
|
BackgroundColor = color
|
|
}
|
|
});
|
|
RolesContainer.AddChild(outerContainer);
|
|
RolesContainer.AddChild(new HSeparator());
|
|
}
|
|
|
|
private Button CreateRoleGroupHeader(string groupName, BoxContainer header, Color color, GridContainer innerContainer)
|
|
{
|
|
var roleGroupCheckbox = new Button
|
|
{
|
|
Name = $"{groupName}GroupCheckbox",
|
|
Text = "Ban all",
|
|
Margin = new Thickness(0, 0, 5, 0),
|
|
ToggleMode = true,
|
|
};
|
|
|
|
// When this is toggled, toggle all buttons in this group so they match.
|
|
roleGroupCheckbox.OnToggled += args =>
|
|
{
|
|
foreach (var role in _roleCheckboxes[groupName])
|
|
{
|
|
role.Pressed = args.Pressed;
|
|
}
|
|
|
|
if (args.Pressed)
|
|
{
|
|
if (!Enum.TryParse(_cfg.GetCVar(CCVars.DepartmentBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
|
{
|
|
_banpanelSawmill
|
|
.Warning("Departmental role ban severity could not be parsed from config!");
|
|
return;
|
|
}
|
|
SeverityOption.SelectId((int) newSeverity);
|
|
}
|
|
else
|
|
{
|
|
foreach (var roleButtons in _roleCheckboxes.Values)
|
|
{
|
|
foreach (var button in roleButtons)
|
|
{
|
|
if (button.Pressed)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity newSeverity))
|
|
{
|
|
_banpanelSawmill
|
|
.Warning("Role ban severity could not be parsed from config!");
|
|
return;
|
|
}
|
|
SeverityOption.SelectId((int) newSeverity);
|
|
}
|
|
};
|
|
|
|
var hideButton = new Button
|
|
{
|
|
Text = Loc.GetString("role-bans-expand-roles") + " " + ContractedArrow,
|
|
ToggleMode = true,
|
|
};
|
|
hideButton.OnPressed += args =>
|
|
{
|
|
innerContainer.Visible = args.Button.Pressed;
|
|
((Button)args.Button).Text = args.Button.Pressed
|
|
? Loc.GetString("role-bans-contract-roles") + " " + ExpandedArrow
|
|
: Loc.GetString("role-bans-expand-roles") + " " + ContractedArrow;
|
|
};
|
|
header.AddChild(new Label
|
|
{
|
|
Text = groupName,
|
|
Modulate = color,
|
|
Margin = new Thickness(0, 0, 5, 0),
|
|
});
|
|
header.AddChild(roleGroupCheckbox);
|
|
header.AddChild(hideButton);
|
|
return roleGroupCheckbox;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a checkbutton specifically for one "role" in a "group"
|
|
/// E.g. it would add the Chief Medical Officer "role" into the "Medical" group.
|
|
/// </summary>
|
|
private void AddRoleCheckbox(string group, string role, GridContainer roleGroupInnerContainer, Button roleGroupCheckbox)
|
|
{
|
|
var roleCheckboxContainer = new BoxContainer();
|
|
var roleCheckButton = new Button
|
|
{
|
|
Name = $"{role}RoleCheckbox",
|
|
Text = role,
|
|
ToggleMode = true,
|
|
};
|
|
roleCheckButton.OnToggled += args =>
|
|
{
|
|
// Checks the role group checkbox if all the children are pressed
|
|
if (args.Pressed && _roleCheckboxes[group].All(e => e.Pressed))
|
|
roleGroupCheckbox.Pressed = args.Pressed;
|
|
else
|
|
roleGroupCheckbox.Pressed = false;
|
|
};
|
|
|
|
// This is adding the icon before the role name
|
|
// TODO: This should not be using raw strings for prototypes as it means it won't be validated at all.
|
|
// I know the ban manager is doing the same thing, but that should not leak into UI code.
|
|
if (_protoMan.TryIndex<JobPrototype>(role, out var jobPrototype) && _protoMan.Resolve(jobPrototype.Icon, out var iconProto))
|
|
{
|
|
var jobIconTexture = new TextureRect
|
|
{
|
|
Texture = _entMan.System<SpriteSystem>().Frame0(iconProto.Icon),
|
|
TextureScale = new Vector2(2.5f, 2.5f),
|
|
Stretch = TextureRect.StretchMode.KeepCentered,
|
|
Margin = new Thickness(5, 0, 0, 0),
|
|
};
|
|
roleCheckboxContainer.AddChild(jobIconTexture);
|
|
}
|
|
|
|
roleCheckboxContainer.AddChild(roleCheckButton);
|
|
|
|
roleGroupInnerContainer.AddChild(roleCheckboxContainer);
|
|
|
|
_roleCheckboxes.TryAdd(group, []);
|
|
_roleCheckboxes[group].Add(roleCheckButton);
|
|
}
|
|
|
|
public void UpdateBanFlag(bool newFlag)
|
|
{
|
|
HasBanFlag = newFlag;
|
|
SubmitButton.Visible = HasBanFlag;
|
|
ModulateSelfOverride = HasBanFlag ? Color.Red : null;
|
|
}
|
|
|
|
public void UpdatePlayerData(string playerName)
|
|
{
|
|
if (string.IsNullOrEmpty(playerName))
|
|
{
|
|
PlayerNameLine.ModulateSelfOverride = Color.Red;
|
|
ErrorLevel |= ErrorLevelEnum.PlayerName;
|
|
UpdateSubmitEnabled();
|
|
return;
|
|
}
|
|
PlayerNameLine.ModulateSelfOverride = null;
|
|
ErrorLevel &= ~ErrorLevelEnum.PlayerName;
|
|
UpdateSubmitEnabled();
|
|
PlayerUsername = playerName;
|
|
PlayerNameLine.Text = playerName;
|
|
}
|
|
|
|
[Flags]
|
|
private enum ErrorLevelEnum : byte
|
|
{
|
|
None = 0,
|
|
Minutes = 1 << 0,
|
|
PlayerName = 1 << 1,
|
|
IpAddress = 1 << 2,
|
|
Hwid = 1 << 3,
|
|
}
|
|
|
|
private ErrorLevelEnum ErrorLevel { get; set; }
|
|
|
|
private void OnMinutesChanged(LineEdit.LineEditEventArgs args)
|
|
{
|
|
TimeLine.Text = args.Text;
|
|
if (!double.TryParse(args.Text, out var result))
|
|
{
|
|
ExpiresLabel.Text = "err";
|
|
ErrorLevel |= ErrorLevelEnum.Minutes;
|
|
TimeLine.ModulateSelfOverride = Color.Red;
|
|
UpdateSubmitEnabled();
|
|
return;
|
|
}
|
|
|
|
ErrorLevel &= ~ErrorLevelEnum.Minutes;
|
|
TimeLine.ModulateSelfOverride = null;
|
|
TimeEntered = result;
|
|
UpdateSubmitEnabled();
|
|
UpdateExpiresLabel();
|
|
}
|
|
|
|
private void OnMultiplierChanged()
|
|
{
|
|
TimeLine.Editable = MultiplierOption.SelectedId != (int) Multipliers.Permanent;
|
|
Multiplier = MultiplierOption.SelectedId switch
|
|
{
|
|
(int) Multipliers.Minutes => 1,
|
|
(int) Multipliers.Hours => 60,
|
|
(int) Multipliers.Days => 60 * 24,
|
|
(int) Multipliers.Weeks => 60 * 24 * 7,
|
|
(int) Multipliers.Months => 60 * 24 * 30,
|
|
(int) Multipliers.Years => 60 * 24 * 365,
|
|
(int) Multipliers.Permanent => 0,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(MultiplierOption.SelectedId), "Multiplier out of range")
|
|
};
|
|
UpdateExpiresLabel();
|
|
}
|
|
|
|
private void UpdateExpiresLabel()
|
|
{
|
|
var minutes = (uint) (TimeEntered * Multiplier);
|
|
ExpiresLabel.Text = minutes == 0
|
|
? $"{Loc.GetString("admin-note-editor-expiry-label")} {Loc.GetString("server-ban-string-never")}"
|
|
: $"{Loc.GetString("admin-note-editor-expiry-label")} {DateTime.Now + TimeSpan.FromMinutes(minutes):yyyy/MM/dd HH:mm:ss}";
|
|
}
|
|
|
|
private void OnIpChanged()
|
|
{
|
|
if (LastConnCheckbox.Pressed && IpAddress is null || !IpCheckbox.Pressed)
|
|
{
|
|
IpAddress = null;
|
|
ErrorLevel &= ~ErrorLevelEnum.IpAddress;
|
|
IpLine.ModulateSelfOverride = null;
|
|
UpdateSubmitEnabled();
|
|
return;
|
|
}
|
|
var ip = IpLine.Text;
|
|
var hid = "0";
|
|
if (ip.Contains('/'))
|
|
{
|
|
var split = ip.Split('/');
|
|
ip = split[0];
|
|
hid = split[1];
|
|
}
|
|
|
|
if (!IPAddress.TryParse(ip, out var parsedIp) || !byte.TryParse(hid, out var hidInt) || hidInt > 128 || hidInt > 32 && parsedIp.AddressFamily == AddressFamily.InterNetwork)
|
|
{
|
|
ErrorLevel |= ErrorLevelEnum.IpAddress;
|
|
IpLine.ModulateSelfOverride = Color.Red;
|
|
UpdateSubmitEnabled();
|
|
return;
|
|
}
|
|
|
|
if (hidInt == 0)
|
|
hidInt = (byte) (parsedIp.AddressFamily == AddressFamily.InterNetworkV6 ? 128 : 32);
|
|
IpAddress = (parsedIp, hidInt);
|
|
ErrorLevel &= ~ErrorLevelEnum.IpAddress;
|
|
IpLine.ModulateSelfOverride = null;
|
|
UpdateSubmitEnabled();
|
|
}
|
|
|
|
private void OnHwidChanged()
|
|
{
|
|
var hwidString = HwidLine.Text;
|
|
ImmutableTypedHwid? hwid = null;
|
|
if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid))
|
|
{
|
|
ErrorLevel |= ErrorLevelEnum.Hwid;
|
|
HwidLine.ModulateSelfOverride = Color.Red;
|
|
UpdateSubmitEnabled();
|
|
return;
|
|
}
|
|
|
|
ErrorLevel &= ~ErrorLevelEnum.Hwid;
|
|
HwidLine.ModulateSelfOverride = null;
|
|
UpdateSubmitEnabled();
|
|
|
|
if (LastConnCheckbox.Pressed || !HwidCheckbox.Pressed)
|
|
{
|
|
Hwid = null;
|
|
return;
|
|
}
|
|
Hwid = hwid;
|
|
}
|
|
|
|
private void OnTypeChanged()
|
|
{
|
|
TypeOption.ModulateSelfOverride = null;
|
|
Tabs.SetTabVisible((int) TabNumbers.Roles, TypeOption.SelectedId == (int) Types.Role);
|
|
NoteSeverity? newSeverity = null;
|
|
switch (TypeOption.SelectedId)
|
|
{
|
|
case (int)Types.Server:
|
|
if (Enum.TryParse(_cfg.GetCVar(CCVars.ServerBanDefaultSeverity), true, out NoteSeverity serverSeverity))
|
|
newSeverity = serverSeverity;
|
|
else
|
|
{
|
|
_banpanelSawmill
|
|
.Warning("Server ban severity could not be parsed from config!");
|
|
}
|
|
|
|
break;
|
|
case (int) Types.Role:
|
|
|
|
if (Enum.TryParse(_cfg.GetCVar(CCVars.RoleBanDefaultSeverity), true, out NoteSeverity roleSeverity))
|
|
{
|
|
newSeverity = roleSeverity;
|
|
}
|
|
else
|
|
{
|
|
_banpanelSawmill
|
|
.Warning("Role ban severity could not be parsed from config!");
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (newSeverity != null)
|
|
SeverityOption.SelectId((int) newSeverity.Value);
|
|
}
|
|
|
|
private void UpdateSubmitEnabled()
|
|
{
|
|
SubmitButton.Disabled = ErrorLevel != ErrorLevelEnum.None;
|
|
}
|
|
|
|
private void OnPlayerNameChanged()
|
|
{
|
|
if (PlayerUsername == PlayerNameLine.Text)
|
|
return;
|
|
PlayerUsername = PlayerNameLine.Text;
|
|
if (!PlayerCheckbox.Pressed)
|
|
return;
|
|
if (string.IsNullOrWhiteSpace(PlayerUsername))
|
|
ErrorLevel |= ErrorLevelEnum.PlayerName;
|
|
else
|
|
ErrorLevel &= ~ErrorLevelEnum.PlayerName;
|
|
|
|
UpdateSubmitEnabled();
|
|
PlayerChanged?.Invoke(PlayerUsername);
|
|
}
|
|
|
|
public void OnPlayerSelectionChanged(PlayerInfo? player)
|
|
{
|
|
PlayerNameLine.Text = player?.Username ?? string.Empty;
|
|
OnPlayerNameChanged();
|
|
}
|
|
|
|
private void ResetTextEditor(GUIBoundKeyEventArgs _)
|
|
{
|
|
ReasonTextEdit.ModulateSelfOverride = null;
|
|
ReasonTextEdit.OnKeyBindDown -= ResetTextEditor;
|
|
}
|
|
|
|
private void SubmitButtonOnOnPressed(BaseButton.ButtonEventArgs obj)
|
|
{
|
|
string[]? roles = null;
|
|
if (TypeOption.SelectedId == (int) Types.Role)
|
|
{
|
|
var rolesList = new List<string>();
|
|
if (_roleCheckboxes.Count == 0)
|
|
throw new DebugAssertException("RoleCheckboxes was empty");
|
|
|
|
foreach (var button in _roleCheckboxes.Values.SelectMany(departmentButtons => departmentButtons))
|
|
{
|
|
if (button is { Pressed: true, Text: not null })
|
|
{
|
|
rolesList.Add(button.Text);
|
|
}
|
|
}
|
|
|
|
if (rolesList.Count == 0)
|
|
{
|
|
Tabs.CurrentTab = (int) TabNumbers.Roles;
|
|
return;
|
|
}
|
|
|
|
roles = rolesList.ToArray();
|
|
}
|
|
|
|
if (TypeOption.SelectedId == (int) Types.None)
|
|
{
|
|
TypeOption.ModulateSelfOverride = Color.Red;
|
|
Tabs.CurrentTab = (int) TabNumbers.BasicInfo;
|
|
return;
|
|
}
|
|
|
|
var reason = Rope.Collapse(ReasonTextEdit.TextRope);
|
|
if (string.IsNullOrWhiteSpace(reason))
|
|
{
|
|
//Tabs.CurrentTab = (int) TabNumbers.Text;
|
|
Tabs.CurrentTab = (int) TabNumbers.BasicInfo;
|
|
ReasonTextEdit.GrabKeyboardFocus();
|
|
ReasonTextEdit.ModulateSelfOverride = Color.Red;
|
|
ReasonTextEdit.OnKeyBindDown += ResetTextEditor;
|
|
return;
|
|
}
|
|
|
|
if (ButtonResetOn is null)
|
|
{
|
|
ButtonResetOn = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
|
|
SubmitButton.ModulateSelfOverride = Color.Red;
|
|
SubmitButton.Text = Loc.GetString("ban-panel-confirm");
|
|
return;
|
|
}
|
|
|
|
var player = PlayerCheckbox.Pressed ? PlayerUsername : null;
|
|
var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null;
|
|
var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null;
|
|
var severity = (NoteSeverity) SeverityOption.SelectedId;
|
|
var erase = EraseCheckbox.Pressed;
|
|
BanSubmitted?.Invoke(player, IpAddress, useLastIp, Hwid, useLastHwid, (uint) (TimeEntered * Multiplier), reason, severity, roles, erase);
|
|
}
|
|
|
|
protected override void FrameUpdate(FrameEventArgs args)
|
|
{
|
|
base.FrameUpdate(args);
|
|
|
|
// This checks for null for free, do not invert it as null always produces a false value
|
|
if (_gameTiming.CurTime > ButtonResetOn)
|
|
{
|
|
ButtonResetOn = null;
|
|
SubmitButton.ModulateSelfOverride = null;
|
|
SubmitButton.Text = Loc.GetString("ban-panel-submit");
|
|
}
|
|
}
|
|
}
|