2021-11-10 13:26:25 +01:00
using System.Linq ;
using Content.Server.Administration.Managers ;
2023-10-13 11:56:12 -07:00
using Content.Server.Chat.Managers ;
2023-10-14 02:02:56 -07:00
using Content.Server.GameTicking ;
using Content.Server.Hands.Systems ;
2023-08-28 16:53:24 -07:00
using Content.Server.Mind ;
2023-10-14 01:55:40 -07:00
using Content.Server.Players.PlayTimeTracking ;
2023-10-14 02:02:56 -07:00
using Content.Server.Popups ;
using Content.Server.StationRecords.Systems ;
2021-11-10 13:26:25 +01:00
using Content.Shared.Administration ;
using Content.Shared.Administration.Events ;
2023-10-13 11:56:12 -07:00
using Content.Shared.CCVar ;
2025-02-09 23:36:01 -04:00
using Content.Shared.Forensics.Components ;
2022-10-16 10:26:29 +13:00
using Content.Shared.GameTicking ;
2023-10-14 02:02:56 -07:00
using Content.Shared.Hands.Components ;
2022-07-10 18:36:53 -07:00
using Content.Shared.IdentityManagement ;
2023-10-14 02:02:56 -07:00
using Content.Shared.Inventory ;
2024-08-31 11:38:03 +00:00
using Content.Shared.Mind ;
2023-10-14 02:02:56 -07:00
using Content.Shared.PDA ;
2023-10-14 01:55:40 -07:00
using Content.Shared.Players.PlayTimeTracking ;
2023-10-14 02:02:56 -07:00
using Content.Shared.Popups ;
2023-08-30 21:46:11 -07:00
using Content.Shared.Roles ;
2025-08-13 12:51:46 +02:00
using Content.Shared.Roles.Components ;
2023-08-30 21:46:11 -07:00
using Content.Shared.Roles.Jobs ;
2023-10-14 02:02:56 -07:00
using Content.Shared.StationRecords ;
using Content.Shared.Throwing ;
2021-11-10 13:26:25 +01:00
using Robust.Server.GameObjects ;
using Robust.Server.Player ;
2023-10-14 02:53:02 -07:00
using Robust.Shared.Audio ;
2023-11-27 22:12:34 +11:00
using Robust.Shared.Audio.Systems ;
2023-10-13 11:56:12 -07:00
using Robust.Shared.Configuration ;
2021-11-10 13:26:25 +01:00
using Robust.Shared.Enums ;
2021-12-30 07:12:28 -08:00
using Robust.Shared.Network ;
2023-08-30 21:46:11 -07:00
using Robust.Shared.Player ;
2025-01-11 22:17:26 +01:00
using Robust.Shared.Prototypes ;
2021-11-10 13:26:25 +01:00
2024-08-15 20:26:57 +02:00
namespace Content.Server.Administration.Systems ;
public sealed class AdminSystem : EntitySystem
2021-11-10 13:26:25 +01:00
{
2024-08-15 20:26:57 +02:00
[Dependency] private readonly IAdminManager _adminManager = default ! ;
[Dependency] private readonly IChatManager _chat = default ! ;
[Dependency] private readonly IConfigurationManager _config = default ! ;
[Dependency] private readonly IPlayerManager _playerManager = default ! ;
[Dependency] private readonly HandsSystem _hands = default ! ;
[Dependency] private readonly SharedJobSystem _jobs = default ! ;
[Dependency] private readonly InventorySystem _inventory = 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 ! ;
2025-01-11 22:17:26 +01:00
[Dependency] private readonly IPrototypeManager _proto = default ! ;
2024-08-15 20:26:57 +02:00
[Dependency] private readonly SharedRoleSystem _role = default ! ;
[Dependency] private readonly GameTicker _gameTicker = default ! ;
[Dependency] private readonly SharedAudioSystem _audio = default ! ;
[Dependency] private readonly StationRecordsSystem _stationRecords = default ! ;
[Dependency] private readonly TransformSystem _transform = default ! ;
private readonly Dictionary < NetUserId , PlayerInfo > _playerList = new ( ) ;
/// <summary>
/// Set of players that have participated in this round.
/// </summary>
public IReadOnlySet < NetUserId > RoundActivePlayers = > _roundActivePlayers ;
private readonly HashSet < NetUserId > _roundActivePlayers = new ( ) ;
public readonly PanicBunkerStatus PanicBunker = new ( ) ;
public override void Initialize ( )
2021-11-10 13:26:25 +01:00
{
2024-08-15 20:26:57 +02:00
base . Initialize ( ) ;
_playerManager . PlayerStatusChanged + = OnPlayerStatusChanged ;
_adminManager . OnPermsChanged + = OnAdminPermsChanged ;
_playTime . SessionPlayTimeUpdated + = OnSessionPlayTimeUpdated ;
// Panic Bunker Settings
Subs . CVar ( _config , CCVars . PanicBunkerEnabled , OnPanicBunkerChanged , true ) ;
Subs . CVar ( _config , CCVars . PanicBunkerDisableWithAdmins , OnPanicBunkerDisableWithAdminsChanged , true ) ;
Subs . CVar ( _config , CCVars . PanicBunkerEnableWithoutAdmins , OnPanicBunkerEnableWithoutAdminsChanged , true ) ;
Subs . CVar ( _config , CCVars . PanicBunkerCountDeadminnedAdmins , OnPanicBunkerCountDeadminnedAdminsChanged , true ) ;
Subs . CVar ( _config , CCVars . PanicBunkerShowReason , OnPanicBunkerShowReasonChanged , true ) ;
Subs . CVar ( _config , CCVars . PanicBunkerMinAccountAge , OnPanicBunkerMinAccountAgeChanged , true ) ;
Subs . CVar ( _config , CCVars . PanicBunkerMinOverallMinutes , OnPanicBunkerMinOverallMinutesChanged , true ) ;
SubscribeLocalEvent < PlayerAttachedEvent > ( OnPlayerAttached ) ;
SubscribeLocalEvent < PlayerDetachedEvent > ( OnPlayerDetached ) ;
SubscribeLocalEvent < RoleAddedEvent > ( OnRoleEvent ) ;
SubscribeLocalEvent < RoleRemovedEvent > ( OnRoleEvent ) ;
SubscribeLocalEvent < RoundRestartCleanupEvent > ( OnRoundRestartCleanup ) ;
2025-01-21 23:48:07 +01:00
2024-09-15 01:55:03 +00:00
SubscribeLocalEvent < ActorComponent , EntityRenamedEvent > ( OnPlayerRenamed ) ;
2025-01-21 23:48:07 +01:00
SubscribeLocalEvent < ActorComponent , IdentityChangedEvent > ( OnIdentityChanged ) ;
2024-08-15 20:26:57 +02:00
}
private void OnRoundRestartCleanup ( RoundRestartCleanupEvent ev )
{
_roundActivePlayers . Clear ( ) ;
foreach ( var ( id , data ) in _playerList )
2021-11-10 13:26:25 +01:00
{
2024-08-15 20:26:57 +02:00
if ( ! data . ActiveThisRound )
continue ;
if ( ! _playerManager . TryGetPlayerData ( id , out var playerData ) )
return ;
_playerManager . TryGetSessionById ( id , out var session ) ;
_playerList [ id ] = GetPlayerInfo ( playerData , session ) ;
2022-10-16 10:26:29 +13:00
}
2024-08-15 20:26:57 +02:00
var updateEv = new FullPlayerListEvent ( ) { PlayersInfo = _playerList . Values . ToList ( ) } ;
foreach ( var admin in _adminManager . ActiveAdmins )
2022-10-16 10:26:29 +13:00
{
2024-08-15 20:26:57 +02:00
RaiseNetworkEvent ( updateEv , admin . Channel ) ;
}
}
2022-10-16 10:26:29 +13:00
2024-09-15 01:55:03 +00:00
private void OnPlayerRenamed ( Entity < ActorComponent > ent , ref EntityRenamedEvent args )
{
UpdatePlayerList ( ent . Comp . PlayerSession ) ;
}
2024-08-15 20:26:57 +02:00
public void UpdatePlayerList ( ICommonSession player )
{
_playerList [ player . UserId ] = GetPlayerInfo ( player . Data , player ) ;
2022-10-16 10:26:29 +13:00
2024-08-15 20:26:57 +02:00
var playerInfoChangedEvent = new PlayerInfoChangedEvent
{
PlayerInfo = _playerList [ player . UserId ]
} ;
2022-10-16 10:26:29 +13:00
2024-08-15 20:26:57 +02:00
foreach ( var admin in _adminManager . ActiveAdmins )
{
RaiseNetworkEvent ( playerInfoChangedEvent , admin . Channel ) ;
}
}
2022-10-16 10:26:29 +13:00
2024-08-15 20:26:57 +02:00
public PlayerInfo ? GetCachedPlayerInfo ( NetUserId ? netUserId )
{
if ( netUserId = = null )
return null ;
2022-10-16 10:26:29 +13:00
2024-08-15 20:26:57 +02:00
_playerList . TryGetValue ( netUserId . Value , out var value ) ;
return value ? ? null ;
}
2021-11-12 19:46:05 +01:00
2025-01-21 23:48:07 +01:00
private void OnIdentityChanged ( Entity < ActorComponent > ent , ref IdentityChangedEvent ev )
2024-08-15 20:26:57 +02:00
{
2025-01-21 23:48:07 +01:00
UpdatePlayerList ( ent . Comp . PlayerSession ) ;
2024-08-15 20:26:57 +02:00
}
2021-12-30 07:12:28 -08:00
2024-08-15 20:26:57 +02:00
private void OnRoleEvent ( RoleEvent ev )
{
2025-04-19 00:23:01 +02:00
if ( ! ev . RoleTypeUpdate | | ! _playerManager . TryGetSessionById ( ev . Mind . UserId , out var session ) )
2024-08-15 20:26:57 +02:00
return ;
2021-11-10 13:26:25 +01:00
2024-08-15 20:26:57 +02:00
UpdatePlayerList ( session ) ;
}
2023-03-23 10:10:49 -05:00
2024-08-15 20:26:57 +02:00
private void OnAdminPermsChanged ( AdminPermsChangedEventArgs obj )
{
UpdatePanicBunker ( ) ;
2023-03-23 10:10:49 -05:00
2024-08-15 20:26:57 +02:00
if ( ! obj . IsAdmin )
2022-07-10 18:36:53 -07:00
{
2024-08-15 20:26:57 +02:00
RaiseNetworkEvent ( new FullPlayerListEvent ( ) , obj . Player . Channel ) ;
return ;
2022-07-10 18:36:53 -07:00
}
2024-08-15 20:26:57 +02:00
SendFullPlayerList ( obj . Player ) ;
}
2021-12-30 07:12:28 -08:00
2024-08-15 20:26:57 +02:00
private void OnPlayerDetached ( PlayerDetachedEvent ev )
{
// If disconnected then the player won't have a connected entity to get character name from.
// The disconnected state gets sent by OnPlayerStatusChanged.
if ( ev . Player . Status = = SessionStatus . Disconnected )
return ;
2021-12-30 07:12:28 -08:00
2024-08-15 20:26:57 +02:00
UpdatePlayerList ( ev . Player ) ;
}
2023-10-13 11:56:12 -07:00
2024-08-15 20:26:57 +02:00
private void OnPlayerAttached ( PlayerAttachedEvent ev )
{
if ( ev . Player . Status = = SessionStatus . Disconnected )
return ;
2021-11-12 19:46:05 +01:00
2024-08-15 20:26:57 +02:00
_roundActivePlayers . Add ( ev . Player . UserId ) ;
UpdatePlayerList ( ev . Player ) ;
}
2021-11-11 00:03:03 +01:00
2024-08-15 20:26:57 +02:00
public override void Shutdown ( )
{
base . Shutdown ( ) ;
_playerManager . PlayerStatusChanged - = OnPlayerStatusChanged ;
_adminManager . OnPermsChanged - = OnAdminPermsChanged ;
_playTime . SessionPlayTimeUpdated - = OnSessionPlayTimeUpdated ;
}
2021-11-10 20:38:18 +01:00
2024-08-15 20:26:57 +02:00
private void OnPlayerStatusChanged ( object? sender , SessionStatusEventArgs e )
{
UpdatePlayerList ( e . Session ) ;
UpdatePanicBunker ( ) ;
}
2021-11-10 13:26:25 +01:00
2024-08-15 20:26:57 +02:00
private void SendFullPlayerList ( ICommonSession playerSession )
{
var ev = new FullPlayerListEvent ( ) ;
2021-11-10 20:38:18 +01:00
2024-08-15 20:26:57 +02:00
ev . PlayersInfo = _playerList . Values . ToList ( ) ;
RaiseNetworkEvent ( ev , playerSession . Channel ) ;
}
2021-11-10 13:26:25 +01:00
2024-08-15 20:26:57 +02:00
private PlayerInfo GetPlayerInfo ( SessionData data , ICommonSession ? session )
{
var name = data . UserName ;
var entityName = string . Empty ;
var identityName = string . Empty ;
2025-03-25 17:03:59 +01:00
var sortWeight = 0 ;
2024-08-15 20:26:57 +02:00
2025-03-05 17:25:42 +01:00
// Visible (identity) name can be different from real name
2024-08-15 20:26:57 +02:00
if ( session ? . AttachedEntity ! = null )
2021-11-10 13:26:25 +01:00
{
2025-06-26 19:50:49 -04:00
entityName = Comp < MetaDataComponent > ( session . AttachedEntity . Value ) . EntityName ;
2024-08-15 20:26:57 +02:00
identityName = Identity . Name ( session . AttachedEntity . Value , EntityManager ) ;
2021-11-10 13:26:25 +01:00
}
2024-08-15 20:26:57 +02:00
var antag = false ;
2025-01-11 22:17:26 +01:00
2025-03-05 17:25:42 +01:00
// Starting role, antagonist status and role type
2025-06-27 01:27:23 +02:00
RoleTypePrototype ? roleType = null ;
2024-08-15 20:26:57 +02:00
var startingRole = string . Empty ;
2025-04-16 19:04:48 +02:00
LocId ? subtype = null ;
2025-03-25 17:03:59 +01:00
if ( _minds . TryGetMind ( session , out var mindId , out var mindComp ) & & mindComp is not null )
2021-11-10 13:26:25 +01:00
{
2025-03-25 17:03:59 +01:00
sortWeight = _role . GetRoleCompByTime ( mindComp ) ? . Comp . SortWeight ? ? 0 ;
2025-01-11 22:17:26 +01:00
if ( _proto . TryIndex ( mindComp . RoleType , out var role ) )
2025-04-16 19:04:48 +02:00
{
2025-01-11 22:17:26 +01:00
roleType = role ;
2025-04-16 19:04:48 +02:00
subtype = mindComp . Subtype ;
}
2025-01-11 22:17:26 +01:00
else
2025-06-27 01:27:23 +02:00
Log . Error ( $"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(RoleTypePrototype.FallbackName)}' instead" ) ;
2025-01-11 22:17:26 +01:00
2024-08-15 20:26:57 +02:00
antag = _role . MindIsAntagonist ( mindId ) ;
startingRole = _jobs . MindTryGetJobName ( mindId ) ;
2021-11-10 13:26:25 +01:00
}
2025-03-05 17:25:42 +01:00
// Connection status and playtime
2024-08-15 20:26:57 +02:00
var connected = session ! = null & & session . Status is SessionStatus . Connected or SessionStatus . InGame ;
2025-03-05 17:25:42 +01:00
// Start with the last available playtime data
var cachedInfo = GetCachedPlayerInfo ( data . UserId ) ;
var overallPlaytime = cachedInfo ? . OverallPlaytime ;
// Overwrite with current playtime data, unless it's null (such as if the player just disconnected)
2024-08-15 20:26:57 +02:00
if ( session ! = null & &
_playTime . TryGetTrackerTimes ( session , out var playTimes ) & &
playTimes . TryGetValue ( PlayTimeTrackingShared . TrackerOverall , out var playTime ) )
2021-11-10 13:26:25 +01:00
{
2024-08-15 20:26:57 +02:00
overallPlaytime = playTime ;
2021-11-10 13:26:25 +01:00
}
2021-11-11 00:03:03 +01:00
2025-03-25 17:03:59 +01:00
return new PlayerInfo (
name ,
entityName ,
identityName ,
startingRole ,
antag ,
2025-06-27 01:27:23 +02:00
roleType ? . ID ,
2025-04-16 19:04:48 +02:00
subtype ,
2025-03-25 17:03:59 +01:00
sortWeight ,
GetNetEntity ( session ? . AttachedEntity ) ,
data . UserId ,
connected ,
_roundActivePlayers . Contains ( data . UserId ) ,
overallPlaytime ) ;
2024-08-15 20:26:57 +02:00
}
2021-12-03 15:09:45 +01:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerChanged ( bool enabled )
{
PanicBunker . Enabled = enabled ;
_chat . SendAdminAlert ( Loc . GetString ( enabled
? "admin-ui-panic-bunker-enabled-admin-alert"
: "admin-ui-panic-bunker-disabled-admin-alert"
) ) ;
2021-12-03 15:09:45 +01:00
2024-08-15 20:26:57 +02:00
SendPanicBunkerStatusAll ( ) ;
}
2021-11-11 00:03:03 +01:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerDisableWithAdminsChanged ( bool enabled )
{
PanicBunker . DisableWithAdmins = enabled ;
UpdatePanicBunker ( ) ;
}
2023-10-13 11:56:12 -07:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerEnableWithoutAdminsChanged ( bool enabled )
{
PanicBunker . EnableWithoutAdmins = enabled ;
UpdatePanicBunker ( ) ;
}
2023-10-13 11:56:12 -07:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerCountDeadminnedAdminsChanged ( bool enabled )
{
PanicBunker . CountDeadminnedAdmins = enabled ;
UpdatePanicBunker ( ) ;
}
2024-07-11 00:14:01 -05:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerShowReasonChanged ( bool enabled )
{
PanicBunker . ShowReason = enabled ;
SendPanicBunkerStatusAll ( ) ;
}
2024-07-11 00:14:01 -05:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerMinAccountAgeChanged ( int minutes )
{
PanicBunker . MinAccountAgeMinutes = minutes ;
SendPanicBunkerStatusAll ( ) ;
}
2023-10-13 11:56:12 -07:00
2024-08-15 20:26:57 +02:00
private void OnPanicBunkerMinOverallMinutesChanged ( int minutes )
{
PanicBunker . MinOverallMinutes = minutes ;
SendPanicBunkerStatusAll ( ) ;
}
2023-10-13 11:56:12 -07:00
2024-08-15 20:26:57 +02:00
private void UpdatePanicBunker ( )
{
2024-12-18 14:15:04 +01:00
var hasAdmins = false ;
foreach ( var admin in _adminManager . AllAdmins )
{
if ( _adminManager . HasAdminFlag ( admin , AdminFlags . Admin , includeDeAdmin : PanicBunker . CountDeadminnedAdmins ) )
{
hasAdmins = true ;
break ;
}
}
2024-08-15 20:26:57 +02:00
// TODO Fix order dependent Cvars
// Please for the sake of my sanity don't make cvars & order dependent.
// Just make a bool field on the system instead of having some cvars automatically modify other cvars.
//
// I.e., this:
// /sudo cvar game.panic_bunker.enabled true
// /sudo cvar game.panic_bunker.disable_with_admins true
// and this:
// /sudo cvar game.panic_bunker.disable_with_admins true
// /sudo cvar game.panic_bunker.enabled true
//
// should have the same effect, but currently setting the disable_with_admins can modify enabled.
if ( hasAdmins & & PanicBunker . DisableWithAdmins )
2023-10-13 11:56:12 -07:00
{
2024-08-15 20:26:57 +02:00
_config . SetCVar ( CCVars . PanicBunkerEnabled , false ) ;
2023-10-13 11:56:12 -07:00
}
2024-08-15 20:26:57 +02:00
else if ( ! hasAdmins & & PanicBunker . EnableWithoutAdmins )
2024-07-11 00:14:01 -05:00
{
2024-08-15 20:26:57 +02:00
_config . SetCVar ( CCVars . PanicBunkerEnabled , true ) ;
2024-07-11 00:14:01 -05:00
}
2024-08-15 20:26:57 +02:00
SendPanicBunkerStatusAll ( ) ;
}
2023-10-13 11:56:12 -07:00
2024-08-15 20:26:57 +02:00
private void SendPanicBunkerStatusAll ( )
{
var ev = new PanicBunkerChangedEvent ( PanicBunker ) ;
foreach ( var admin in _adminManager . AllAdmins )
2024-07-11 00:14:01 -05:00
{
2024-08-15 20:26:57 +02:00
RaiseNetworkEvent ( ev , admin ) ;
2024-07-11 00:14:01 -05:00
}
2024-08-15 20:26:57 +02:00
}
2024-07-11 00:14:01 -05:00
2024-08-31 11:38:03 +00:00
/// <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 ( NetUserId uid )
2024-07-11 00:14:01 -05:00
{
2024-08-31 11:38:03 +00:00
_chat . DeleteMessagesBy ( uid ) ;
2025-07-09 21:04:57 +02:00
var eraseEvent = new EraseEvent ( uid ) ;
2024-08-31 11:38:03 +00:00
if ( ! _minds . TryGetMind ( uid , out var mindId , out var mind ) | | mind . OwnedEntity = = null | | TerminatingOrDeleted ( mind . OwnedEntity . Value ) )
2025-07-09 21:04:57 +02:00
{
RaiseLocalEvent ( ref eraseEvent ) ;
2024-08-31 11:38:03 +00:00
return ;
2025-07-09 21:04:57 +02:00
}
2024-08-31 11:38:03 +00:00
var entity = mind . OwnedEntity . Value ;
if ( TryComp ( entity , out TransformComponent ? transform ) )
2024-07-11 00:14:01 -05:00
{
2024-08-31 11:38:03 +00:00
var coordinates = _transform . GetMoverCoordinates ( entity , transform ) ;
var name = Identity . Entity ( entity , EntityManager ) ;
2024-08-15 20:26:57 +02:00
_popup . PopupCoordinates ( Loc . GetString ( "admin-erase-popup" , ( "user" , name ) ) , coordinates , PopupType . LargeCaution ) ;
var filter = Filter . Pvs ( coordinates , 1 , EntityManager , _playerManager ) ;
var audioParams = new AudioParams ( ) . WithVolume ( 3 ) ;
_audio . PlayStatic ( "/Audio/Effects/pop_high.ogg" , filter , coordinates , true , audioParams ) ;
2024-07-11 00:14:01 -05:00
}
2023-10-14 02:02:56 -07:00
2024-08-31 11:38:03 +00:00
foreach ( var item in _inventory . GetHandOrInventoryEntities ( entity ) )
2023-10-14 02:02:56 -07:00
{
2024-08-15 20:26:57 +02:00
if ( TryComp ( item , out PdaComponent ? pda ) & &
TryComp ( pda . ContainedId , out StationRecordKeyStorageComponent ? keyStorage ) & &
keyStorage . Key is { } key & &
_stationRecords . TryGetRecord ( key , out GeneralStationRecord ? record ) )
2023-10-14 02:02:56 -07:00
{
2024-08-15 20:26:57 +02:00
if ( TryComp ( entity , out DnaComponent ? dna ) & &
dna . DNA ! = record . DNA )
{
continue ;
}
2023-10-14 02:02:56 -07:00
2024-08-15 20:26:57 +02:00
if ( TryComp ( entity , out FingerprintComponent ? fingerPrint ) & &
fingerPrint . Fingerprint ! = record . Fingerprint )
2023-10-14 02:02:56 -07:00
{
2024-08-15 20:26:57 +02:00
continue ;
2023-10-14 02:02:56 -07:00
}
2024-08-15 20:26:57 +02:00
_stationRecords . RemoveRecord ( key ) ;
Del ( item ) ;
2023-10-14 02:02:56 -07:00
}
2024-08-15 20:26:57 +02:00
}
2023-10-14 02:02:56 -07:00
2024-08-31 11:38:03 +00:00
if ( _inventory . TryGetContainerSlotEnumerator ( entity , out var enumerator ) )
2024-08-15 20:26:57 +02:00
{
while ( enumerator . NextItem ( out var item , out var slot ) )
2023-10-14 02:02:56 -07:00
{
2024-08-31 11:38:03 +00:00
if ( _inventory . TryUnequip ( entity , entity , slot . Name , true , true ) )
2024-08-15 20:26:57 +02:00
_physics . ApplyAngularImpulse ( item , ThrowingSystem . ThrowAngularImpulse ) ;
2023-10-14 02:02:56 -07:00
}
2024-08-15 20:26:57 +02:00
}
2023-10-14 02:02:56 -07:00
2024-08-31 11:38:03 +00:00
if ( TryComp ( entity , out HandsComponent ? hands ) )
2024-08-15 20:26:57 +02:00
{
2025-06-25 09:13:03 -04:00
foreach ( var hand in _hands . EnumerateHands ( ( entity , hands ) ) )
2023-10-14 02:02:56 -07:00
{
2025-06-25 09:13:03 -04:00
_hands . TryDrop ( ( entity , hands ) , hand , checkActionBlocker : false , doDropInteraction : false ) ;
2023-10-14 02:02:56 -07:00
}
}
2024-08-31 11:38:03 +00:00
_minds . WipeMind ( mindId , mind ) ;
QueueDel ( entity ) ;
2023-10-14 02:02:56 -07:00
2024-08-31 11:38:03 +00:00
if ( _playerManager . TryGetSessionById ( uid , out var session ) )
_gameTicker . SpawnObserver ( session ) ;
2025-07-09 21:04:57 +02:00
RaiseLocalEvent ( ref eraseEvent ) ;
2024-08-31 11:38:03 +00:00
}
2024-06-12 23:38:43 -07:00
2024-08-15 20:26:57 +02:00
private void OnSessionPlayTimeUpdated ( ICommonSession session )
{
UpdatePlayerList ( session ) ;
2021-11-10 13:26:25 +01:00
}
}
2025-07-09 21:04:57 +02:00
/// <summary>
/// Event fired after a player is erased by an admin
/// </summary>
/// <param name="PlayerNetUserId">NetUserId of the player that was the target of the Erase</param>
[ByRefEvent]
public record struct EraseEvent ( NetUserId PlayerNetUserId ) ;