Add admin Erase verb, add checkbox to erase from the ban panel (#20985)
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
<LineEdit Name="HwidLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc ban-panel-hwid}" ToolTip="{Loc ban-panel-ip-hwid-tooltip}" />
|
<LineEdit Name="HwidLine" MinWidth="100" HorizontalExpand="True" PlaceHolder="{Loc ban-panel-hwid}" ToolTip="{Loc ban-panel-ip-hwid-tooltip}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<CheckBox Name="LastConnCheckbox" Margin="2" Text="{Loc ban-panel-last-conn}" Pressed="True" />
|
<CheckBox Name="LastConnCheckbox" Margin="2" Text="{Loc ban-panel-last-conn}" Pressed="True" />
|
||||||
|
<CheckBox Name="EraseCheckbox" Margin="2" Text="{Loc ban-panel-erase}" Pressed="False" />
|
||||||
<BoxContainer Orientation="Horizontal" Margin="2">
|
<BoxContainer Orientation="Horizontal" Margin="2">
|
||||||
<LineEdit Name="TimeLine" MaxWidth="150" MinWidth="70" PlaceHolder="0" />
|
<LineEdit Name="TimeLine" MaxWidth="150" MinWidth="70" PlaceHolder="0" />
|
||||||
<OptionButton Name="MultiplierOption" />
|
<OptionButton Name="MultiplierOption" />
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal" Margin="4">
|
<BoxContainer Orientation="Horizontal" Margin="4">
|
||||||
<OptionButton Name="TypeOption" />
|
<OptionButton Name="TypeOption" />
|
||||||
<Control MinWidth="30"></Control>
|
<Control MinWidth="30" />
|
||||||
<Label Text="{Loc ban-panel-severity}" />
|
<Label Text="{Loc ban-panel-severity}" />
|
||||||
<OptionButton Name="SeverityOption" />
|
<OptionButton Name="SeverityOption" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using Content.Client.Administration.UI.CustomControls;
|
using Content.Client.Administration.UI.CustomControls;
|
||||||
using Content.Client.Stylesheets;
|
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
@@ -23,7 +20,7 @@ namespace Content.Client.Administration.UI.BanPanel;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class BanPanel : DefaultWindow
|
public sealed partial class BanPanel : DefaultWindow
|
||||||
{
|
{
|
||||||
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?>? BanSubmitted;
|
public event Action<string?, (IPAddress, int)?, bool, byte[]?, bool, uint, string, NoteSeverity, string[]?, bool>? BanSubmitted;
|
||||||
public event Action<string>? PlayerChanged;
|
public event Action<string>? PlayerChanged;
|
||||||
private string? PlayerUsername { get; set; }
|
private string? PlayerUsername { get; set; }
|
||||||
private (IPAddress, int)? IpAddress { get; set; }
|
private (IPAddress, int)? IpAddress { get; set; }
|
||||||
@@ -441,7 +438,8 @@ public sealed partial class BanPanel : DefaultWindow
|
|||||||
var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null;
|
var useLastIp = IpCheckbox.Pressed && LastConnCheckbox.Pressed && IpAddress is null;
|
||||||
var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null;
|
var useLastHwid = HwidCheckbox.Pressed && LastConnCheckbox.Pressed && Hwid is null;
|
||||||
var severity = (NoteSeverity) SeverityOption.SelectedId;
|
var severity = (NoteSeverity) SeverityOption.SelectedId;
|
||||||
BanSubmitted?.Invoke(player, IpAddress, useLastIp, Hwid, useLastHwid, (uint) (TimeEntered * Multiplier), reason, severity, roles);
|
var erase = EraseCheckbox.Pressed;
|
||||||
|
BanSubmitted?.Invoke(player, IpAddress, useLastIp, Hwid, useLastHwid, (uint) (TimeEntered * Multiplier), reason, severity, roles, erase);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void FrameUpdate(FrameEventArgs args)
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Client.Eui;
|
using Content.Client.Eui;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Eui;
|
using Content.Shared.Eui;
|
||||||
@@ -19,8 +14,8 @@ public sealed class BanPanelEui : BaseEui
|
|||||||
{
|
{
|
||||||
BanPanel = new BanPanel();
|
BanPanel = new BanPanel();
|
||||||
BanPanel.OnClose += () => SendMessage(new CloseEuiMessage());
|
BanPanel.OnClose += () => SendMessage(new CloseEuiMessage());
|
||||||
BanPanel.BanSubmitted += (player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles)
|
BanPanel.BanSubmitted += (player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles, erase)
|
||||||
=> SendMessage(new BanPanelEuiStateMsg.CreateBanRequest(player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles));
|
=> SendMessage(new BanPanelEuiStateMsg.CreateBanRequest(player, ip, useLastIp, hwid, useLastHwid, minutes, reason, severity, roles, erase));
|
||||||
BanPanel.PlayerChanged += player => SendMessage(new BanPanelEuiStateMsg.GetPlayerInfoRequest(player));
|
BanPanel.PlayerChanged += player => SendMessage(new BanPanelEuiStateMsg.GetPlayerInfoRequest(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Content.Client.Chat.UI;
|
|||||||
using Content.Client.Examine;
|
using Content.Client.Examine;
|
||||||
using Content.Client.Gameplay;
|
using Content.Client.Gameplay;
|
||||||
using Content.Client.Ghost;
|
using Content.Client.Ghost;
|
||||||
using Content.Client.Lobby.UI;
|
|
||||||
using Content.Client.UserInterface.Screens;
|
using Content.Client.UserInterface.Screens;
|
||||||
using Content.Client.UserInterface.Systems.Chat.Widgets;
|
using Content.Client.UserInterface.Systems.Chat.Widgets;
|
||||||
using Content.Client.UserInterface.Systems.Gameplay;
|
using Content.Client.UserInterface.Systems.Gameplay;
|
||||||
@@ -31,7 +30,6 @@ using Robust.Shared.Configuration;
|
|||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Replays;
|
using Robust.Shared.Replays;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -135,7 +133,8 @@ public sealed class ChatUIController : UIController
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Dictionary<ChatChannel, int> _unreadMessages = new();
|
private readonly Dictionary<ChatChannel, int> _unreadMessages = new();
|
||||||
|
|
||||||
public readonly List<(GameTick, ChatMessage)> History = new();
|
// TODO add a cap for this for non-replays
|
||||||
|
public readonly List<(GameTick Tick, ChatMessage Msg)> History = new();
|
||||||
|
|
||||||
// Maintains which channels a client should be able to filter (for showing in the chatbox)
|
// Maintains which channels a client should be able to filter (for showing in the chatbox)
|
||||||
// and select (for attempting to send on).
|
// and select (for attempting to send on).
|
||||||
@@ -166,6 +165,7 @@ public sealed class ChatUIController : UIController
|
|||||||
_player.LocalPlayerChanged += OnLocalPlayerChanged;
|
_player.LocalPlayerChanged += OnLocalPlayerChanged;
|
||||||
_state.OnStateChanged += StateChanged;
|
_state.OnStateChanged += StateChanged;
|
||||||
_net.RegisterNetMessage<MsgChatMessage>(OnChatMessage);
|
_net.RegisterNetMessage<MsgChatMessage>(OnChatMessage);
|
||||||
|
_net.RegisterNetMessage<MsgDeleteChatMessagesBy>(OnDeleteChatMessagesBy);
|
||||||
SubscribeNetworkEvent<DamageForceSayEvent>(OnDamageForceSay);
|
SubscribeNetworkEvent<DamageForceSayEvent>(OnDamageForceSay);
|
||||||
|
|
||||||
_speechBubbleRoot = new LayoutContainer();
|
_speechBubbleRoot = new LayoutContainer();
|
||||||
@@ -867,6 +867,16 @@ public sealed class ChatUIController : UIController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnDeleteChatMessagesBy(MsgDeleteChatMessagesBy msg)
|
||||||
|
{
|
||||||
|
// This will delete messages from an entity even if different players were the author.
|
||||||
|
// Usages of the erase admin verb should be rare enough that this does not matter.
|
||||||
|
// Otherwise the client would need to know that one entity has multiple author players,
|
||||||
|
// or the server would need to track when and which entities a player sent messages as.
|
||||||
|
History.RemoveAll(h => h.Msg.SenderKey == msg.Key || msg.Entities.Contains(h.Msg.SenderEntity));
|
||||||
|
Repopulate();
|
||||||
|
}
|
||||||
|
|
||||||
public void RegisterChat(ChatBox chat)
|
public void RegisterChat(ChatBox chat)
|
||||||
{
|
{
|
||||||
_chats.Add(chat);
|
_chats.Add(chat);
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using Content.Client.Chat;
|
|
||||||
using Content.Client.Chat.TypingIndicator;
|
|
||||||
using Content.Client.UserInterface.Systems.Chat.Controls;
|
using Content.Client.UserInterface.Systems.Chat.Controls;
|
||||||
using Content.Shared.Chat;
|
using Content.Shared.Chat;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
@@ -54,14 +52,12 @@ public partial class ChatBox : UIWidget
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg is { Read: false, AudioPath: { } })
|
if (msg is { Read: false, AudioPath: not null })
|
||||||
SoundSystem.Play(msg.AudioPath, Filter.Local(), new AudioParams().WithVolume(msg.AudioVolume));
|
SoundSystem.Play(msg.AudioPath, Filter.Local(), new AudioParams().WithVolume(msg.AudioVolume));
|
||||||
|
|
||||||
msg.Read = true;
|
msg.Read = true;
|
||||||
|
|
||||||
var color = msg.MessageColorOverride != null
|
var color = msg.MessageColorOverride ?? msg.Channel.TextColor();
|
||||||
? msg.MessageColorOverride.Value
|
|
||||||
: msg.Channel.TextColor();
|
|
||||||
|
|
||||||
AddLine(msg.WrappedMessage, color);
|
AddLine(msg.WrappedMessage, color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,29 @@ using System.Collections.Immutable;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
|
using Content.Server.Administration.Systems;
|
||||||
using Content.Server.Chat.Managers;
|
using Content.Server.Chat.Managers;
|
||||||
using Content.Server.EUI;
|
using Content.Server.EUI;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Eui;
|
using Content.Shared.Eui;
|
||||||
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
namespace Content.Server.Administration;
|
namespace Content.Server.Administration;
|
||||||
|
|
||||||
public sealed class BanPanelEui : BaseEui
|
public sealed class BanPanelEui : BaseEui, IPostInjectInit
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IBanManager _banManager = default!;
|
[Dependency] private readonly IBanManager _banManager = default!;
|
||||||
|
[Dependency] private readonly IEntityManager _entities = default!;
|
||||||
|
[Dependency] private readonly ILogManager _log = default!;
|
||||||
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
[Dependency] private readonly IPlayerLocator _playerLocator = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IChatManager _chat = default!;
|
[Dependency] private readonly IChatManager _chat = default!;
|
||||||
[Dependency] private readonly IAdminManager _admins = default!;
|
[Dependency] private readonly IAdminManager _admins = default!;
|
||||||
|
|
||||||
|
private ISawmill _sawmill = default!;
|
||||||
|
|
||||||
private NetUserId? PlayerId { get; set; }
|
private NetUserId? PlayerId { get; set; }
|
||||||
private string PlayerName { get; set; } = string.Empty;
|
private string PlayerName { get; set; } = string.Empty;
|
||||||
private IPAddress? LastAddress { get; set; }
|
private IPAddress? LastAddress { get; set; }
|
||||||
@@ -41,7 +48,7 @@ public sealed class BanPanelEui : BaseEui
|
|||||||
switch (msg)
|
switch (msg)
|
||||||
{
|
{
|
||||||
case BanPanelEuiStateMsg.CreateBanRequest r:
|
case BanPanelEuiStateMsg.CreateBanRequest r:
|
||||||
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles);
|
BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles, r.Erase);
|
||||||
break;
|
break;
|
||||||
case BanPanelEuiStateMsg.GetPlayerInfoRequest r:
|
case BanPanelEuiStateMsg.GetPlayerInfoRequest r:
|
||||||
ChangePlayer(r.PlayerUsername);
|
ChangePlayer(r.PlayerUsername);
|
||||||
@@ -49,11 +56,11 @@ public sealed class BanPanelEui : BaseEui
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray<byte>? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection<string>? roles)
|
private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray<byte>? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection<string>? roles, bool erase)
|
||||||
{
|
{
|
||||||
if (!_admins.HasAdminFlag(Player, AdminFlags.Ban))
|
if (!_admins.HasAdminFlag(Player, AdminFlags.Ban))
|
||||||
{
|
{
|
||||||
Logger.WarningS("admin.bans_eui", $"{Player.Name} ({Player.UserId}) tried to create a ban with no ban flag");
|
_sawmill.Warning($"{Player.Name} ({Player.UserId}) tried to create a ban with no ban flag");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (target == null && string.IsNullOrWhiteSpace(ipAddressString) && hwid == null)
|
if (target == null && string.IsNullOrWhiteSpace(ipAddressString) && hwid == null)
|
||||||
@@ -120,7 +127,23 @@ public sealed class BanPanelEui : BaseEui
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (erase &&
|
||||||
|
targetUid != null &&
|
||||||
|
_playerManager.TryGetSessionById(targetUid.Value, out var targetPlayer))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_entities.TrySystem(out AdminSystem? adminSystem))
|
||||||
|
adminSystem.Erase(targetPlayer);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_sawmill.Error($"Error while erasing banned player:\n{e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_banManager.CreateServerBan(targetUid, target, Player.UserId, addressRange, targetHWid, minutes, severity, reason);
|
_banManager.CreateServerBan(targetUid, target, Player.UserId, addressRange, targetHWid, minutes, severity, reason);
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,4 +183,9 @@ public sealed class BanPanelEui : BaseEui
|
|||||||
|
|
||||||
StateDirty();
|
StateDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PostInject()
|
||||||
|
{
|
||||||
|
_sawmill = _log.GetSawmill("admin.bans_eui");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,28 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
using Content.Server.Chat.Managers;
|
using Content.Server.Chat.Managers;
|
||||||
|
using Content.Server.Forensics;
|
||||||
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
using Content.Server.IdentityManagement;
|
using Content.Server.IdentityManagement;
|
||||||
using Content.Server.Mind;
|
using Content.Server.Mind;
|
||||||
using Content.Server.Players.PlayTimeTracking;
|
using Content.Server.Players.PlayTimeTracking;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.StationRecords.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Administration.Events;
|
using Content.Shared.Administration.Events;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.PDA;
|
||||||
using Content.Shared.Players.PlayTimeTracking;
|
using Content.Shared.Players.PlayTimeTracking;
|
||||||
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Content.Shared.Roles.Jobs;
|
using Content.Shared.Roles.Jobs;
|
||||||
|
using Content.Shared.StationRecords;
|
||||||
|
using Content.Shared.Throwing;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -27,10 +38,17 @@ namespace Content.Server.Administration.Systems
|
|||||||
[Dependency] private readonly IChatManager _chat = default!;
|
[Dependency] private readonly IChatManager _chat = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly PlayTimeTrackingManager _playTime = default!;
|
[Dependency] private readonly HandsSystem _hands = default!;
|
||||||
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
||||||
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
[Dependency] private readonly MindSystem _minds = default!;
|
[Dependency] private readonly MindSystem _minds = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly PhysicsSystem _physics = default!;
|
||||||
|
[Dependency] private readonly PlayTimeTrackingManager _playTime = default!;
|
||||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||||
|
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||||
|
[Dependency] private readonly StationRecordsSystem _stationRecords = default!;
|
||||||
|
[Dependency] private readonly TransformSystem _transform = default!;
|
||||||
|
|
||||||
private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new();
|
private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new();
|
||||||
|
|
||||||
@@ -299,5 +317,76 @@ namespace Content.Server.Administration.Systems
|
|||||||
RaiseNetworkEvent(ev, admin);
|
RaiseNetworkEvent(ev, admin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Erases a player from the round.
|
||||||
|
/// This removes them and any trace of them from the round, deleting their
|
||||||
|
/// chat messages and showing a popup to other players.
|
||||||
|
/// Their items are dropped on the ground.
|
||||||
|
/// </summary>
|
||||||
|
public void Erase(IPlayerSession player)
|
||||||
|
{
|
||||||
|
var entity = player.AttachedEntity;
|
||||||
|
_chat.DeleteMessagesBy(player);
|
||||||
|
|
||||||
|
if (entity != null && !TerminatingOrDeleted(entity.Value))
|
||||||
|
{
|
||||||
|
if (TryComp(entity.Value, out TransformComponent? transform))
|
||||||
|
{
|
||||||
|
var coordinates = _transform.GetMoverCoordinates(entity.Value, transform);
|
||||||
|
var name = Identity.Entity(entity.Value, EntityManager);
|
||||||
|
_popup.PopupCoordinates(Loc.GetString("admin-erase-popup", ("user", name)), coordinates, PopupType.LargeCaution);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in _inventory.GetHandOrInventoryEntities(entity.Value))
|
||||||
|
{
|
||||||
|
if (TryComp(item, out PdaComponent? pda) &&
|
||||||
|
TryComp(pda.ContainedId, out StationRecordKeyStorageComponent? keyStorage) &&
|
||||||
|
keyStorage.Key is { } key &&
|
||||||
|
_stationRecords.TryGetRecord(key.OriginStation, key, out GeneralStationRecord? record))
|
||||||
|
{
|
||||||
|
if (TryComp(entity, out DnaComponent? dna) &&
|
||||||
|
dna.DNA != record.DNA)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp(entity, out FingerprintComponent? fingerPrint) &&
|
||||||
|
fingerPrint.Fingerprint != record.Fingerprint)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_stationRecords.RemoveRecord(key.OriginStation, key);
|
||||||
|
Del(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp(entity.Value, out InventoryComponent? inventory) &&
|
||||||
|
_inventory.TryGetSlots(entity.Value, out var slots, inventory))
|
||||||
|
{
|
||||||
|
foreach (var slot in slots)
|
||||||
|
{
|
||||||
|
if (_inventory.TryUnequip(entity.Value, entity.Value, slot.Name, out var item, true, true))
|
||||||
|
{
|
||||||
|
_physics.ApplyAngularImpulse(item.Value, ThrowingSystem.ThrowAngularImpulse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp(entity.Value, out HandsComponent? hands))
|
||||||
|
{
|
||||||
|
foreach (var hand in _hands.EnumerateHands(entity.Value, hands))
|
||||||
|
{
|
||||||
|
_hands.TryDrop(entity.Value, hand, checkActionBlocker: false, doDropInteraction: false, handsComp: hands);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_minds.WipeMind(player);
|
||||||
|
QueueDel(entity);
|
||||||
|
|
||||||
|
_gameTicker.SpawnObserver(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ namespace Content.Server.Administration.Systems
|
|||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly AdminSystem _adminSystem = default!;
|
||||||
[Dependency] private readonly DisposalTubeSystem _disposalTubes = default!;
|
[Dependency] private readonly DisposalTubeSystem _disposalTubes = default!;
|
||||||
[Dependency] private readonly EuiManager _euiManager = default!;
|
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||||
[Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!;
|
[Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!;
|
||||||
@@ -140,6 +141,20 @@ namespace Content.Server.Administration.Systems
|
|||||||
},
|
},
|
||||||
Impact = LogImpact.Medium,
|
Impact = LogImpact.Medium,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Erase
|
||||||
|
args.Verbs.Add(new Verb
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("admin-verbs-erase"),
|
||||||
|
Category = VerbCategory.Admin,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
_adminSystem.Erase(targetActor.PlayerSession);
|
||||||
|
},
|
||||||
|
Impact = LogImpact.Extreme,
|
||||||
|
ConfirmationPopup = true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin Logs
|
// Admin Logs
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
using Content.Server.Administration.Systems;
|
using Content.Server.Administration.Systems;
|
||||||
@@ -50,9 +51,13 @@ namespace Content.Server.Chat.Managers
|
|||||||
private bool _oocEnabled = true;
|
private bool _oocEnabled = true;
|
||||||
private bool _adminOocEnabled = true;
|
private bool _adminOocEnabled = true;
|
||||||
|
|
||||||
|
public Dictionary<IPlayerSession, int> SenderKeys { get; } = new();
|
||||||
|
public Dictionary<IPlayerSession, HashSet<NetEntity>> SenderEntities { get; } = new();
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
_netManager.RegisterNetMessage<MsgChatMessage>();
|
_netManager.RegisterNetMessage<MsgChatMessage>();
|
||||||
|
_netManager.RegisterNetMessage<MsgDeleteChatMessagesBy>();
|
||||||
|
|
||||||
_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
|
_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
|
||||||
_configurationManager.OnValueChanged(CCVars.AdminOocEnabled, OnAdminOocEnabledChanged, true);
|
_configurationManager.OnValueChanged(CCVars.AdminOocEnabled, OnAdminOocEnabledChanged, true);
|
||||||
@@ -74,6 +79,15 @@ namespace Content.Server.Chat.Managers
|
|||||||
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-admin-ooc-chat-enabled-message" : "chat-manager-admin-ooc-chat-disabled-message"));
|
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-admin-ooc-chat-enabled-message" : "chat-manager-admin-ooc-chat-disabled-message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DeleteMessagesBy(IPlayerSession player)
|
||||||
|
{
|
||||||
|
var key = SenderKeys.GetValueOrDefault(player);
|
||||||
|
var entities = SenderEntities.GetValueOrDefault(player) ?? new HashSet<NetEntity>();
|
||||||
|
var msg = new MsgDeleteChatMessagesBy { Key = key, Entities = entities };
|
||||||
|
|
||||||
|
_netManager.ServerSendToAll(msg);
|
||||||
|
}
|
||||||
|
|
||||||
#region Server Announcements
|
#region Server Announcements
|
||||||
|
|
||||||
public void DispatchServerAnnouncement(string message, Color? colorOverride = null)
|
public void DispatchServerAnnouncement(string message, Color? colorOverride = null)
|
||||||
@@ -202,8 +216,12 @@ namespace Content.Server.Chat.Managers
|
|||||||
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
|
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref var key = ref CollectionsMarshal.GetValueRefOrAddDefault(SenderKeys, player, out var exists);
|
||||||
|
if (!exists)
|
||||||
|
key = SenderKeys.Count;
|
||||||
|
|
||||||
//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
|
//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
|
||||||
ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride);
|
ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride, senderKey: key);
|
||||||
_mommiLink.SendOOCMessage(player.Name, message);
|
_mommiLink.SendOOCMessage(player.Name, message);
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"OOC from {player:Player}: {message}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"OOC from {player:Player}: {message}");
|
||||||
}
|
}
|
||||||
@@ -220,6 +238,11 @@ namespace Content.Server.Chat.Managers
|
|||||||
var wrappedMessage = Loc.GetString("chat-manager-send-admin-chat-wrap-message",
|
var wrappedMessage = Loc.GetString("chat-manager-send-admin-chat-wrap-message",
|
||||||
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
|
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
|
||||||
("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
|
("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
|
||||||
|
|
||||||
|
ref var key = ref CollectionsMarshal.GetValueRefOrAddDefault(SenderKeys, player, out var exists);
|
||||||
|
if (!exists)
|
||||||
|
key = SenderKeys.Count;
|
||||||
|
|
||||||
foreach (var client in clients)
|
foreach (var client in clients)
|
||||||
{
|
{
|
||||||
var isSource = client != player.ConnectedClient;
|
var isSource = client != player.ConnectedClient;
|
||||||
@@ -230,7 +253,7 @@ namespace Content.Server.Chat.Managers
|
|||||||
false,
|
false,
|
||||||
client,
|
client,
|
||||||
audioPath: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundPath) : default,
|
audioPath: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundPath) : default,
|
||||||
audioVolume: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundVolume) : default);
|
audioVolume: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundVolume) : default, senderKey: key);
|
||||||
}
|
}
|
||||||
|
|
||||||
_adminLogger.Add(LogType.Chat, $"Admin chat from {player:Player}: {message}");
|
_adminLogger.Add(LogType.Chat, $"Admin chat from {player:Player}: {message}");
|
||||||
@@ -240,9 +263,9 @@ namespace Content.Server.Chat.Managers
|
|||||||
|
|
||||||
#region Utility
|
#region Utility
|
||||||
|
|
||||||
public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0)
|
public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, int? senderKey = null)
|
||||||
{
|
{
|
||||||
var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), hideChat, colorOverride, audioPath, audioVolume);
|
var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), senderKey, hideChat, colorOverride, audioPath, audioVolume);
|
||||||
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);
|
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);
|
||||||
|
|
||||||
if (!recordReplay)
|
if (!recordReplay)
|
||||||
@@ -260,7 +283,7 @@ namespace Content.Server.Chat.Managers
|
|||||||
|
|
||||||
public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, List<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
|
public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, List<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
|
||||||
{
|
{
|
||||||
var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), hideChat, colorOverride, audioPath, audioVolume);
|
var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), null, hideChat, colorOverride, audioPath, audioVolume);
|
||||||
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients);
|
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients);
|
||||||
|
|
||||||
if (!recordReplay)
|
if (!recordReplay)
|
||||||
@@ -288,9 +311,9 @@ namespace Content.Server.Chat.Managers
|
|||||||
ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients, colorOverride, audioPath, audioVolume);
|
ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients, colorOverride, audioPath, audioVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
|
public void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, int? senderKey = null)
|
||||||
{
|
{
|
||||||
var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), hideChat, colorOverride, audioPath, audioVolume);
|
var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), senderKey, hideChat, colorOverride, audioPath, audioVolume);
|
||||||
_netManager.ServerSendToAll(new MsgChatMessage() { Message = msg });
|
_netManager.ServerSendToAll(new MsgChatMessage() { Message = msg });
|
||||||
|
|
||||||
if (!recordReplay)
|
if (!recordReplay)
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ namespace Content.Server.Chat.Managers
|
|||||||
{
|
{
|
||||||
public interface IChatManager
|
public interface IChatManager
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Keys identifying messages sent by a specific player, used when sending
|
||||||
|
/// <see cref="MsgChatMessage"/>
|
||||||
|
/// </summary>
|
||||||
|
Dictionary<IPlayerSession, int> SenderKeys { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tracks which entities a player was attached to while sending messages.
|
||||||
|
/// </summary>
|
||||||
|
Dictionary<IPlayerSession, HashSet<NetEntity>> SenderEntities { get; }
|
||||||
|
|
||||||
void Initialize();
|
void Initialize();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -27,15 +38,17 @@ namespace Content.Server.Chat.Managers
|
|||||||
void SendAdminAlert(EntityUid player, string message);
|
void SendAdminAlert(EntityUid player, string message);
|
||||||
|
|
||||||
void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat,
|
void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat,
|
||||||
INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0);
|
INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, int? senderKey = null);
|
||||||
|
|
||||||
void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay,
|
void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay,
|
||||||
IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0);
|
IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0);
|
||||||
|
|
||||||
void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride, string? audioPath = null, float audioVolume = 0);
|
void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride, string? audioPath = null, float audioVolume = 0);
|
||||||
|
|
||||||
void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0);
|
void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, int? senderKey = null);
|
||||||
|
|
||||||
bool MessageCharacterLimit(IPlayerSession player, string message);
|
bool MessageCharacterLimit(IPlayerSession player, string message);
|
||||||
|
|
||||||
|
void DeleteMessagesBy(IPlayerSession player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.Ghost;
|
using Content.Shared.Ghost;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Inventory;
|
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Radio;
|
using Content.Shared.Radio;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -33,6 +32,7 @@ using Robust.Shared.Utility;
|
|||||||
|
|
||||||
namespace Content.Server.Chat.Systems;
|
namespace Content.Server.Chat.Systems;
|
||||||
|
|
||||||
|
// TODO refactor whatever active warzone this class and chatmanager have become
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ChatSystem is responsible for in-simulation chat handling, such as whispering, speaking, emoting, etc.
|
/// ChatSystem is responsible for in-simulation chat handling, such as whispering, speaking, emoting, etc.
|
||||||
/// ChatSystem depends on ChatManager to actually send the messages.
|
/// ChatSystem depends on ChatManager to actually send the messages.
|
||||||
@@ -191,6 +191,9 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
if (!CanSendInGame(message, shell, player))
|
if (!CanSendInGame(message, shell, player))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
_chatManager.SenderEntities.GetOrNew(player).Add(GetNetEntity(source));
|
||||||
|
|
||||||
if (desiredType == InGameICChatType.Speak && message.StartsWith(LocalPrefix))
|
if (desiredType == InGameICChatType.Speak && message.StartsWith(LocalPrefix))
|
||||||
{
|
{
|
||||||
// prevent radios and remove prefix.
|
// prevent radios and remove prefix.
|
||||||
@@ -484,7 +487,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
_chatManager.ChatMessageToOne(ChatChannel.Whisper, obfuscatedMessage, wrappedUnknownMessage, source, false, session.ConnectedClient);
|
_chatManager.ChatMessageToOne(ChatChannel.Whisper, obfuscatedMessage, wrappedUnknownMessage, source, false, session.ConnectedClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
_replay.RecordServerMessage(new ChatMessage(ChatChannel.Whisper, message, wrappedMessage, GetNetEntity(source), MessageRangeHideChatForReplay(range)));
|
_replay.RecordServerMessage(new ChatMessage(ChatChannel.Whisper, message, wrappedMessage, GetNetEntity(source), null, MessageRangeHideChatForReplay(range)));
|
||||||
|
|
||||||
var ev = new EntitySpokeEvent(source, message, channel, obfuscatedMessage);
|
var ev = new EntitySpokeEvent(source, message, channel, obfuscatedMessage);
|
||||||
RaiseLocalEvent(source, ev, true);
|
RaiseLocalEvent(source, ev, true);
|
||||||
@@ -559,6 +562,8 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
("entityName", name),
|
("entityName", name),
|
||||||
("message", FormattedMessage.EscapeText(message)));
|
("message", FormattedMessage.EscapeText(message)));
|
||||||
|
|
||||||
|
_chatManager.SenderEntities.GetOrNew(player).Add(GetNetEntity(source));
|
||||||
|
|
||||||
SendInVoiceRange(ChatChannel.LOOC, message, wrappedMessage, source, hideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal);
|
SendInVoiceRange(ChatChannel.LOOC, message, wrappedMessage, source, hideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal);
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"LOOC from {player:Player}: {message}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"LOOC from {player:Player}: {message}");
|
||||||
}
|
}
|
||||||
@@ -585,8 +590,9 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_chatManager.ChatMessageToMany(ChatChannel.Dead, message, wrappedMessage, source, hideChat, true, clients.ToList());
|
_chatManager.SenderEntities.GetOrNew(player).Add(GetNetEntity(source));
|
||||||
|
|
||||||
|
_chatManager.ChatMessageToMany(ChatChannel.Dead, message, wrappedMessage, source, hideChat, true, clients.ToList());
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -651,7 +657,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
_chatManager.ChatMessageToOne(channel, message, wrappedMessage, source, entHideChat, session.ConnectedClient);
|
_chatManager.ChatMessageToOne(channel, message, wrappedMessage, source, entHideChat, session.ConnectedClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
_replay.RecordServerMessage(new ChatMessage(channel, message, wrappedMessage, GetNetEntity(source), MessageRangeHideChatForReplay(range)));
|
_replay.RecordServerMessage(new ChatMessage(channel, message, wrappedMessage, GetNetEntity(source), null, MessageRangeHideChatForReplay(range)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -893,4 +899,3 @@ public enum ChatTransmitRange : byte
|
|||||||
/// Ghosts can't hear or see it at all. Regular players can if in-range.
|
/// Ghosts can't hear or see it at all. Regular players can if in-range.
|
||||||
NoGhosts
|
NoGhosts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public sealed class CrewManifestSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
SubscribeLocalEvent<AfterGeneralRecordCreatedEvent>(AfterGeneralRecordCreated);
|
SubscribeLocalEvent<AfterGeneralRecordCreatedEvent>(AfterGeneralRecordCreated);
|
||||||
SubscribeLocalEvent<RecordModifiedEvent>(OnRecordModified);
|
SubscribeLocalEvent<RecordModifiedEvent>(OnRecordModified);
|
||||||
|
SubscribeLocalEvent<RecordRemovedEvent>(OnRecordRemoved);
|
||||||
SubscribeLocalEvent<CrewManifestViewerComponent, BoundUIClosedEvent>(OnBoundUiClose);
|
SubscribeLocalEvent<CrewManifestViewerComponent, BoundUIClosedEvent>(OnBoundUiClose);
|
||||||
SubscribeLocalEvent<CrewManifestViewerComponent, CrewManifestOpenUiMessage>(OpenEuiFromBui);
|
SubscribeLocalEvent<CrewManifestViewerComponent, CrewManifestOpenUiMessage>(OpenEuiFromBui);
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||||
@@ -83,6 +84,12 @@ public sealed class CrewManifestSystem : EntitySystem
|
|||||||
UpdateEuis(ev.Key.OriginStation);
|
UpdateEuis(ev.Key.OriginStation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRecordRemoved(RecordRemovedEvent ev)
|
||||||
|
{
|
||||||
|
BuildCrewManifest(ev.Key.OriginStation);
|
||||||
|
UpdateEuis(ev.Key.OriginStation);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnBoundUiClose(EntityUid uid, CrewManifestViewerComponent component, BoundUIClosedEvent ev)
|
private void OnBoundUiClose(EntityUid uid, CrewManifestViewerComponent component, BoundUIClosedEvent ev)
|
||||||
{
|
{
|
||||||
var owningStation = _stationSystem.GetOwningStation(uid);
|
var owningStation = _stationSystem.GetOwningStation(uid);
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Radio.Components;
|
using Content.Server.Radio.Components;
|
||||||
using Content.Server.VoiceMask;
|
using Content.Server.VoiceMask;
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Shared.Chat;
|
using Content.Shared.Chat;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Radio;
|
using Content.Shared.Radio;
|
||||||
|
using Content.Shared.Radio.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Replays;
|
using Robust.Shared.Replays;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Content.Shared.Radio.Components;
|
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
namespace Content.Server.Radio.EntitySystems;
|
namespace Content.Server.Radio.EntitySystems;
|
||||||
|
|
||||||
@@ -87,7 +85,8 @@ public sealed class RadioSystem : EntitySystem
|
|||||||
ChatChannel.Radio,
|
ChatChannel.Radio,
|
||||||
message,
|
message,
|
||||||
wrappedMessage,
|
wrappedMessage,
|
||||||
NetEntity.Invalid);
|
NetEntity.Invalid,
|
||||||
|
null);
|
||||||
var chatMsg = new MsgChatMessage { Message = chat };
|
var chatMsg = new MsgChatMessage { Message = chat };
|
||||||
var ev = new RadioReceiveEvent(message, messageSource, channel, chatMsg);
|
var ev = new RadioReceiveEvent(message, messageSource, channel, chatMsg);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Station.Systems;
|
using Content.Server.Station.Systems;
|
||||||
using Content.Shared.StationRecords;
|
using Content.Shared.StationRecords;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Content.Server.StationRecords.Systems;
|
namespace Content.Server.StationRecords.Systems;
|
||||||
|
|
||||||
@@ -18,6 +18,7 @@ public sealed class GeneralStationRecordConsoleSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, GeneralStationRecordsFilterMsg>(OnFiltersChanged);
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, GeneralStationRecordsFilterMsg>(OnFiltersChanged);
|
||||||
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, RecordModifiedEvent>(UpdateUserInterface);
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, RecordModifiedEvent>(UpdateUserInterface);
|
||||||
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, AfterGeneralRecordCreatedEvent>(UpdateUserInterface);
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, AfterGeneralRecordCreatedEvent>(UpdateUserInterface);
|
||||||
|
SubscribeLocalEvent<GeneralStationRecordConsoleComponent, RecordRemovedEvent>(UpdateUserInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateUserInterface<T>(EntityUid uid, GeneralStationRecordConsoleComponent component, T ev)
|
private void UpdateUserInterface<T>(EntityUid uid, GeneralStationRecordConsoleComponent component, T ev)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Server.GameTicking;
|
|
||||||
using Content.Server.Forensics;
|
using Content.Server.Forensics;
|
||||||
|
using Content.Server.GameTicking;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
@@ -160,8 +160,13 @@ public sealed class StationRecordsSystem : SharedStationRecordsSystem
|
|||||||
if (!Resolve(station, ref records))
|
if (!Resolve(station, ref records))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
RaiseLocalEvent(new RecordRemovedEvent(station, key));
|
if (records.Records.RemoveAllRecords(key))
|
||||||
return records.Records.RemoveAllRecords(key);
|
{
|
||||||
|
RaiseLocalEvent(new RecordRemovedEvent(station, key));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Net;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Eui;
|
using Content.Shared.Eui;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace Content.Shared.Administration;
|
namespace Content.Shared.Administration;
|
||||||
|
|
||||||
@@ -33,8 +32,9 @@ public static class BanPanelEuiStateMsg
|
|||||||
public string[]? Roles { get; set; }
|
public string[]? Roles { get; set; }
|
||||||
public bool UseLastIp { get; set; }
|
public bool UseLastIp { get; set; }
|
||||||
public bool UseLastHwid { get; set; }
|
public bool UseLastHwid { get; set; }
|
||||||
|
public bool Erase { get; set; }
|
||||||
|
|
||||||
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles)
|
public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles, bool erase)
|
||||||
{
|
{
|
||||||
Player = player;
|
Player = player;
|
||||||
IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}";
|
IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}";
|
||||||
@@ -45,6 +45,7 @@ public static class BanPanelEuiStateMsg
|
|||||||
Reason = reason;
|
Reason = reason;
|
||||||
Severity = severity;
|
Severity = severity;
|
||||||
Roles = roles;
|
Roles = roles;
|
||||||
|
Erase = erase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
using System.IO;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Lidgren.Network;
|
using Lidgren.Network;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Content.Shared.Chat
|
namespace Content.Shared.Chat
|
||||||
{
|
{
|
||||||
@@ -14,6 +14,14 @@ namespace Content.Shared.Chat
|
|||||||
public string Message;
|
public string Message;
|
||||||
public string WrappedMessage;
|
public string WrappedMessage;
|
||||||
public NetEntity SenderEntity;
|
public NetEntity SenderEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifier sent when <see cref="SenderEntity"/> is <see cref="NetEntity.Invalid"/>
|
||||||
|
/// if this was sent by a player to assign a key to the sender of this message.
|
||||||
|
/// This is unique per sender.
|
||||||
|
/// </summary>
|
||||||
|
public int? SenderKey;
|
||||||
|
|
||||||
public bool HideChat;
|
public bool HideChat;
|
||||||
public Color? MessageColorOverride;
|
public Color? MessageColorOverride;
|
||||||
public string? AudioPath;
|
public string? AudioPath;
|
||||||
@@ -22,12 +30,13 @@ namespace Content.Shared.Chat
|
|||||||
[NonSerialized]
|
[NonSerialized]
|
||||||
public bool Read;
|
public bool Read;
|
||||||
|
|
||||||
public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
|
public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, int? senderKey, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
|
||||||
{
|
{
|
||||||
Channel = channel;
|
Channel = channel;
|
||||||
Message = message;
|
Message = message;
|
||||||
WrappedMessage = wrappedMessage;
|
WrappedMessage = wrappedMessage;
|
||||||
SenderEntity = source;
|
SenderEntity = source;
|
||||||
|
SenderKey = senderKey;
|
||||||
HideChat = hideChat;
|
HideChat = hideChat;
|
||||||
MessageColorOverride = colorOverride;
|
MessageColorOverride = colorOverride;
|
||||||
AudioPath = audioPath;
|
AudioPath = audioPath;
|
||||||
|
|||||||
37
Content.Shared/Chat/MsgDeleteChatMessagesBy.cs
Normal file
37
Content.Shared/Chat/MsgDeleteChatMessagesBy.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using Lidgren.Network;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Chat;
|
||||||
|
|
||||||
|
public sealed class MsgDeleteChatMessagesBy : NetMessage
|
||||||
|
{
|
||||||
|
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||||
|
|
||||||
|
public int Key;
|
||||||
|
public HashSet<NetEntity> Entities = default!;
|
||||||
|
|
||||||
|
public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer)
|
||||||
|
{
|
||||||
|
Key = buffer.ReadInt32();
|
||||||
|
|
||||||
|
var entities = buffer.ReadInt32();
|
||||||
|
Entities = new HashSet<NetEntity>(entities);
|
||||||
|
|
||||||
|
for (var i = 0; i < entities; i++)
|
||||||
|
{
|
||||||
|
Entities.Add(buffer.ReadNetEntity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer)
|
||||||
|
{
|
||||||
|
buffer.Write(Key);
|
||||||
|
|
||||||
|
buffer.Write(Entities.Count);
|
||||||
|
foreach (var ent in Entities)
|
||||||
|
{
|
||||||
|
buffer.Write(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,3 +19,8 @@ Entries:
|
|||||||
- {message: 'Added total playtime to the F7 player list and the AHelp window title.', type: Add}
|
- {message: 'Added total playtime to the F7 player list and the AHelp window title.', type: Add}
|
||||||
id: 3
|
id: 3
|
||||||
time: '2023-10-14T08:55:00.0000000+00:00'
|
time: '2023-10-14T08:55:00.0000000+00:00'
|
||||||
|
- author: DrSmugleaf
|
||||||
|
changes:
|
||||||
|
- {message: 'Added admin Erase verb, add checkbox to erase from the ban panel.', type: Add}
|
||||||
|
id: 4
|
||||||
|
time: '2023-10-14T09:00:00.0000000+00:00'
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ admin-verbs-teleport-to = Teleport To
|
|||||||
admin-verbs-teleport-here = Teleport Here
|
admin-verbs-teleport-here = Teleport Here
|
||||||
admin-verbs-freeze = Freeze
|
admin-verbs-freeze = Freeze
|
||||||
admin-verbs-unfreeze = Unfreeze
|
admin-verbs-unfreeze = Unfreeze
|
||||||
|
admin-verbs-erase = Erase
|
||||||
toolshed-verb-mark = Mark
|
toolshed-verb-mark = Mark
|
||||||
toolshed-verb-mark-description = Places this entity into the $marked variable, a list of entities, replacing it's prior value.
|
toolshed-verb-mark-description = Places this entity into the $marked variable, a list of entities, replacing it's prior value.
|
||||||
|
|||||||
1
Resources/Locale/en-US/administration/ui/admin-erase.ftl
Normal file
1
Resources/Locale/en-US/administration/ui/admin-erase.ftl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
admin-erase-popup = {$user} disappears without a trace. You should keep playing as if they never existed.
|
||||||
@@ -76,6 +76,7 @@ ban-panel-years = Years
|
|||||||
ban-panel-permanent = Permanent
|
ban-panel-permanent = Permanent
|
||||||
ban-panel-ip-hwid-tooltip = Leave empty and check the checkbox below to use last connection's details
|
ban-panel-ip-hwid-tooltip = Leave empty and check the checkbox below to use last connection's details
|
||||||
ban-panel-severity = Severity:
|
ban-panel-severity = Severity:
|
||||||
|
ban-panel-erase = Erase chat messages and player from round
|
||||||
|
|
||||||
# Ban string
|
# Ban string
|
||||||
server-ban-string = {$admin} created a {$severity} severity server ban that expires {$expires} for [{$name}, {$ip}, {$hwid}], with reason: {$reason}
|
server-ban-string = {$admin} created a {$severity} severity server ban that expires {$expires} for [{$name}, {$ip}, {$hwid}], with reason: {$reason}
|
||||||
|
|||||||
Reference in New Issue
Block a user