diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs index b12c90e16e..cebbed8ee8 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs @@ -130,14 +130,14 @@ public sealed partial class MindTests ActorComponent actor = default!; Assert.Multiple(() => { - Assert.That(player, Is.EqualTo(mind.Session), "Player session does not match mind session"); + Assert.That(player.UserId, Is.EqualTo(mind.UserId), "Player UserId does not match mind UserId"); Assert.That(entMan.System().GetMind(player.UserId), Is.EqualTo(mindId)); Assert.That(player.AttachedEntity, Is.EqualTo(mind.CurrentEntity), "Player is not attached to the mind's current entity."); Assert.That(entMan.EntityExists(mind.OwnedEntity), "The mind's current entity does not exist"); Assert.That(mind.VisitingEntity == null || entMan.EntityExists(mind.VisitingEntity), "The minds visited entity does not exist."); Assert.That(entMan.TryGetComponent(mind.CurrentEntity, out actor)); }); - Assert.That(actor.PlayerSession, Is.EqualTo(mind.Session)); + Assert.That(actor.PlayerSession.UserId, Is.EqualTo(mind.UserId)); return (mindId, mind); } @@ -161,7 +161,6 @@ public sealed partial class MindTests { Assert.That(player.Status, Is.EqualTo(SessionStatus.Disconnected)); Assert.That(mind.UserId, Is.Not.Null); - Assert.That(mind.Session, Is.Null); }); } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs index a1fccfb68d..db87797553 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs @@ -67,7 +67,7 @@ public sealed partial class MindTests Assert.Multiple(() => { Assert.That(entMan.Deleted(entity)); - Assert.That(mind.Comp.OwnedEntity, Is.Null); + Assert.That(mind.Comp.OwnedEntity, Is.Not.EqualTo(entity)); }); // Reconnect diff --git a/Content.Server/Administration/Commands/AGhostCommand.cs b/Content.Server/Administration/Commands/AGhostCommand.cs index 6acaadbf83..09c8d0eb50 100644 --- a/Content.Server/Administration/Commands/AGhostCommand.cs +++ b/Content.Server/Administration/Commands/AGhostCommand.cs @@ -8,6 +8,7 @@ using Content.Shared.Mind; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Console; +using Robust.Shared.Player; namespace Content.Server.Administration.Commands; @@ -15,7 +16,7 @@ namespace Content.Server.Administration.Commands; public sealed class AGhostCommand : LocalizedCommands { [Dependency] private readonly IEntityManager _entities = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; public override string Command => "aghost"; public override string Help => "aghost"; @@ -104,8 +105,8 @@ public sealed class AGhostCommand : LocalizedCommands // TODO: Remove duplication between all this and "GamePreset.OnGhostAttempt()"... if (!string.IsNullOrWhiteSpace(mind.CharacterName)) metaDataSystem.SetEntityName(ghost, mind.CharacterName); - else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) - metaDataSystem.SetEntityName(ghost, mind.Session.Name); + else if (!string.IsNullOrWhiteSpace(player.Name)) + metaDataSystem.SetEntityName(ghost, player.Name); mindSystem.Visit(mindId, ghost, mind); } diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 0868157f6e..b9916bc5b2 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -153,9 +153,7 @@ public sealed class AdminSystem : EntitySystem private void OnRoleEvent(RoleEvent ev) { - var session = _minds.GetSession(ev.Mind); - - if (!ev.RoleTypeUpdate || session == null) + if (!ev.RoleTypeUpdate || !_playerManager.TryGetSessionById(ev.Mind.UserId, out var session)) return; UpdatePlayerList(session); diff --git a/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs b/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs index b647e026e3..ff644ad3e0 100644 --- a/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs +++ b/Content.Server/Anomaly/Effects/InnerBodyAnomalySystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Popups; using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Physics.Events; +using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Anomaly.Effects; @@ -26,6 +27,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly BodySystem _body = default!; [Dependency] private readonly IChatManager _chat = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly JitteringSystem _jitter = default!; [Dependency] private readonly MindSystem _mind = default!; @@ -102,7 +104,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem if (ent.Comp.StartMessage is not null && _mind.TryGetMind(ent, out _, out var mindComponent) && - mindComponent.Session != null) + _player.TryGetSessionById(mindComponent.UserId, out var session)) { var message = Loc.GetString(ent.Comp.StartMessage); var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message)); @@ -111,7 +113,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem wrappedMessage, default, false, - mindComponent.Session.Channel, + session.Channel, _messageColor); _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution); @@ -137,7 +139,8 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem private void OnSeverityChanged(Entity ent, ref AnomalySeverityChangedEvent args) { - if (!_mind.TryGetMind(ent, out _, out var mindComponent) || mindComponent.Session == null) + if (!_mind.TryGetMind(ent, out _, out var mindComponent) || + !_player.TryGetSessionById(mindComponent.UserId, out var session)) return; var message = string.Empty; @@ -172,7 +175,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem wrappedMessage, default, false, - mindComponent.Session.Channel, + session.Channel, _messageColor); _popup.PopupEntity(message, ent, ent, PopupType.MediumCaution); @@ -214,7 +217,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem if (ent.Comp.EndMessage is not null && _mind.TryGetMind(ent, out _, out var mindComponent) && - mindComponent.Session != null) + _player.TryGetSessionById(mindComponent.UserId, out var session)) { var message = Loc.GetString(ent.Comp.EndMessage); var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message)); @@ -223,7 +226,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem wrappedMessage, default, false, - mindComponent.Session.Channel, + session.Channel, _messageColor); diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index 93b5fa6136..975c802eed 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -264,10 +264,10 @@ public sealed partial class AntagSelectionSystem if (!_mind.TryGetMind(entity, out _, out var mindComponent)) return; - if (mindComponent.Session == null) + if (!_playerManager.TryGetSessionById(mindComponent.UserId, out var session)) return; - SendBriefing(mindComponent.Session, briefing, briefingColor, briefingSound); + SendBriefing(session, briefing, briefingColor, briefingSound); } /// diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index c86ff802ce..535ca8658d 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -44,6 +44,7 @@ internal sealed partial class ChatManager : IChatManager [Dependency] private readonly INetConfigurationManager _netConfigManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly PlayerRateLimitManager _rateLimitManager = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; /// /// The maximum length a player-sent message can be sent @@ -179,7 +180,12 @@ internal sealed partial class ChatManager : IChatManager var adminSystem = _entityManager.System(); var antag = mind.UserId != null && (adminSystem.GetCachedPlayerInfo(mind.UserId.Value)?.Antag ?? false); - SendAdminAlert($"{mind.Session?.Name}{(antag ? " (ANTAG)" : "")} {message}"); + // We shouldn't be repeating this but I don't want to touch any more chat code than necessary + var playerName = mind.UserId is { } userId && _player.TryGetSessionById(userId, out var session) + ? session.Name + : "Unknown"; + + SendAdminAlert($"{playerName}{(antag ? " (ANTAG)" : "")} {message}"); } public void SendHookOOC(string sender, string message) diff --git a/Content.Server/Cloning/CloningConsoleSystem.cs b/Content.Server/Cloning/CloningConsoleSystem.cs index 39eac842f0..b5cf2dbb8a 100644 --- a/Content.Server/Cloning/CloningConsoleSystem.cs +++ b/Content.Server/Cloning/CloningConsoleSystem.cs @@ -165,7 +165,7 @@ namespace Content.Server.Cloning if (!_mindSystem.TryGetMind(body.Value, out var mindId, out var mind)) return; - if (mind.UserId.HasValue == false || mind.Session == null) + if (mind.UserId.HasValue == false || !_playerManager.ValidSessionId(mind.UserId.Value)) return; if (_cloningPodSystem.TryCloning(cloningPodUid, body.Value, (mindId, mind), cloningPod, scannerComp.CloningFailChanceMultiplier)) diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs index 2c3dcaba43..63821d9251 100644 --- a/Content.Server/GameTicking/GameTicker.Player.cs +++ b/Content.Server/GameTicking/GameTicker.Player.cs @@ -32,11 +32,8 @@ namespace Content.Server.GameTicking { if (args.NewStatus != SessionStatus.Disconnected) { - mind.Session = session; _pvsOverride.AddSessionOverride(mindId.Value, session); } - - DebugTools.Assert(mind.Session == session); } DebugTools.Assert(session.GetMind() == mindId); @@ -126,10 +123,9 @@ namespace Content.Server.GameTicking case SessionStatus.Disconnected: { _chatManager.SendAdminAnnouncement(Loc.GetString("player-leave-message", ("name", args.Session.Name))); - if (mind != null) + if (mindId != null) { - _pvsOverride.ClearOverride(GetNetEntity(mindId!.Value)); - mind.Session = null; + _pvsOverride.RemoveSessionOverride(mindId.Value, session); } _userDb.ClientDisconnected(session); diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index 9ec932b06f..311e78d009 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -28,6 +28,7 @@ using Content.Shared.Zombies; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Content.Shared.Cuffs.Components; +using Robust.Shared.Player; namespace Content.Server.GameTicking.Rules; @@ -36,19 +37,20 @@ namespace Content.Server.GameTicking.Rules; /// public sealed class RevolutionaryRuleSystem : GameRuleSystem { - [Dependency] private readonly IAdminLogManager _adminLogManager = default!; [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; [Dependency] private readonly EuiManager _euiMan = default!; + [Dependency] private readonly IAdminLogManager _adminLogManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly RoleSystem _role = default!; - [Dependency] private readonly SharedStunSystem _stun = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; [Dependency] private readonly StationSystem _stationSystem = default!; - [Dependency] private readonly IGameTiming _timing = default!; //Used in OnPostFlash, no reference to the rule component is available public readonly ProtoId RevolutionaryNpcFaction = "Revolutionary"; @@ -165,8 +167,8 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem { [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ZombieSystem _zombie = default!; public override void Initialize() @@ -95,9 +96,10 @@ public sealed class ZombieRuleSystem : GameRuleSystem { var meta = MetaData(survivor); var username = string.Empty; - if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null) + if (_mindSystem.TryGetMind(survivor, out _, out var mind) && + _player.TryGetSessionById(mind.UserId, out var session)) { - username = mind.Session.Name; + username = session.Name; } args.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index df4d998128..0cccab64b6 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -53,7 +53,7 @@ namespace Content.Server.Ghost [Dependency] private readonly MindSystem _minds = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; @@ -364,7 +364,7 @@ namespace Content.Server.Ghost private IEnumerable GetPlayerWarps(EntityUid except) { - foreach (var player in _playerManager.Sessions) + foreach (var player in _player.Sessions) { if (player.AttachedEntity is not {Valid: true} attached) continue; @@ -484,8 +484,8 @@ namespace Content.Server.Ghost // However, that should rarely happen. if (!string.IsNullOrWhiteSpace(mind.Comp.CharacterName)) _metaData.SetEntityName(ghost, mind.Comp.CharacterName); - else if (!string.IsNullOrWhiteSpace(mind.Comp.Session?.Name)) - _metaData.SetEntityName(ghost, mind.Comp.Session.Name); + else if (mind.Comp.UserId is { } userId && _player.TryGetSessionById(userId, out var session)) + _metaData.SetEntityName(ghost, session.Name); if (mind.Comp.TimeOfDeath.HasValue) { @@ -530,9 +530,9 @@ namespace Content.Server.Ghost if (mind.PreventGhosting && !forced) { - if (mind.Session != null) // Logging is suppressed to prevent spam from ghost attempts caused by movement attempts + if (_player.TryGetSessionById(mind.UserId, out var session)) // Logging is suppressed to prevent spam from ghost attempts caused by movement attempts { - _chatManager.DispatchServerMessage(mind.Session, Loc.GetString("comp-mind-ghosting-prevented"), + _chatManager.DispatchServerMessage(session, Loc.GetString("comp-mind-ghosting-prevented"), true); } diff --git a/Content.Server/Ghost/ReturnToBodyEui.cs b/Content.Server/Ghost/ReturnToBodyEui.cs index 77b143f7df..50664b30d1 100644 --- a/Content.Server/Ghost/ReturnToBodyEui.cs +++ b/Content.Server/Ghost/ReturnToBodyEui.cs @@ -2,19 +2,22 @@ using Content.Server.EUI; using Content.Shared.Eui; using Content.Shared.Ghost; using Content.Shared.Mind; +using Robust.Shared.Network; +using Robust.Shared.Player; namespace Content.Server.Ghost; public sealed class ReturnToBodyEui : BaseEui { private readonly SharedMindSystem _mindSystem; + private readonly ISharedPlayerManager _player; + private readonly NetUserId? _userId; - private readonly MindComponent _mind; - - public ReturnToBodyEui(MindComponent mind, SharedMindSystem mindSystem) + public ReturnToBodyEui(MindComponent mind, SharedMindSystem mindSystem, ISharedPlayerManager player) { - _mind = mind; _mindSystem = mindSystem; + _player = player; + _userId = mind.UserId; } public override void HandleMessage(EuiMessageBase msg) @@ -28,7 +31,8 @@ public sealed class ReturnToBodyEui : BaseEui return; } - _mindSystem.UnVisit(_mind.Session); + if (_userId is { } userId && _player.TryGetSessionById(userId, out var session)) + _mindSystem.UnVisit(session); Close(); } diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index ba272c92f5..94d62641ce 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -36,12 +36,13 @@ public sealed class DefibrillatorSystem : EntitySystem [Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly ElectrocutionSystem _electrocution = default!; [Dependency] private readonly EuiManager _euiManager = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; - [Dependency] private readonly RottingSystem _rotting = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; + [Dependency] private readonly RottingSystem _rotting = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; @@ -212,13 +213,13 @@ public sealed class DefibrillatorSystem : EntitySystem } if (_mind.TryGetMind(target, out _, out var mind) && - mind.Session is { } playerSession) + _player.TryGetSessionById(mind.UserId, out var playerSession)) { session = playerSession; // notify them they're being revived. if (mind.CurrentEntity != target) { - _euiManager.OpenEui(new ReturnToBodyEui(mind, _mind), session); + _euiManager.OpenEui(new ReturnToBodyEui(mind, _mind, _player), session); } } else diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index 0d3c0c750c..5601f19e58 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -69,7 +69,7 @@ public sealed class MindSystem : SharedMindSystem TransferTo(mindId, null, createGhost: false, mind: mind); DebugTools.AssertNull(mind.OwnedEntity); - if (!component.GhostOnShutdown || mind.Session == null || _gameTicker.RunLevel == GameRunLevel.PreRoundLobby) + if (!component.GhostOnShutdown || _gameTicker.RunLevel == GameRunLevel.PreRoundLobby) return; var ghost = _ghosts.SpawnGhost((mindId, mind), uid); @@ -93,16 +93,6 @@ public sealed class MindSystem : SharedMindSystem return false; } - public ICommonSession? GetSession(MindComponent mind) - { - return mind.Session; - } - - public bool TryGetSession(MindComponent mind, [NotNullWhen(true)] out ICommonSession? session) - { - return (session = GetSession(mind)) != null; - } - public override void WipeAllMinds() { base.WipeAllMinds(); @@ -144,10 +134,10 @@ public sealed class MindSystem : SharedMindSystem // Do this AFTER the entity changes above as this will fire off a player-detached event // which will run ghosting twice. - if (GetSession(mind) is { } session) + if (_players.TryGetSessionById(mind.UserId, out var session)) _players.SetAttachedEntity(session, entity); - Log.Info($"Session {mind.Session?.Name} visiting entity {entity}."); + Log.Info($"Session {session?.Name} visiting entity {entity}."); } public override void UnVisit(EntityUid mindId, MindComponent? mind = null) @@ -162,17 +152,19 @@ public sealed class MindSystem : SharedMindSystem RemoveVisitingEntity(mindId, mind); - if (mind.Session == null || mind.Session.AttachedEntity == mind.VisitingEntity) + if (mind.UserId == null || !_players.TryGetSessionById(mind.UserId.Value, out var session)) + return; + + if (session.AttachedEntity == mind.VisitingEntity) return; var owned = mind.OwnedEntity; - if (GetSession(mind) is { } session) - _players.SetAttachedEntity(session, owned); + _players.SetAttachedEntity(session, owned); if (owned.HasValue) { _adminLogger.Add(LogType.Mind, LogImpact.Low, - $"{mind.Session.Name} returned to {ToPrettyString(owned.Value)}"); + $"{session.Name} returned to {ToPrettyString(owned.Value)}"); } } @@ -199,7 +191,8 @@ public sealed class MindSystem : SharedMindSystem if (TryComp(entity.Value, out var actor)) { // Happens when transferring to your currently visited entity. - if (actor.PlayerSession != mind.Session) + if (!_players.TryGetSessionByEntity(entity.Value, out var session) || + mind.UserId == null || actor.PlayerSession != session ) { throw new ArgumentException("Visit target already has a session.", nameof(entity)); } @@ -253,12 +246,12 @@ public sealed class MindSystem : SharedMindSystem } // Player is CURRENTLY connected. - var session = GetSession(mind); - if (session != null && !alreadyAttached && mind.VisitingEntity == null) + if (mind.UserId != null && _players.TryGetSessionById(mind.UserId.Value, out var userSession) + && !alreadyAttached && mind.VisitingEntity == null) { - _players.SetAttachedEntity(session, entity, true); - DebugTools.Assert(session.AttachedEntity == entity, $"Failed to attach entity."); - Log.Info($"Session {session.Name} transferred to entity {entity}."); + _players.SetAttachedEntity(userSession, entity, true); + DebugTools.Assert(userSession.AttachedEntity == entity, "Failed to attach entity."); + Log.Info($"Session {userSession.Name} transferred to entity {entity}."); } if (entity != null) @@ -288,18 +281,18 @@ public sealed class MindSystem : SharedMindSystem return; Dirty(mindId, mind); - var netMind = GetNetEntity(mindId); - _pvsOverride.ClearOverride(netMind); + if (userId != null && !_players.TryGetPlayerData(userId.Value, out _)) { Log.Error($"Attempted to set mind user to invalid value {userId}"); return; } - if (mind.Session != null) + // Clear any existing entity attachment + if (_players.TryGetSessionById(mind.UserId, out var oldSession)) { - _players.SetAttachedEntity(GetSession(mind), null); - mind.Session = null; + _players.SetAttachedEntity(oldSession, null); + _pvsOverride.RemoveSessionOverride(mindId, oldSession); } if (mind.UserId != null) @@ -311,10 +304,7 @@ public sealed class MindSystem : SharedMindSystem } if (userId == null) - { - DebugTools.AssertNull(mind.Session); return; - } if (UserMinds.TryGetValue(userId.Value, out var oldMindId) && TryComp(oldMindId, out MindComponent? oldMind)) @@ -333,11 +323,10 @@ public sealed class MindSystem : SharedMindSystem if (_players.GetPlayerData(userId.Value).ContentData() is { } data) data.Mind = mindId; - if (_players.TryGetSessionById(userId.Value, out var ret)) + if (_players.TryGetSessionById(userId.Value, out var session)) { - mind.Session = ret; - _pvsOverride.AddSessionOverride(mindId, ret); - _players.SetAttachedEntity(ret, mind.CurrentEntity); + _pvsOverride.AddSessionOverride(mindId, session); + _players.SetAttachedEntity(session, mind.CurrentEntity); } } diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs index b8c0d3f450..9f4b6599b8 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs @@ -123,7 +123,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem private void OnRoleEvent(RoleEvent ev) { - if (_minds.TryGetSession(ev.Mind, out var session)) + if (_playerManager.TryGetSessionById(ev.Mind.UserId, out var session)) _tracking.QueueRefreshTrackers(session); } diff --git a/Content.Server/Roles/Jobs/JobSystem.cs b/Content.Server/Roles/Jobs/JobSystem.cs index 28cfbe76b5..3603225dfb 100644 --- a/Content.Server/Roles/Jobs/JobSystem.cs +++ b/Content.Server/Roles/Jobs/JobSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Mind; using Content.Shared.Mind; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; +using Robust.Shared.Player; namespace Content.Server.Roles.Jobs; @@ -14,6 +15,7 @@ public sealed class JobSystem : SharedJobSystem { [Dependency] private readonly IChatManager _chat = default!; [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; [Dependency] private readonly RoleSystem _roles = default!; public override void Initialize() @@ -42,7 +44,7 @@ public sealed class JobSystem : SharedJobSystem if (args.Silent) return; - if (!_mind.TryGetSession(mindId, out var session)) + if (!_player.TryGetSessionById(component.UserId, out var session)) return; if (!MindTryGetJob(mindId, out var prototype)) diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs index 278ac48579..6cbd039c73 100644 --- a/Content.Server/Roles/RoleSystem.cs +++ b/Content.Server/Roles/RoleSystem.cs @@ -46,7 +46,7 @@ public sealed class RoleSystem : SharedRoleSystem public void RoleUpdateMessage(MindComponent mind) { - if (mind.Session is null) + if (!Player.TryGetSessionById(mind.UserId, out var session)) return; if (!_proto.TryIndex(mind.RoleType, out var proto)) @@ -55,8 +55,6 @@ public sealed class RoleSystem : SharedRoleSystem var roleText = Loc.GetString(proto.Name); var color = proto.Color; - var session = mind.Session; - //TODO add audio? Would need to be optional so it does not play on role changes that already come with their own audio // _audio.PlayGlobal(Sound, session); diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index d8fa1f300b..82e247cb0b 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -54,7 +54,7 @@ public sealed partial class BorgSystem : SharedBorgSystem [Dependency] private readonly UserInterfaceSystem _ui = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - + [Dependency] private readonly ISharedPlayerManager _player = default!; [ValidatePrototypeId] public const string BorgJobId = "Borg"; @@ -110,9 +110,10 @@ public sealed partial class BorgSystem : SharedBorgSystem if (component.BrainEntity == null && brain != null && _whitelistSystem.IsWhitelistPassOrNull(component.BrainWhitelist, used)) { - if (_mind.TryGetMind(used, out _, out var mind) && mind.Session != null) + if (_mind.TryGetMind(used, out _, out var mind) && + _player.TryGetSessionById(mind.UserId, out var session)) { - if (!CanPlayerBeBorged(mind.Session)) + if (!CanPlayerBeBorged(session)) { Popup.PopupEntity(Loc.GetString("borg-player-not-allowed"), used, args.User); return; @@ -245,10 +246,11 @@ public sealed partial class BorgSystem : SharedBorgSystem container.ID != chassisComponent.BrainContainerId) return; - if (!_mind.TryGetMind(uid, out var mindId, out var mind) || mind.Session == null) + if (!_mind.TryGetMind(uid, out var mindId, out var mind) || + !_player.TryGetSessionById(mind.UserId, out var session)) return; - if (!CanPlayerBeBorged(mind.Session)) + if (!CanPlayerBeBorged(session)) { Popup.PopupEntity(Loc.GetString("borg-player-not-allowed-eject"), uid); Container.RemoveEntity(containerEnt, uid); diff --git a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs index d5a4db591a..3343fec7ab 100644 --- a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs +++ b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Antag; using Content.Server.Traitor.Components; using Content.Shared.Mind.Components; +using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Systems; @@ -11,6 +12,7 @@ namespace Content.Server.Traitor.Systems; public sealed class AutoTraitorSystem : EntitySystem { [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; public override void Initialize() { @@ -21,6 +23,9 @@ public sealed class AutoTraitorSystem : EntitySystem private void OnMindAdded(EntityUid uid, AutoTraitorComponent comp, MindAddedMessage args) { - _antag.ForceMakeAntag(args.Mind.Comp.Session, comp.Profile); + if (!_player.TryGetSessionById(args.Mind.Comp.UserId, out var session)) + return; + + _antag.ForceMakeAntag(session, comp.Profile); } } diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index a7a22c4175..7be4570160 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -35,7 +35,9 @@ using Content.Shared.Prying.Components; using Content.Shared.Traits.Assorted; using Robust.Shared.Audio.Systems; using Content.Shared.Ghost.Roles.Components; +using Content.Shared.Roles; using Content.Shared.Tag; +using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Zombies; @@ -61,6 +63,8 @@ public sealed partial class ZombieSystem [Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly NameModifierSystem _nameMod = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; private static readonly ProtoId CannotSuicideTag = "CannotSuicide"; @@ -234,8 +238,8 @@ public sealed partial class ZombieSystem _npc.SleepNPC(target, htn); //He's gotta have a mind - var hasMind = _mind.TryGetMind(target, out var mindId, out _); - if (hasMind && _mind.TryGetSession(mindId, out var session)) + var hasMind = _mind.TryGetMind(target, out var mindId, out var mind); + if (hasMind && mind != null && _player.TryGetSessionById(mind.UserId, out var session)) { //Zombie role for player manifest _role.MindAddRole(mindId, "MindRoleZombie", mind: null, silent: true); diff --git a/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs b/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs index f6b7a34785..e17a0a9167 100644 --- a/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs +++ b/Content.Shared/Bed/Cryostorage/SharedCryostorageSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Mobs.Systems; using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Shared.Bed.Cryostorage; @@ -17,13 +18,14 @@ namespace Content.Shared.Bed.Cryostorage; /// public abstract class SharedCryostorageSystem : EntitySystem { - [Dependency] protected readonly ISharedAdminLogManager AdminLog = default!; - [Dependency] private readonly IConfigurationManager _configuration = default!; + [Dependency] private readonly IConfigurationManager _configuration = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ISharedPlayerManager _player = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] protected readonly ISharedAdminLogManager AdminLog = default!; [Dependency] protected readonly SharedMindSystem Mind = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; protected EntityUid? PausedMap { get; private set; } @@ -123,7 +125,8 @@ public abstract class SharedCryostorageSystem : EntitySystem if (args.Dragged == args.User) return; - if (!Mind.TryGetMind(args.Dragged, out _, out var mindComp) || mindComp.Session?.AttachedEntity != args.Dragged) + if (!_player.TryGetSessionByEntity(args.Dragged, out var session) || + session.AttachedEntity != args.Dragged) return; args.CanDrop = false; diff --git a/Content.Shared/Mind/MindComponent.cs b/Content.Shared/Mind/MindComponent.cs index 3b2b934478..7864d61a8c 100644 --- a/Content.Shared/Mind/MindComponent.cs +++ b/Content.Shared/Mind/MindComponent.cs @@ -113,12 +113,4 @@ public sealed partial class MindComponent : Component /// [DataField] public LocId? Subtype; - - /// - /// The session of the player owning this mind. - /// Can be null, in which case the player is currently not logged in. - /// - [ViewVariables, Access(typeof(SharedMindSystem), typeof(SharedGameTicker))] - // TODO remove this after moving IPlayerManager functions to shared - public ICommonSession? Session { get; set; } } diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 1b1fcabc98..ab17a4221e 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -26,6 +26,7 @@ public abstract partial class SharedMindSystem : EntitySystem [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly SharedPlayerSystem _player = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; [Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; @@ -153,23 +154,31 @@ public abstract partial class SharedMindSystem : EntitySystem if (!mindContainer.ShowExamineInfo || !args.IsInDetailsRange) return; - // TODO predict we can't right now because session stuff isnt networked + // TODO: Move this out of the SharedMindSystem into its own comp and predict it if (_net.IsClient) return; var dead = _mobState.IsDead(uid); - var hasUserId = CompOrNull(mindContainer.Mind)?.UserId; - var hasSession = CompOrNull(mindContainer.Mind)?.Session; + var mind = CompOrNull(mindContainer.Mind); + var hasUserId = mind?.UserId; + var hasActiveSession = hasUserId != null && _playerManager.ValidSessionId(hasUserId.Value); + + // Scenarios: + // 1. Dead + No User ID: Entity is permanently dead with no player ever attached + // 2. Dead + Has User ID + No Session: Player died and disconnected + // 3. Dead + Has Session: Player is dead but still connected + // 4. Alive + No User ID: Entity was never controlled by a player + // 5. Alive + No Session: Player disconnected while alive (SSD) if (dead && hasUserId == null) args.PushMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-dead-and-irrecoverable", ("ent", uid))}[/color]"); - else if (dead && hasSession == null) + else if (dead && !hasActiveSession) args.PushMarkup($"[color=yellow]{Loc.GetString("comp-mind-examined-dead-and-ssd", ("ent", uid))}[/color]"); else if (dead) args.PushMarkup($"[color=red]{Loc.GetString("comp-mind-examined-dead", ("ent", uid))}[/color]"); else if (hasUserId == null) args.PushMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-catatonic", ("ent", uid))}[/color]"); - else if (hasSession == null) + else if (!hasActiveSession) args.PushMarkup($"[color=yellow]{Loc.GetString("comp-mind-examined-ssd", ("ent", uid))}[/color]"); } @@ -460,12 +469,6 @@ public abstract partial class SharedMindSystem : EntitySystem return false; } - public bool TryGetSession(EntityUid? mindId, [NotNullWhen(true)] out ICommonSession? session) - { - session = null; - return TryComp(mindId, out MindComponent? mind) && (session = mind.Session) != null; - } - /// /// Gets a mind from uid and/or MindContainerComponent. Used for null checks. /// diff --git a/Content.Shared/Roles/SharedRoleSystem.cs b/Content.Shared/Roles/SharedRoleSystem.cs index 83ef6cbf23..0407251414 100644 --- a/Content.Shared/Roles/SharedRoleSystem.cs +++ b/Content.Shared/Roles/SharedRoleSystem.cs @@ -10,6 +10,7 @@ using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.Map; +using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -18,12 +19,13 @@ namespace Content.Shared.Roles; public abstract class SharedRoleSystem : EntitySystem { - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; - [Dependency] private readonly SharedMindSystem _minds = default!; - [Dependency] private readonly IPrototypeManager _prototypes = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] protected readonly ISharedPlayerManager Player = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedMindSystem _minds = default!; private JobRequirementOverridePrototype? _requirementOverride; @@ -256,7 +258,7 @@ public abstract class SharedRoleSystem : EntitySystem Dirty(mind, comp); // Update player character window - if (_minds.TryGetSession(mind, out var session)) + if (Player.TryGetSessionById(comp.UserId, out var session)) RaiseNetworkEvent(new MindRoleTypeChangedEvent(), session.Channel); else { @@ -589,8 +591,11 @@ public abstract class SharedRoleSystem : EntitySystem /// public void MindPlaySound(EntityUid mindId, SoundSpecifier? sound, MindComponent? mind = null) { - if (Resolve(mindId, ref mind) && mind.Session != null) - _audio.PlayGlobal(sound, mind.Session); + if (!Resolve(mindId, ref mind)) + return; + + if (Player.TryGetSessionById(mind.UserId, out var session)) + _audio.PlayGlobal(sound, session); } // TODO ROLES Change to readonly.