diff --git a/Content.IntegrationTests/Pair/TestPair.Recycle.cs b/Content.IntegrationTests/Pair/TestPair.Recycle.cs index bdd4fc7791..8de7149043 100644 --- a/Content.IntegrationTests/Pair/TestPair.Recycle.cs +++ b/Content.IntegrationTests/Pair/TestPair.Recycle.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using Content.Server.GameTicking; +using Content.Server.Mind; using Content.Server.Mind.Components; using Content.Shared.CCVar; using Content.Shared.GameTicking; @@ -38,7 +39,7 @@ public sealed partial class TestPair : IAsyncDisposable await Server.WaitPost(() => Server.EntMan.DeleteEntity(TestMap.MapUid)); TestMap = null; } - + var usageTime = Watch.Elapsed; Watch.Restart(); await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: Test borrowed pair {Id} for {usageTime.TotalMilliseconds} ms"); @@ -80,7 +81,7 @@ public sealed partial class TestPair : IAsyncDisposable { if (State != PairState.InUse) throw new Exception($"{nameof(CleanReturnAsync)}: Unexpected state. Pair: {Id}. State: {State}."); - + await _testOut.WriteLineAsync($"{nameof(CleanReturnAsync)}: Return of pair {Id} started"); State = PairState.CleanDisposed; await OnCleanDispose(); @@ -106,7 +107,7 @@ public sealed partial class TestPair : IAsyncDisposable throw new Exception($"{nameof(DisposeAsync)}: Unexpected state. Pair: {Id}. State: {State}."); } } - + public async Task CleanPooledPair(PoolSettings settings, TextWriter testOut) { Settings = default!; @@ -160,7 +161,7 @@ public sealed partial class TestPair : IAsyncDisposable await ReallyBeIdle(); await testOut.WriteLineAsync($"Recycling: {Watch.Elapsed.TotalMilliseconds} ms: Done recycling"); } - + public void ValidateSettings(PoolSettings settings) { var cfg = Server.CfgMan; @@ -211,8 +212,9 @@ public sealed partial class TestPair : IAsyncDisposable Assert.That(entMan.HasComponent(session.AttachedEntity)); var mindCont = entMan.GetComponent(session.AttachedEntity!.Value); Assert.NotNull(mindCont.Mind); - Assert.Null(mindCont.Mind?.VisitingEntity); - Assert.That(mindCont.Mind!.OwnedEntity, Is.EqualTo(session.AttachedEntity!.Value)); - Assert.That(mindCont.Mind.UserId, Is.EqualTo(session.UserId)); + Assert.True(entMan.TryGetComponent(mindCont.Mind, out MindComponent? mind)); + Assert.Null(mind!.VisitingEntity); + Assert.That(mind.OwnedEntity, Is.EqualTo(session.AttachedEntity!.Value)); + Assert.That(mind.UserId, Is.EqualTo(session.UserId)); } -} \ No newline at end of file +} diff --git a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs index adf06487a9..5eecfd01ec 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostRoleTests.cs @@ -44,19 +44,20 @@ public sealed class GhostRoleTests var conHost = client.ResolveDependency(); var mindSystem = entMan.System(); var session = sPlayerMan.ServerSessions.Single(); - var originalMind = session.ContentData()!.Mind!; + var originalMindId = session.ContentData()!.Mind!.Value; // Spawn player entity & attach EntityUid originalMob = default; await server.WaitPost(() => { originalMob = entMan.SpawnEntity(null, MapCoordinates.Nullspace); - mindSystem.TransferTo(originalMind, originalMob, true); + mindSystem.TransferTo(originalMindId, originalMob, true); }); // Check player got attached. await pair.RunTicksSync(10); Assert.That(session.AttachedEntity, Is.EqualTo(originalMob)); + var originalMind = entMan.GetComponent(originalMindId); Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); Assert.Null(originalMind.VisitingEntity); @@ -66,7 +67,7 @@ public sealed class GhostRoleTests var ghost = session.AttachedEntity; Assert.That(entMan.HasComponent(ghost)); Assert.That(ghost, Is.Not.EqualTo(originalMob)); - Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMind)); + Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMindId)); Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost)); @@ -83,8 +84,9 @@ public sealed class GhostRoleTests // Check player got attached to ghost role. await pair.RunTicksSync(10); - var newMind = session.ContentData()!.Mind!; - Assert.That(newMind, Is.Not.EqualTo(originalMind)); + var newMindId = session.ContentData()!.Mind!.Value; + var newMind = entMan.GetComponent(newMindId); + Assert.That(newMindId, Is.Not.EqualTo(originalMindId)); Assert.That(session.AttachedEntity, Is.EqualTo(ghostRole)); Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole)); Assert.Null(newMind.VisitingEntity); @@ -101,12 +103,12 @@ public sealed class GhostRoleTests Assert.That(entMan.HasComponent(otherGhost)); Assert.That(otherGhost, Is.Not.EqualTo(originalMob)); Assert.That(otherGhost, Is.Not.EqualTo(ghostRole)); - Assert.That(session.ContentData()?.Mind, Is.EqualTo(newMind)); + Assert.That(session.ContentData()?.Mind, Is.EqualTo(newMindId)); Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole)); Assert.That(newMind.VisitingEntity, Is.EqualTo(session.AttachedEntity)); // Next, control the original entity again: - await server.WaitPost(() => mindSystem.SetUserId(originalMind, session.UserId)); + await server.WaitPost(() => mindSystem.SetUserId(originalMindId, session.UserId)); await pair.RunTicksSync(10); Assert.That(session.AttachedEntity, Is.EqualTo(originalMob)); Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs index 52878f555b..019d887842 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.EntityDeletion.cs @@ -31,7 +31,8 @@ public sealed partial class MindTests EntityUid playerEnt = default; EntityUid visitEnt = default; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; await server.WaitAssertion(() => { var player = playerMan.ServerSessions.Single(); @@ -39,9 +40,10 @@ public sealed partial class MindTests playerEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace); visitEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace); - mind = mindSystem.CreateMind(player.UserId); - mindSystem.TransferTo(mind, playerEnt); - mindSystem.Visit(mind, visitEnt); + mindId = mindSystem.CreateMind(player.UserId); + mind = entMan.GetComponent(mindId); + mindSystem.TransferTo(mindId, playerEnt); + mindSystem.Visit(mindId, visitEnt); Assert.Multiple(() => { @@ -84,12 +86,14 @@ public sealed partial class MindTests var mindSystem = entMan.EntitySysManager.GetEntitySystem(); EntityUid playerEnt = default; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; await server.WaitAssertion(() => { playerEnt = entMan.SpawnEntity(null, coordinates); - mind = player.ContentData()!.Mind!; - mindSystem.TransferTo(mind, playerEnt); + mindId = player.ContentData()!.Mind!.Value; + mind = entMan.GetComponent(mindId); + mindSystem.TransferTo(mindId, playerEnt); Assert.That(mind.CurrentEntity, Is.EqualTo(playerEnt)); }); @@ -169,15 +173,15 @@ public sealed partial class MindTests await server.WaitAssertion(() => { ghost = entMan.SpawnEntity("MobObserver", MapCoordinates.Nullspace); - mindSystem.Visit(mind, ghost); + mindSystem.Visit(mind.Id, ghost); }); Assert.Multiple(() => { Assert.That(player.AttachedEntity, Is.EqualTo(ghost)); Assert.That(entMan.HasComponent(player.AttachedEntity), "player is not a ghost"); - Assert.That(mind.VisitingEntity, Is.EqualTo(player.AttachedEntity)); - Assert.That(mind.OwnedEntity, Is.EqualTo(originalEntity)); + Assert.That(mind.Comp.VisitingEntity, Is.EqualTo(player.AttachedEntity)); + Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(originalEntity)); }); await pair.RunTicksSync(5); @@ -192,8 +196,8 @@ public sealed partial class MindTests { Assert.That(player.AttachedEntity, Is.EqualTo(ghost)); Assert.That(entMan.HasComponent(player.AttachedEntity)); - Assert.That(mind.VisitingEntity, Is.Null); - Assert.That(mind.OwnedEntity, Is.EqualTo(ghost)); + Assert.That(mind.Comp.VisitingEntity, Is.Null); + Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(ghost)); }); await pair.CleanReturnAsync(); @@ -233,8 +237,10 @@ public sealed partial class MindTests Assert.That(entMan.GetComponent(player.AttachedEntity!.Value).EntityPrototype?.ID, Is.EqualTo("AdminObserver")); }); - var mind = player.ContentData()?.Mind; - Assert.That(mind, Is.Not.Null); + var mindId = player.ContentData()?.Mind; + Assert.That(mindId, Is.Not.Null); + + var mind = entMan.GetComponent(mindId.Value); Assert.That(mind.VisitingEntity, Is.Null); await pair.CleanReturnAsync(); diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs index 1c663d1cce..1eab9cd6c6 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs @@ -9,7 +9,6 @@ using Robust.Shared.Enums; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Network; -using IPlayerManager = Robust.Server.Player.IPlayerManager; namespace Content.IntegrationTests.Tests.Minds; @@ -40,19 +39,21 @@ public sealed partial class MindTests var player = playerMan.ServerSessions.Single(); EntityUid entity = default; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; await pair.Server.WaitPost(() => { entity = entMan.SpawnEntity(null, MapCoordinates.Nullspace); - mind = mindSys.CreateMind(player.UserId); - mindSys.TransferTo(mind, entity); + mindId = mindSys.CreateMind(player.UserId); + mind = entMan.GetComponent(mindId); + mindSys.TransferTo(mindId, entity); }); await pair.RunTicksSync(5); Assert.Multiple(() => { - Assert.That(player.ContentData()?.Mind, Is.EqualTo(mind)); + Assert.That(player.ContentData()?.Mind, Is.EqualTo(mindId)); Assert.That(player.AttachedEntity, Is.EqualTo(entity)); 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"); @@ -67,23 +68,25 @@ public sealed partial class MindTests var playerMan = pair.Server.ResolveDependency(); var mindSys = entMan.System(); EntityUid ghostUid = default; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; var player = playerMan.ServerSessions.Single(); await pair.Server.WaitAssertion(() => { var oldUid = player.AttachedEntity; ghostUid = entMan.SpawnEntity("MobObserver", MapCoordinates.Nullspace); - mind = mindSys.GetMind(player.UserId); - Assert.That(mind, Is.Not.Null); + mindId = mindSys.GetMind(player.UserId)!.Value; + Assert.That(mindId, Is.Not.EqualTo(default(EntityUid))); + mind = entMan.GetComponent(mindId); if (visit) { - mindSys.Visit(mind, ghostUid); + mindSys.Visit(mindId, ghostUid); return; } - mindSys.TransferTo(mind, ghostUid); + mindSys.TransferTo(mindId, ghostUid); if (oldUid != null) entMan.DeleteEntity(oldUid.Value); @@ -111,15 +114,16 @@ public sealed partial class MindTests /// /// Get the player's current mind and check that the entities exists. /// - private static Mind GetMind(Pair.TestPair pair) + private static (EntityUid Id, MindComponent Comp) GetMind(Pair.TestPair pair) { var playerMan = pair.Server.ResolveDependency(); var entMan = pair.Server.ResolveDependency(); var player = playerMan.ServerSessions.SingleOrDefault(); Assert.That(player, Is.Not.Null); - var mind = player.ContentData()!.Mind; - Assert.That(mind, Is.Not.Null); + var mindId = player.ContentData()!.Mind!.Value; + Assert.That(mindId, Is.Not.EqualTo(default(EntityUid))); + var mind = entMan.GetComponent(mindId); Assert.Multiple(() => { Assert.That(player.AttachedEntity, Is.EqualTo(mind.CurrentEntity), "Player is not attached to the mind's current entity."); @@ -127,15 +131,17 @@ public sealed partial class MindTests Assert.That(mind.VisitingEntity == null || entMan.EntityExists(mind.VisitingEntity), "The minds visited entity does not exist."); }); - return mind; + return (mindId, mind); } private static async Task Disconnect(Pair.TestPair pair) { var netManager = pair.Client.ResolveDependency(); var playerMan = pair.Server.ResolveDependency(); + var entMan = pair.Server.ResolveDependency(); var player = playerMan.ServerSessions.Single(); - var mind = player.ContentData()!.Mind; + var mindId = player.ContentData()!.Mind!.Value; + var mind = entMan.GetComponent(mindId); await pair.Client.WaitAssertion(() => { diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs index a6db9bbd09..8d957bf197 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.ReconnectTests.cs @@ -29,8 +29,8 @@ public sealed partial class MindTests { Assert.That(GetMind(pair), Is.EqualTo(mind)); Assert.That(entMan.Deleted(ghost)); - Assert.That(entMan.HasComponent(mind.OwnedEntity)); - Assert.That(mind.VisitingEntity, Is.Null); + Assert.That(entMan.HasComponent(mind.Comp.OwnedEntity)); + Assert.That(mind.Comp.VisitingEntity, Is.Null); }); await pair.CleanReturnAsync(); @@ -52,11 +52,11 @@ public sealed partial class MindTests var player = playerMan.ServerSessions.Single(); var name = player.Name; var user = player.UserId; - Assert.That(mind.OwnedEntity, Is.Not.Null); - var entity = mind.OwnedEntity.Value; + Assert.That(mind.Comp.OwnedEntity, Is.Not.Null); + var entity = mind.Comp.OwnedEntity.Value; // Player is not a ghost - Assert.That(!entMan.HasComponent(mind.CurrentEntity)); + Assert.That(!entMan.HasComponent(mind.Comp.CurrentEntity)); // Disconnect await Disconnect(pair); @@ -67,7 +67,7 @@ public sealed partial class MindTests Assert.Multiple(() => { Assert.That(entMan.Deleted(entity)); - Assert.That(mind.OwnedEntity, Is.Null); + Assert.That(mind.Comp.OwnedEntity, Is.Null); }); // Reconnect @@ -79,8 +79,8 @@ public sealed partial class MindTests // Player is now a new ghost entity Assert.That(GetMind(pair), Is.EqualTo(mind)); - Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity)); - Assert.That(entMan.HasComponent(mind.OwnedEntity)); + Assert.That(mind.Comp.OwnedEntity, Is.Not.EqualTo(entity)); + Assert.That(entMan.HasComponent(mind.Comp.OwnedEntity)); }); await pair.CleanReturnAsync(); @@ -98,7 +98,7 @@ public sealed partial class MindTests var entMan = pair.Server.ResolveDependency(); var mind = GetMind(pair); - var original = mind.CurrentEntity; + var original = mind.Comp.CurrentEntity; var ghost = await VisitGhost(pair); await DisconnectReconnect(pair); @@ -106,7 +106,7 @@ public sealed partial class MindTests Assert.Multiple(() => { Assert.That(mind, Is.EqualTo(GetMind(pair))); - Assert.That(mind.CurrentEntity, Is.EqualTo(original)); + Assert.That(mind.Comp.CurrentEntity, Is.EqualTo(original)); Assert.That(entMan.Deleted(original), Is.False); Assert.That(entMan.Deleted(ghost)); }); @@ -128,12 +128,12 @@ public sealed partial class MindTests var mind = GetMind(pair); // Make player visit a new mob - var original = mind.CurrentEntity; + var original = mind.Comp.CurrentEntity; EntityUid visiting = default; await pair.Server.WaitAssertion(() => { visiting = entMan.SpawnEntity(null, MapCoordinates.Nullspace); - mindSys.Visit(mind, visiting); + mindSys.Visit(mind.Id, visiting); }); await pair.RunTicksSync(5); @@ -145,7 +145,7 @@ public sealed partial class MindTests Assert.That(GetMind(pair), Is.EqualTo(mind)); Assert.That(entMan.Deleted(original), Is.False); Assert.That(entMan.Deleted(visiting), Is.False); - Assert.That(mind.CurrentEntity, Is.EqualTo(visiting)); + Assert.That(mind.Comp.CurrentEntity, Is.EqualTo(visiting)); }); await pair.CleanReturnAsync(); diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.cs b/Content.IntegrationTests/Tests/Minds/MindTests.cs index 99cd8edb97..7928f4368f 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.cs @@ -8,17 +8,17 @@ using Content.Server.Mind.Commands; using Content.Server.Mind.Components; using Content.Server.Players; using Content.Server.Roles; +using Content.Server.Roles.Jobs; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; -using Content.Shared.Roles; using Robust.Server.Console; using Robust.Server.GameObjects; +using Robust.Server.Player; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Prototypes; -using IPlayerManager = Robust.Server.Player.IPlayerManager; namespace Content.IntegrationTests.Tests.Minds; @@ -66,12 +66,13 @@ public sealed partial class MindTests var entity = entMan.SpawnEntity(null, new MapCoordinates()); var mindComp = entMan.EnsureComponent(entity); - var mind = mindSystem.CreateMind(null); + var mindId = mindSystem.CreateMind(null); + var mind = entMan.GetComponent(mindId); Assert.That(mind.UserId, Is.EqualTo(null)); - mindSystem.TransferTo(mind, entity); - Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); + mindSystem.TransferTo(mindId, entity, mind: mind); + Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId)); }); await pair.CleanReturnAsync(); @@ -92,15 +93,16 @@ public sealed partial class MindTests var entity = entMan.SpawnEntity(null, new MapCoordinates()); var mindComp = entMan.EnsureComponent(entity); - var mind = mindSystem.CreateMind(null); - mindSystem.TransferTo(mind, entity); - Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); + var mindId = mindSystem.CreateMind(null); + mindSystem.TransferTo(mindId, entity); + Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId)); var mind2 = mindSystem.CreateMind(null); mindSystem.TransferTo(mind2, entity); Assert.Multiple(() => { Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind2)); + var mind = entMan.GetComponent(mindId); Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity)); }); }); @@ -119,7 +121,7 @@ public sealed partial class MindTests EntityUid entity = default!; MindContainerComponent mindContainerComp = default!; - Mind mind = default!; + EntityUid mindId = default!; var mindSystem = entMan.EntitySysManager.GetEntitySystem(); var damageableSystem = entMan.EntitySysManager.GetEntitySystem(); @@ -128,12 +130,13 @@ public sealed partial class MindTests entity = entMan.SpawnEntity("MindTestEntityDamageable", new MapCoordinates()); mindContainerComp = entMan.EnsureComponent(entity); - mind = mindSystem.CreateMind(null); + mindId = mindSystem.CreateMind(null); - mindSystem.TransferTo(mind, entity); + mindSystem.TransferTo(mindId, entity); Assert.Multiple(() => { - Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mind)); + Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mindId)); + var mind = entMan.GetComponent(mindId); Assert.That(!mindSystem.IsCharacterDeadPhysically(mind)); }); }); @@ -149,13 +152,14 @@ public sealed partial class MindTests } damageableSystem.SetDamage(entity, damageable, new DamageSpecifier(prototype, FixedPoint2.New(401))); - Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mind)); + Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mindId)); }); await pair.RunTicksSync(5); await server.WaitAssertion(() => { + var mind = entMan.GetComponent(mindId); Assert.That(mindSystem.IsCharacterDeadPhysically(mind)); }); @@ -211,20 +215,22 @@ public sealed partial class MindTests await pair.RunTicksSync(5); var mindSystem = entMan.EntitySysManager.GetEntitySystem(); var originalMind = GetMind(pair); - var userId = originalMind.UserId; + var userId = originalMind.Comp.UserId; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; await server.WaitAssertion(() => { var entity = entMan.SpawnEntity(null, new MapCoordinates()); var mindComp = entMan.EnsureComponent(entity); entMan.DirtyEntity(entity); - mind = mindSystem.CreateMind(null); - mindSystem.TransferTo(mind, entity); + mindId = mindSystem.CreateMind(null); + mind = entMan.GetComponent(mindId); + mindSystem.TransferTo(mindId, entity); Assert.Multiple(() => { - Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); + Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId)); Assert.That(mindComp.HasMind); }); }); @@ -233,18 +239,18 @@ public sealed partial class MindTests await server.WaitAssertion(() => { - mindSystem.SetUserId(mind, userId); + mindSystem.SetUserId(mindId, userId); Assert.Multiple(() => { Assert.That(mind.UserId, Is.EqualTo(userId)); - Assert.That(originalMind.UserId, Is.EqualTo(null)); + Assert.That(originalMind.Comp.UserId, Is.EqualTo(null)); }); - mindSystem.SetUserId(originalMind, userId); + mindSystem.SetUserId(originalMind.Id, userId); Assert.Multiple(() => { Assert.That(mind.UserId, Is.EqualTo(null)); - Assert.That(originalMind.UserId, Is.EqualTo(userId)); + Assert.That(originalMind.Comp.UserId, Is.EqualTo(userId)); }); }); @@ -264,57 +270,59 @@ public sealed partial class MindTests await server.WaitAssertion(() => { var mindSystem = entMan.EntitySysManager.GetEntitySystem(); + var roleSystem = entMan.EntitySysManager.GetEntitySystem(); var entity = entMan.SpawnEntity(null, new MapCoordinates()); var mindComp = entMan.EnsureComponent(entity); - var mind = mindSystem.CreateMind(null); + var mindId = mindSystem.CreateMind(null); + var mind = entMan.EnsureComponent(mindId); Assert.That(mind.UserId, Is.EqualTo(null)); - mindSystem.TransferTo(mind, entity); - Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); + mindSystem.TransferTo(mindId, entity); + Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId)); Assert.Multiple(() => { - Assert.That(mindSystem.HasRole(mind), Is.False); - Assert.That(mindSystem.HasRole(mind), Is.False); + Assert.That(roleSystem.MindHasRole(mindId), Is.False); + Assert.That(roleSystem.MindHasRole(mindId), Is.False); }); - var traitorRole = new TraitorRole(mind, new AntagPrototype()); + var traitorRole = new TraitorRoleComponent(); - mindSystem.AddRole(mind, traitorRole); + roleSystem.MindAddRole(mindId, traitorRole); Assert.Multiple(() => { - Assert.That(mindSystem.HasRole(mind)); - Assert.That(mindSystem.HasRole(mind), Is.False); + Assert.That(roleSystem.MindHasRole(mindId)); + Assert.That(roleSystem.MindHasRole(mindId), Is.False); }); - var jobRole = new Job(mind, new JobPrototype()); + var jobRole = new JobComponent(); - mindSystem.AddRole(mind, jobRole); + roleSystem.MindAddRole(mindId, jobRole); Assert.Multiple(() => { - Assert.That(mindSystem.HasRole(mind)); - Assert.That(mindSystem.HasRole(mind)); + Assert.That(roleSystem.MindHasRole(mindId)); + Assert.That(roleSystem.MindHasRole(mindId)); }); - mindSystem.RemoveRole(mind, traitorRole); + roleSystem.MindRemoveRole(mindId); Assert.Multiple(() => { - Assert.That(mindSystem.HasRole(mind), Is.False); - Assert.That(mindSystem.HasRole(mind)); + Assert.That(roleSystem.MindHasRole(mindId), Is.False); + Assert.That(roleSystem.MindHasRole(mindId)); }); - mindSystem.RemoveRole(mind, jobRole); + roleSystem.MindRemoveRole(mindId); Assert.Multiple(() => { - Assert.That(mindSystem.HasRole(mind), Is.False); - Assert.That(mindSystem.HasRole(mind), Is.False); + Assert.That(roleSystem.MindHasRole(mindId), Is.False); + Assert.That(roleSystem.MindHasRole(mindId), Is.False); }); }); @@ -335,7 +343,8 @@ public sealed partial class MindTests var ghostSystem = entMan.EntitySysManager.GetEntitySystem(); EntityUid entity = default!; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; var player = playerMan.ServerSessions.Single(); await server.WaitAssertion(() => @@ -343,12 +352,13 @@ public sealed partial class MindTests entity = entMan.SpawnEntity(null, new MapCoordinates()); var mindComp = entMan.EnsureComponent(entity); - mind = mindSystem.CreateMind(player.UserId, "Mindy McThinker"); + mindId = mindSystem.CreateMind(player.UserId, "Mindy McThinker"); + mind = entMan.GetComponent(mindId); Assert.That(mind.UserId, Is.EqualTo(player.UserId)); - mindSystem.TransferTo(mind, entity); - Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); + mindSystem.TransferTo(mindId, entity); + Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId)); }); await pair.RunTicksSync(5); @@ -361,7 +371,8 @@ public sealed partial class MindTests await pair.RunTicksSync(5); EntityUid mob = default!; - Mind mobMind = default!; + EntityUid mobMindId = default!; + MindComponent mobMind = default!; await server.WaitAssertion(() => { @@ -370,22 +381,25 @@ public sealed partial class MindTests mob = entMan.SpawnEntity(null, new MapCoordinates()); MakeSentientCommand.MakeSentient(mob, IoCManager.Resolve()); - mobMind = mindSystem.CreateMind(player.UserId, "Mindy McThinker the Second"); + mobMindId = mindSystem.CreateMind(player.UserId, "Mindy McThinker the Second"); + mobMind = entMan.GetComponent(mobMindId); - mindSystem.SetUserId(mobMind, player.UserId); - mindSystem.TransferTo(mobMind, mob); + mindSystem.SetUserId(mobMindId, player.UserId); + mindSystem.TransferTo(mobMindId, mob); }); await pair.RunTicksSync(5); await server.WaitAssertion(() => { - var m = player.ContentData()?.Mind; - Assert.That(m, Is.Not.Null); + var mId = player.ContentData()?.Mind!.Value; + Assert.That(mId, Is.Not.Null); + Assert.That(mId, Is.Not.EqualTo(default(EntityUid))); + var m = entMan.GetComponent(mId!.Value); Assert.Multiple(() => { Assert.That(m!.OwnedEntity, Is.EqualTo(mob)); - Assert.That(m, Is.Not.EqualTo(mind)); + Assert.That(mId, Is.Not.EqualTo(mindId)); }); }); @@ -416,7 +430,8 @@ public sealed partial class MindTests //EntityUid entity = default!; EntityUid ghostRole = default!; EntityUid ghost = default!; - Mind mind = default!; + EntityUid mindId = default!; + MindComponent mind = default!; var player = playerMan.ServerSessions.Single(); await server.WaitAssertion(() => @@ -434,7 +449,8 @@ public sealed partial class MindTests var data = player.ContentData(); Assert.That(data?.Mind, Is.Not.EqualTo(null)); - mind = data!.Mind!; + mindId = data!.Mind!.Value; + mind = entMan.GetComponent(mindId); Assert.That(mind.OwnedEntity, Is.Not.Null); @@ -460,8 +476,8 @@ public sealed partial class MindTests await server.WaitAssertion(() => { - var data = player.ContentData()!; - Assert.That(data.Mind!.OwnedEntity, Is.EqualTo(ghostRole)); + var data = entMan.GetComponent(player.ContentData()!.Mind!.Value); + Assert.That(data.OwnedEntity, Is.EqualTo(ghostRole)); serverConsole.ExecuteCommand(player, "aghost"); Assert.That(player.AttachedEntity, Is.Not.Null); diff --git a/Content.Server/Administration/Commands/AGhost.cs b/Content.Server/Administration/Commands/AGhost.cs index 279a254089..56d9087fa2 100644 --- a/Content.Server/Administration/Commands/AGhost.cs +++ b/Content.Server/Administration/Commands/AGhost.cs @@ -1,6 +1,5 @@ using Content.Server.GameTicking; using Content.Server.Mind; -using Content.Server.Players; using Content.Shared.Administration; using Content.Shared.Ghost; using Robust.Server.Player; @@ -26,20 +25,18 @@ namespace Content.Server.Administration.Commands return; } - var mind = player.ContentData()?.Mind; - - if (mind == null) + var mindSystem = _entities.System(); + if (!mindSystem.TryGetMind(player, out var mindId, out var mind)) { shell.WriteLine("You can't ghost here!"); return; } - var mindSystem = _entities.System(); var metaDataSystem = _entities.System(); if (mind.VisitingEntity != default && _entities.TryGetComponent(mind.VisitingEntity, out var oldGhostComponent)) { - mindSystem.UnVisit(mind); + mindSystem.UnVisit(mindId, mind); // If already an admin ghost, then return to body. if (oldGhostComponent.CanGhostInteract) return; @@ -61,12 +58,12 @@ namespace Content.Server.Administration.Commands else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) metaDataSystem.SetEntityName(ghost, mind.Session.Name); - mindSystem.Visit(mind, ghost); + mindSystem.Visit(mindId, ghost, mind); } else { metaDataSystem.SetEntityName(ghost, player.Name); - mindSystem.TransferTo(mind, ghost); + mindSystem.TransferTo(mindId, ghost, mind: mind); } var comp = _entities.GetComponent(ghost); diff --git a/Content.Server/Administration/Commands/ControlMob.cs b/Content.Server/Administration/Commands/ControlMob.cs index 18fb3bbb65..b0f7237429 100644 --- a/Content.Server/Administration/Commands/ControlMob.cs +++ b/Content.Server/Administration/Commands/ControlMob.cs @@ -1,10 +1,7 @@ using Content.Server.Mind; -using Content.Server.Mind.Components; -using Content.Server.Players; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; -using Robust.Shared.Utility; namespace Content.Server.Administration.Commands { @@ -45,18 +42,14 @@ namespace Content.Server.Administration.Commands return; } - if (!_entities.HasComponent(target)) + var mindSystem = _entities.System(); + if (!mindSystem.TryGetMind(target, out var mindId, out var mind)) { shell.WriteLine(Loc.GetString("shell-entity-is-not-mob")); return; } - var mind = player.ContentData()?.Mind; - - DebugTools.AssertNotNull(mind); - - var mindSystem = _entities.System(); - mindSystem.TransferTo(mind!, target); + mindSystem.TransferTo(mindId, target, mind: mind); } } } diff --git a/Content.Server/Administration/Commands/SetMindCommand.cs b/Content.Server/Administration/Commands/SetMindCommand.cs index 50263bb328..5fcb7ac8b2 100644 --- a/Content.Server/Administration/Commands/SetMindCommand.cs +++ b/Content.Server/Administration/Commands/SetMindCommand.cs @@ -68,13 +68,9 @@ namespace Content.Server.Administration.Commands } var mindSystem = entityManager.System(); + var metadata = entityManager.GetComponent(eUid); - var mind = playerCData.Mind; - if (mind == null) - { - mind = mindSystem.CreateMind(session.UserId); - mind.CharacterName = entityManager.GetComponent(eUid).EntityName; - } + var mind = playerCData.Mind ?? mindSystem.CreateMind(session.UserId, metadata.EntityName); mindSystem.TransferTo(mind, eUid, ghostOverride); } diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 48751e02c6..eab4bba78e 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -1,10 +1,9 @@ -using System.Globalization; using System.Linq; using Content.Server.Administration.Managers; -using Content.Server.GameTicking.Events; using Content.Server.IdentityManagement; -using Content.Server.Players; +using Content.Server.Mind; using Content.Server.Roles; +using Content.Server.Roles.Jobs; using Content.Shared.Administration; using Content.Shared.Administration.Events; using Content.Shared.GameTicking; @@ -20,6 +19,9 @@ namespace Content.Server.Administration.Systems { [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly JobSystem _jobs = default!; + [Dependency] private readonly MindSystem _minds = default!; + [Dependency] private readonly RoleSystem _role = default!; private readonly Dictionary _playerList = new(); @@ -102,10 +104,10 @@ namespace Content.Server.Administration.Systems private void OnRoleEvent(RoleEvent ev) { - if (!ev.Role.Antagonist || ev.Role.Mind.Session == null) + if (!ev.Antagonist || ev.Mind.Session == null) return; - UpdatePlayerList(ev.Role.Mind.Session); + UpdatePlayerList(ev.Mind.Session); } private void OnAdminPermsChanged(AdminPermsChangedEventArgs obj) @@ -169,12 +171,13 @@ namespace Content.Server.Administration.Systems identityName = Identity.Name(session.AttachedEntity.Value, EntityManager); } - var mind = data.ContentData()?.Mind; - - var job = mind?.AllRoles.FirstOrDefault(role => role is Job); - var startingRole = job != null ? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(job.Name) : string.Empty; - - var antag = mind?.AllRoles.Any(r => r.Antagonist) ?? false; + var antag = false; + var startingRole = string.Empty; + if (_minds.TryGetMind(session, out var mindId, out _)) + { + antag = _role.MindIsAntagonist(mindId); + startingRole = _jobs.MindTryGetJobName(mindId); + } var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs index f29035340e..a1e1c7a45d 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs @@ -1,4 +1,5 @@ using Content.Server.GameTicking.Rules; +using Content.Server.Mind; using Content.Server.Mind.Components; using Content.Server.Zombies; using Content.Shared.Administration; @@ -15,6 +16,7 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; [Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!; [Dependency] private readonly PiratesRuleSystem _piratesRule = default!; + [Dependency] private readonly MindSystem _minds = default!; // All antag verbs have names so invokeverb works. private void AddAntagVerbs(GetVerbsEvent args) @@ -38,10 +40,10 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"), Act = () => { - if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null) + if (!_minds.TryGetSession(targetMindComp.Mind, out var session)) return; - _traitorRule.MakeTraitor(targetMindComp.Mind.Session); + _traitorRule.MakeTraitor(session); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-traitor"), @@ -70,10 +72,10 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"), Act = () => { - if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null) + if (!_minds.TryGetMind(args.Target, out var mindId, out var mind)) return; - _nukeopsRule.MakeLoneNukie(targetMindComp.Mind); + _nukeopsRule.MakeLoneNukie(mindId, mind); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-nuclear-operative"), @@ -87,10 +89,10 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"), Act = () => { - if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null) + if (!_minds.TryGetMind(args.Target, out var mindId, out var mind)) return; - _piratesRule.MakePirate(targetMindComp.Mind); + _piratesRule.MakePirate(mindId, mind); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-pirate"), diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 030c1faafa..5667f990c2 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -11,7 +11,6 @@ using Content.Server.Ghost.Roles; using Content.Server.Mind; using Content.Server.Mind.Commands; using Content.Server.Mind.Components; -using Content.Server.Players; using Content.Server.Prayer; using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; @@ -262,11 +261,10 @@ namespace Content.Server.Administration.Systems { MakeSentientCommand.MakeSentient(args.Target, EntityManager); - var mind = player.ContentData()?.Mind; - if (mind == null) + if (!_minds.TryGetMind(player, out var mindId, out var mind)) return; - _mindSystem.TransferTo(mind, args.Target, ghostCheckOverride: true); + _mindSystem.TransferTo(mindId, args.Target, ghostCheckOverride: true, mind: mind); }, Impact = LogImpact.High, ConfirmationPopup = true diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 07fa061d51..c970ea2096 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using Content.Server.Administration.Managers; using Content.Server.Discord; using Content.Server.GameTicking; -using Content.Server.Players; +using Content.Server.Mind; using Content.Shared.Administration; using Content.Shared.CCVar; using JetBrains.Annotations; @@ -31,6 +31,7 @@ namespace Content.Server.Administration.Systems [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerLocator _playerLocator = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly MindSystem _minds = default!; private ISawmill _sawmill = default!; private readonly HttpClient _httpClient = new(); @@ -221,8 +222,6 @@ namespace Content.Server.Administration.Systems return; } - var characterName = _playerManager.GetPlayerData(userId).ContentData()?.Mind?.CharacterName; - var linkToPrevious = string.Empty; // If we have all the data required, we can link to the embed of the previous round or embed that was too long @@ -238,6 +237,7 @@ namespace Content.Server.Administration.Systems } } + var characterName = _minds.GetCharacterName(userId); existingEmbed = (null, lookup.Username, linkToPrevious, characterName, _gameTicker.RunLevel); } diff --git a/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs b/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs index f1dfa66d49..a76e56c604 100644 --- a/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs +++ b/Content.Server/Ame/EntitySystems/AmeControllerSystem.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Ame.Components; using Content.Server.Chat.Managers; @@ -15,8 +17,6 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Timing; -using System.Diagnostics.CodeAnalysis; -using System.Linq; namespace Content.Server.Ame.EntitySystems; @@ -201,7 +201,7 @@ public sealed class AmeControllerSystem : EntitySystem safeLimit = group.CoreCount * 2; if (oldValue <= safeLimit && value > safeLimit) - _chatManager.SendAdminAlert(user.Value, $"increased AME over safe limit to {controller.InjectionAmount}", mindContainer); + _chatManager.SendAdminAlert(user.Value, $"increased AME over safe limit to {controller.InjectionAmount}"); } public void AdjustInjectionAmount(EntityUid uid, int delta, int min = 0, int max = int.MaxValue, EntityUid? user = null, AmeControllerComponent? controller = null) diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index e9dee38be3..1ed9cf71c0 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -8,9 +8,7 @@ using Content.Server.Mind; using Content.Shared.Body.Components; using Content.Shared.Body.Organ; using Content.Shared.Body.Part; -using Content.Shared.Body.Prototypes; using Content.Shared.Body.Systems; -using Content.Shared.Coordinates; using Content.Shared.Humanoid; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; @@ -106,14 +104,10 @@ public sealed class BodySystem : SharedBodySystem private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args) { - if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mind)) + if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mindId, out var mind)) { - if (!mind.TimeOfDeath.HasValue) - { - mind.TimeOfDeath = _gameTiming.RealTime; - } - - _ticker.OnGhostAttempt(mind, true); + mind.TimeOfDeath ??= _gameTiming.RealTime; + _ticker.OnGhostAttempt(mindId, true, mind: mind); } } diff --git a/Content.Server/Body/Systems/BrainSystem.cs b/Content.Server/Body/Systems/BrainSystem.cs index ce3dadc628..5598226142 100644 --- a/Content.Server/Body/Systems/BrainSystem.cs +++ b/Content.Server/Body/Systems/BrainSystem.cs @@ -5,7 +5,6 @@ using Content.Server.Mind.Components; using Content.Shared.Body.Components; using Content.Shared.Body.Events; using Content.Shared.Body.Organ; -using Content.Shared.Body.Part; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; @@ -57,10 +56,10 @@ namespace Content.Server.Body.Systems _movementSpeed.ChangeBaseSpeed(newEntity, 0, 0 , 0, move); } - if (!_mindSystem.TryGetMind(oldEntity, out var mind, oldMind)) + if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind)) return; - _mindSystem.TransferTo(mind, newEntity); + _mindSystem.TransferTo(mindId, newEntity, mind: mind); } } } diff --git a/Content.Server/CharacterInfo/CharacterInfoSystem.cs b/Content.Server/CharacterInfo/CharacterInfoSystem.cs index 84131f87e3..7393056ef2 100644 --- a/Content.Server/CharacterInfo/CharacterInfoSystem.cs +++ b/Content.Server/CharacterInfo/CharacterInfoSystem.cs @@ -1,5 +1,6 @@ -using Content.Server.Mind.Components; +using Content.Server.Mind; using Content.Server.Roles; +using Content.Server.Roles.Jobs; using Content.Shared.CharacterInfo; using Content.Shared.Objectives; @@ -7,6 +8,10 @@ namespace Content.Server.CharacterInfo; public sealed class CharacterInfoSystem : EntitySystem { + [Dependency] private readonly JobSystem _jobs = default!; + [Dependency] private readonly MindSystem _minds = default!; + [Dependency] private readonly RoleSystem _roles = default!; + public override void Initialize() { base.Initialize(); @@ -25,10 +30,8 @@ public sealed class CharacterInfoSystem : EntitySystem var conditions = new Dictionary>(); var jobTitle = "No Profession"; var briefing = "!!ERROR: No Briefing!!"; //should never show on the UI unless there's an issue - if (TryComp(entity, out var mindContainerComponent) && mindContainerComponent.Mind != null) + if (_minds.TryGetMind(entity, out var mindId, out var mind)) { - var mind = mindContainerComponent.Mind; - // Get objectives foreach (var objective in mind.AllObjectives) { @@ -41,17 +44,11 @@ public sealed class CharacterInfoSystem : EntitySystem } } - // Get job title - foreach (var role in mind.AllRoles) - { - if (role.GetType() != typeof(Job)) continue; - - jobTitle = role.Name; - break; - } + if (_jobs.MindTryGetJobName(mindId, out var jobName)) + jobTitle = jobName; // Get briefing - briefing = mind.Briefing; + briefing = _roles.MindGetBriefing(mindId) ?? string.Empty; } RaiseNetworkEvent(new CharacterInfoEvent(entity, jobTitle, conditions, briefing), args.SenderSession); diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index 16c699b0a9..df3cd67f63 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -1,5 +1,5 @@ using Content.Server.GameTicking; -using Content.Server.Players; +using Content.Server.Mind; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; @@ -26,25 +26,27 @@ namespace Content.Server.Chat.Commands if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) return; - var mind = player.ContentData()?.Mind; + var minds = IoCManager.Resolve().System(); // This check also proves mind not-null for at the end when the mob is ghosted. - if (mind?.OwnedEntity is not { Valid: true } victim) + if (!minds.TryGetMind(player, out var mindId, out var mind) || + mind.OwnedEntity is not { Valid: true } victim) { shell.WriteLine("You don't have a mind!"); return; } + var gameTicker = EntitySystem.Get(); var suicideSystem = EntitySystem.Get(); if (suicideSystem.Suicide(victim)) { // Prevent the player from returning to the body. // Note that mind cannot be null because otherwise victim would be null. - gameTicker.OnGhostAttempt(mind, false); + gameTicker.OnGhostAttempt(mindId, false, mind: mind); return; } - if (gameTicker.OnGhostAttempt(mind, true)) + if (gameTicker.OnGhostAttempt(mindId, true, mind: mind)) return; shell.WriteLine("You can't ghost right now."); diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index ee55f5a91c..b92771c369 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -2,17 +2,15 @@ using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Administration.Systems; -using Content.Server.Mind.Components; +using Content.Server.Mind; using Content.Server.MoMMI; using Content.Server.Preferences.Managers; -using Content.Server.Station.Systems; using Content.Shared.Administration; using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Database; using Robust.Server.Player; using Robust.Shared.Configuration; -using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Replays; @@ -116,19 +114,19 @@ namespace Content.Server.Chat.Managers ChatMessageToMany(ChatChannel.AdminAlert, message, wrappedMessage, default, false, true, clients); } - public void SendAdminAlert(EntityUid player, string message, MindContainerComponent? mindContainerComponent = null) + public void SendAdminAlert(EntityUid player, string message) { - if ((mindContainerComponent == null && !_entityManager.TryGetComponent(player, out mindContainerComponent)) || !mindContainerComponent.HasMind) + var mindSystem = _entityManager.System(); + if (!mindSystem.TryGetMind(player, out var mindId, out var mind)) { SendAdminAlert(message); return; } var adminSystem = _entityManager.System(); - var antag = mindContainerComponent.Mind!.UserId != null - && (adminSystem.GetCachedPlayerInfo(mindContainerComponent.Mind!.UserId.Value)?.Antag ?? false); + var antag = mind.UserId != null && (adminSystem.GetCachedPlayerInfo(mind.UserId.Value)?.Antag ?? false); - SendAdminAlert($"{mindContainerComponent.Mind!.Session?.Name}{(antag ? " (ANTAG)" : "")} {message}"); + SendAdminAlert($"{mind.Session?.Name}{(antag ? " (ANTAG)" : "")} {message}"); } public void SendHookOOC(string sender, string message) diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index f3e0e8e2cf..6d81848f7e 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -1,4 +1,3 @@ -using Content.Server.Mind.Components; using Content.Shared.Chat; using Robust.Server.Player; using Robust.Shared.Network; @@ -24,7 +23,7 @@ namespace Content.Server.Chat.Managers void SendHookOOC(string sender, string message); void SendAdminAnnouncement(string message); void SendAdminAlert(string message); - void SendAdminAlert(EntityUid player, string message, MindContainerComponent? mindContainerComponent = null); + void SendAdminAlert(EntityUid player, string message); 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); diff --git a/Content.Server/Cloning/AcceptCloningEui.cs b/Content.Server/Cloning/AcceptCloningEui.cs index c3a4e67060..e1b463aaee 100644 --- a/Content.Server/Cloning/AcceptCloningEui.cs +++ b/Content.Server/Cloning/AcceptCloningEui.cs @@ -1,4 +1,5 @@ using Content.Server.EUI; +using Content.Server.Mind; using Content.Shared.Cloning; using Content.Shared.Eui; @@ -6,11 +7,13 @@ namespace Content.Server.Cloning { public sealed class AcceptCloningEui : BaseEui { + private readonly EntityUid _mindId; + private readonly MindComponent _mind; private readonly CloningSystem _cloningSystem; - private readonly Mind.Mind _mind; - public AcceptCloningEui(Mind.Mind mind, CloningSystem cloningSys) + public AcceptCloningEui(EntityUid mindId, MindComponent mind, CloningSystem cloningSys) { + _mindId = mindId; _mind = mind; _cloningSystem = cloningSys; } @@ -26,7 +29,7 @@ namespace Content.Server.Cloning return; } - _cloningSystem.TransferMindToClone(_mind); + _cloningSystem.TransferMindToClone(_mindId, _mind); Close(); } } diff --git a/Content.Server/Cloning/CloningConsoleSystem.cs b/Content.Server/Cloning/CloningConsoleSystem.cs index 165923038e..665d1ac70e 100644 --- a/Content.Server/Cloning/CloningConsoleSystem.cs +++ b/Content.Server/Cloning/CloningConsoleSystem.cs @@ -1,23 +1,23 @@ using System.Linq; -using JetBrains.Annotations; using Content.Server.Administration.Logs; -using Content.Server.Medical.Components; using Content.Server.Cloning.Components; using Content.Server.DeviceLinking.Systems; +using Content.Server.Medical.Components; +using Content.Server.Mind; using Content.Server.Power.Components; -using Content.Server.Mind.Components; -using Content.Server.UserInterface; using Content.Server.Power.EntitySystems; -using Robust.Server.GameObjects; -using Robust.Server.Player; -using Content.Shared.Cloning.CloningConsole; +using Content.Server.UserInterface; using Content.Shared.Cloning; +using Content.Shared.Cloning.CloningConsole; using Content.Shared.Database; using Content.Shared.DeviceLinking; using Content.Shared.DeviceLinking.Events; using Content.Shared.IdentityManagement; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Server.Player; namespace Content.Server.Cloning { @@ -31,6 +31,8 @@ namespace Content.Server.Cloning [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; + public override void Initialize() { base.Initialize(); @@ -162,12 +164,10 @@ namespace Content.Server.Cloning if (body is null) return; - if (!TryComp(body, out var mindComp)) + if (!_mindSystem.TryGetMind(body.Value, out var mindId, out var mind)) return; - var mind = mindComp.Mind; - - if (mind == null || mind.UserId.HasValue == false || mind.Session == null) + if (mind.UserId.HasValue == false || mind.Session == null) return; if (_cloningSystem.TryCloning(cloningPodUid, body.Value, mind, cloningPod, scannerComp.CloningFailChanceMultiplier)) @@ -212,15 +212,15 @@ namespace Content.Server.Cloning { scanBodyInfo = MetaData(scanBody.Value).EntityName; - TryComp(scanBody, out var mindComp); - if (!_mobStateSystem.IsDead(scanBody.Value)) { clonerStatus = ClonerStatus.ScannerOccupantAlive; } else { - if (mindComp == null || mindComp.Mind == null || mindComp.Mind.UserId == null || !_playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out _)) + if (!_mindSystem.TryGetMind(scanBody.Value, out _, out var mind) || + mind.UserId == null || + !_playerManager.TryGetSessionById(mind.UserId.Value, out _)) { clonerStatus = ClonerStatus.NoMindDetected; } diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 4448bc6bc0..1eb9f01ced 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -1,41 +1,40 @@ -using Content.Shared.GameTicking; -using Content.Shared.Damage; -using Content.Shared.Examine; -using Content.Shared.Cloning; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Chat.Systems; +using Content.Server.Cloning.Components; +using Content.Server.Construction; +using Content.Server.DeviceLinking.Systems; +using Content.Server.EUI; +using Content.Server.Fluids.EntitySystems; +using Content.Server.Humanoid; +using Content.Server.Jobs; +using Content.Server.Materials; +using Content.Server.Mind; +using Content.Server.Mind.Components; +using Content.Server.Popups; +using Content.Server.Power.EntitySystems; +using Content.Server.Roles.Jobs; +using Content.Server.Traits.Assorted; using Content.Shared.Atmos; using Content.Shared.CCVar; -using Content.Server.Cloning.Components; -using Content.Server.Mind.Components; -using Content.Server.Power.EntitySystems; -using Content.Server.Atmos.EntitySystems; -using Content.Server.EUI; -using Content.Server.Humanoid; using Content.Shared.Chemistry.Components; -using Content.Server.Fluids.EntitySystems; -using Content.Server.Chat.Systems; -using Content.Server.Construction; -using Content.Server.DeviceLinking.Events; -using Content.Server.DeviceLinking.Systems; -using Content.Server.Materials; -using Content.Server.Jobs; +using Content.Shared.Cloning; +using Content.Shared.Damage; using Content.Shared.DeviceLinking.Events; using Content.Shared.Emag.Components; -using Content.Server.Mind; +using Content.Shared.Emag.Systems; +using Content.Shared.Examine; +using Content.Shared.GameTicking; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; -using Content.Shared.Zombies; using Content.Shared.Mobs.Systems; -using Robust.Server.GameObjects; using Robust.Server.Containers; +using Robust.Server.GameObjects; using Robust.Server.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Physics.Components; -using Content.Shared.Emag.Systems; -using Content.Server.Popups; -using Content.Server.Traits.Assorted; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.Cloning { @@ -62,8 +61,9 @@ namespace Content.Server.Cloning [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!; + [Dependency] private readonly JobSystem _jobs = default!; - public readonly Dictionary ClonesWaitingForMind = new(); + public readonly Dictionary ClonesWaitingForMind = new(); public const float EasyModeCloningCost = 0.7f; public override void Initialize() @@ -102,7 +102,7 @@ namespace Content.Server.Cloning args.AddPercentageUpgrade("cloning-pod-component-upgrade-biomass-requirement", component.BiomassRequirementMultiplier); } - internal void TransferMindToClone(Mind.Mind mind) + internal void TransferMindToClone(EntityUid mindId, MindComponent mind) { if (!ClonesWaitingForMind.TryGetValue(mind, out var entity) || !EntityManager.EntityExists(entity) || @@ -110,8 +110,8 @@ namespace Content.Server.Cloning mindComp.Mind != null) return; - _mindSystem.TransferTo(mind, entity, ghostCheckOverride: true); - _mindSystem.UnVisit(mind); + _mindSystem.TransferTo(mindId, entity, ghostCheckOverride: true, mind: mind); + _mindSystem.UnVisit(mindId, mind); ClonesWaitingForMind.Remove(mind); } @@ -154,7 +154,7 @@ namespace Content.Server.Cloning args.PushMarkup(Loc.GetString("cloning-pod-biomass", ("number", _material.GetMaterialAmount(uid, component.RequiredMaterial)))); } - public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Mind.Mind mind, CloningPodComponent? clonePod, float failChanceModifier = 1) + public bool TryCloning(EntityUid uid, EntityUid bodyToClone, MindComponent mind, CloningPodComponent? clonePod, float failChanceModifier = 1) { if (!Resolve(uid, ref clonePod)) return false; @@ -167,7 +167,7 @@ namespace Content.Server.Cloning if (EntityManager.EntityExists(clone) && !_mobStateSystem.IsDead(clone) && TryComp(clone, out var cloneMindComp) && - (cloneMindComp.Mind == null || cloneMindComp.Mind == mind)) + (cloneMindComp.Mind == null || cloneMindComp.Mind == mind.Owner)) return false; // Mind already has clone ClonesWaitingForMind.Remove(mind); @@ -253,16 +253,16 @@ namespace Content.Server.Cloning clonePod.BodyContainer.Insert(mob); ClonesWaitingForMind.Add(mind, mob); UpdateStatus(uid, CloningPodStatus.NoMind, clonePod); - _euiManager.OpenEui(new AcceptCloningEui(mind, this), client); + var mindId = mind.Owner; + _euiManager.OpenEui(new AcceptCloningEui(mindId, mind, this), client); AddComp(uid); - // TODO: Ideally, components like this should be on a mind entity so this isn't neccesary. - // Remove this when 'mind entities' are added. + // TODO: Ideally, components like this should be components on the mind entity so this isn't necessary. // Add on special job components to the mob. - if (mind.CurrentJob != null) + if (_jobs.MindTryGetJob(mindId, out _, out var prototype)) { - foreach (var special in mind.CurrentJob.Prototype.Special) + foreach (var special in prototype.Special) { if (special is AddComponentSpecial) special.AfterEquip(mob); diff --git a/Content.Server/Cloning/Components/BeingClonedComponent.cs b/Content.Server/Cloning/Components/BeingClonedComponent.cs index a3e6ce220d..c3a4f80002 100644 --- a/Content.Server/Cloning/Components/BeingClonedComponent.cs +++ b/Content.Server/Cloning/Components/BeingClonedComponent.cs @@ -1,10 +1,12 @@ +using Content.Server.Mind; + namespace Content.Server.Cloning.Components { [RegisterComponent] public sealed partial class BeingClonedComponent : Component { [ViewVariables] - public Mind.Mind? Mind = default; + public MindComponent? Mind = default; [ViewVariables] public EntityUid Parent; diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs index a74903a235..8ce1003b02 100644 --- a/Content.Server/GameTicking/GameTicker.GamePreset.cs +++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Content.Server.GameTicking.Presets; using Content.Server.Maps; +using Content.Server.Mind; using Content.Shared.CCVar; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; @@ -192,8 +193,11 @@ namespace Content.Server.GameTicking } } - public bool OnGhostAttempt(Mind.Mind mind, bool canReturnGlobal, bool viaCommand = false) + public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaCommand = false, MindComponent? mind = null) { + if (!Resolve(mindId, ref mind)) + return false; + var playerEntity = mind.CurrentEntity; if (playerEntity != null && viaCommand) @@ -222,7 +226,7 @@ namespace Content.Server.GameTicking if (mind.VisitingEntity != default) { - _mind.UnVisit(mind); + _mind.UnVisit(mindId, mind: mind); } var position = Exists(playerEntity) @@ -281,9 +285,9 @@ namespace Content.Server.GameTicking _ghost.SetCanReturnToBody(ghostComponent, canReturn); if (canReturn) - _mind.Visit(mind, ghost); + _mind.Visit(mindId, ghost, mind); else - _mind.TransferTo(mind, ghost); + _mind.TransferTo(mindId, ghost, mind: mind); return true; } @@ -308,11 +312,11 @@ namespace Content.Server.GameTicking public sealed class GhostAttemptHandleEvent : HandledEntityEventArgs { - public Mind.Mind Mind { get; } + public MindComponent Mind { get; } public bool CanReturnGlobal { get; } public bool Result { get; set; } - public GhostAttemptHandleEvent(Mind.Mind mind, bool canReturnGlobal) + public GhostAttemptHandleEvent(MindComponent mind, bool canReturnGlobal) { Mind = mind; CanReturnGlobal = canReturnGlobal; diff --git a/Content.Server/GameTicking/GameTicker.Player.cs b/Content.Server/GameTicking/GameTicker.Player.cs index 894d133f4d..6eb35be528 100644 --- a/Content.Server/GameTicking/GameTicker.Player.cs +++ b/Content.Server/GameTicking/GameTicker.Player.cs @@ -8,7 +8,6 @@ using Robust.Server.Player; using Robust.Shared.Enums; using Robust.Shared.Timing; using Robust.Shared.Utility; -using PlayerData = Content.Server.Players.PlayerData; namespace Content.Server.GameTicking { @@ -27,7 +26,7 @@ namespace Content.Server.GameTicking { var session = args.Session; - if (_mind.TryGetMind(session.UserId, out var mind)) + if (_mind.TryGetMind(session.UserId, out var mindId, out var mind)) { if (args.OldStatus == SessionStatus.Connecting && args.NewStatus == SessionStatus.Connected) mind.Session = session; @@ -35,7 +34,7 @@ namespace Content.Server.GameTicking DebugTools.Assert(mind.Session == session); } - DebugTools.Assert(session.GetMind() == mind); + DebugTools.Assert(session.GetMind() == mindId); switch (args.NewStatus) { @@ -47,7 +46,7 @@ namespace Content.Server.GameTicking if (session.Data.ContentDataUncast == null) { var data = new PlayerData(session.UserId, args.Session.Name); - data.Mind = mind; + data.Mind = mindId; session.Data.ContentDataUncast = data; } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index aa66ee9372..00515c614c 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -1,25 +1,25 @@ +using System.Linq; using Content.Server.Announcements; +using Content.Server.Discord; using Content.Server.GameTicking.Events; using Content.Server.Ghost; using Content.Server.Maps; +using Content.Server.Mind; using Content.Server.Players; +using Content.Shared.Database; using Content.Shared.GameTicking; using Content.Shared.Preferences; using JetBrains.Annotations; using Prometheus; using Robust.Server.Maps; using Robust.Server.Player; +using Robust.Shared.Asynchronous; using Robust.Shared.Audio; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Random; using Robust.Shared.Utility; -using System.Linq; -using Content.Server.Discord; -using Content.Shared.Database; -using Robust.Shared.Asynchronous; -using PlayerData = Content.Server.Players.PlayerData; namespace Content.Server.GameTicking { @@ -329,8 +329,8 @@ namespace Content.Server.GameTicking //Generate a list of basic player info to display in the end round summary. var listOfPlayerInfo = new List(); // Grab the great big book of all the Minds, we'll need them for this. - var allMinds = _mindTracker.AllMinds; - foreach (var mind in allMinds) + var allMinds = EntityQueryEnumerator(); + while (allMinds.MoveNext(out var mindId, out var mind)) { // TODO don't list redundant observer roles? // I.e., if a player was an observer ghost, then a hamster ghost role, maybe just list hamster and not @@ -338,7 +338,7 @@ namespace Content.Server.GameTicking var userId = mind.UserId ?? mind.OriginalOwnerUserId; var connected = false; - var observer = mind.AllRoles.Any(role => role is ObserverRole); + var observer = HasComp(mindId); // Continuing if (userId != null && _playerManager.ValidSessionId(userId.Value)) { @@ -350,7 +350,8 @@ namespace Content.Server.GameTicking contentPlayerData = playerData.ContentData(); } // Finish - var antag = mind.AllRoles.Any(role => role.Antagonist); + + var antag = _roles.MindIsAntagonist(mindId); var playerIcName = "Unknown"; @@ -363,6 +364,8 @@ namespace Content.Server.GameTicking if (Exists(entity)) _pvsOverride.AddGlobalOverride(entity.Value, recursive: true); + var roles = _roles.MindGetAllRoles(mindId); + var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo() { // Note that contentPlayerData?.Name sticks around after the player is disconnected. @@ -372,8 +375,8 @@ namespace Content.Server.GameTicking PlayerICName = playerIcName, PlayerEntityUid = entity, Role = antag - ? mind.AllRoles.First(role => role.Antagonist).Name - : mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("game-ticker-unknown-role"), + ? roles.First(role => role.Antagonist).Name + : roles.FirstOrDefault().Name ?? Loc.GetString("game-ticker-unknown-role"), Antag = antag, Observer = observer, Connected = connected @@ -496,6 +499,13 @@ namespace Content.Server.GameTicking PlayerJoinLobby(player); } + // Round restart cleanup event, so entity systems can reset. + var ev = new RoundRestartCleanupEvent(); + RaiseLocalEvent(ev); + + // So clients' entity systems can clean up too... + RaiseNetworkEvent(ev, Filter.Broadcast()); + // Delete all entities. foreach (var entity in EntityManager.GetEntities().ToArray()) { @@ -530,13 +540,6 @@ namespace Content.Server.GameTicking _allPreviousGameRules.Clear(); - // Round restart cleanup event, so entity systems can reset. - var ev = new RoundRestartCleanupEvent(); - RaiseLocalEvent(ev); - - // So clients' entity systems can clean up too... - RaiseNetworkEvent(ev, Filter.Broadcast()); - DisallowLateJoin = false; _playerGameStatuses.Clear(); foreach (var session in _playerManager.ServerSessions) diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index bde73605b3..ee1dfc598a 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -4,6 +4,7 @@ using System.Numerics; using Content.Server.Administration.Managers; using Content.Server.Ghost; using Content.Server.Players; +using Content.Server.Roles.Jobs; using Content.Server.Spawners.Components; using Content.Server.Speech.Components; using Content.Server.Station.Components; @@ -18,13 +19,13 @@ using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; -using Job = Content.Server.Roles.Job; namespace Content.Server.GameTicking { public sealed partial class GameTicker { [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly JobSystem _jobs = default!; [ValidatePrototypeId] private const string ObserverPrototypeName = "MobObserver"; @@ -186,12 +187,12 @@ namespace Content.Server.GameTicking _mind.SetUserId(newMind, data.UserId); var jobPrototype = _prototypeManager.Index(jobId); - var job = new Job(newMind, jobPrototype); - _mind.AddRole(newMind, job); + var job = new JobComponent { PrototypeId = jobId }; + _roles.MindAddRole(newMind, job); + var jobName = _jobs.MindTryGetJobName(newMind); _playTimeTrackings.PlayerRolesChanged(player); - var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character); DebugTools.AssertNotNull(mobMaybe); var mob = mobMaybe!.Value; @@ -204,7 +205,7 @@ namespace Content.Server.GameTicking Loc.GetString( "latejoin-arrival-announcement", ("character", MetaData(mob).EntityName), - ("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(job.Name)) + ("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName)) ), Loc.GetString("latejoin-arrival-sender"), playDefaultSound: false); } @@ -217,9 +218,9 @@ namespace Content.Server.GameTicking _stationJobs.TryAssignJob(station, jobPrototype); if (lateJoin) - _adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {job.Name:jobName}."); + _adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}."); else - _adminLogger.Add(LogType.RoundStartJoin, LogImpact.Medium, $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {job.Name:jobName}."); + _adminLogger.Add(LogType.RoundStartJoin, LogImpact.Medium, $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}."); // Make sure they're aware of extended access. if (Comp(station).ExtendedAccess @@ -305,15 +306,15 @@ namespace Content.Server.GameTicking if (mind == null) { mind = _mind.CreateMind(player.UserId); - _mind.SetUserId(mind, player.UserId); - _mind.AddRole(mind, new ObserverRole(mind)); + _mind.SetUserId(mind.Value, player.UserId); + _roles.MindAddRole(mind.Value, new ObserverRoleComponent()); } var name = GetPlayerProfile(player).Name; var ghost = SpawnObserverMob(); _metaData.SetEntityName(ghost, name); _ghost.SetCanReturnToBody(ghost, false); - _mind.TransferTo(mind, ghost); + _mind.TransferTo(mind.Value, ghost); } #region Mob Spawning Helpers diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index bd80ef0bb3..88e1427572 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -8,6 +8,7 @@ using Content.Server.Maps; using Content.Server.Mind; using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; +using Content.Server.Roles; using Content.Server.ServerUpdates; using Content.Server.Shuttles.Systems; using Content.Server.Station.Systems; @@ -21,14 +22,14 @@ using Robust.Server.GameObjects; using Robust.Server.GameStates; using Robust.Shared.Configuration; using Robust.Shared.Console; -#if EXCEPTION_TOLERANCE -using Robust.Shared.Exceptions; -#endif using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.Utility; +#if EXCEPTION_TOLERANCE +using Robust.Shared.Exceptions; +#endif namespace Content.Server.GameTicking { @@ -57,7 +58,6 @@ namespace Content.Server.GameTicking [Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly GhostSystem _ghost = default!; [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly MindTrackerSystem _mindTracker = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!; [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; @@ -67,6 +67,7 @@ namespace Content.Server.GameTicking [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly UserDbDataManager _userDb = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly RoleSystem _roles = default!; [ViewVariables] private bool _initialized; [ViewVariables] private bool _postInitialized; diff --git a/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs index def1ed3de0..1d03b41d77 100644 --- a/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs @@ -6,7 +6,7 @@ namespace Content.Server.GameTicking.Rules.Components; public sealed partial class PiratesRuleComponent : Component { [ViewVariables] - public List Pirates = new(); + public List Pirates = new(); [ViewVariables] public EntityUid PirateShip = EntityUid.Invalid; [ViewVariables] diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index 8c010b17c3..883abef52f 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -1,5 +1,4 @@ -using Content.Server.Roles; -using Content.Shared.Preferences; +using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Audio; @@ -10,12 +9,12 @@ namespace Content.Server.GameTicking.Rules.Components; [RegisterComponent, Access(typeof(TraitorRuleSystem))] public sealed partial class TraitorRuleComponent : Component { - public List Traitors = new(); + public readonly List TraitorMinds = new(); [DataField("traitorPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer))] public string TraitorPrototypeId = "Traitor"; - public int TotalTraitors => Traitors.Count; + public int TotalTraitors => TraitorMinds.Count; public string[] Codewords = new string[3]; public enum SelectionState diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 7295bae805..63aee534ee 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -6,7 +6,6 @@ using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Events; using Content.Server.Humanoid; -using Content.Server.Humanoid.Systems; using Content.Server.Mind; using Content.Server.Mind.Components; using Content.Server.NPC.Components; @@ -20,7 +19,6 @@ using Content.Server.Shuttles.Systems; using Content.Server.Spawners.Components; using Content.Server.Station.Components; using Content.Server.Station.Systems; -using Content.Server.Traitor; using Content.Shared.Dataset; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; @@ -58,8 +56,12 @@ public sealed class NukeopsRuleSystem : GameRuleSystem [Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly ShuttleSystem _shuttle = default!; [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly RoleSystem _roles = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [ValidatePrototypeId] + public const string NukeopsId = "Nukeops"; + public override void Initialize() { base.Initialize(); @@ -87,10 +89,10 @@ public sealed class NukeopsRuleSystem : GameRuleSystem continue; // If entity has a prior mind attached, add them to the players list. - if (!TryComp(uid, out var mindComponent)) + if (!_mindSystem.TryGetMind(uid, out _, out var mind)) continue; - var session = mindComponent.Mind?.Session; + var session = mind?.Session; var name = MetaData(uid).EntityName; if (session != null) nukeops.OperativePlayers.Add(name, session); @@ -592,22 +594,21 @@ public sealed class NukeopsRuleSystem : GameRuleSystem private void OnMindAdded(EntityUid uid, NukeOperativeComponent component, MindAddedMessage args) { - if (!TryComp(uid, out var mindContainerComponent) || mindContainerComponent.Mind == null) + if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind)) return; - var mind = mindContainerComponent.Mind; - foreach (var nukeops in EntityQuery()) { if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || !nukeops.EndsRound) { role ??= nukeops.OperativeRoleProto; - _mindSystem.AddRole(mind, new NukeopsRole(mind, _prototypeManager.Index(role))); + _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = role }); nukeops.OperativeMindPendingData.Remove(uid); } - if (!_mindSystem.TryGetSession(mind, out var playerSession)) + if (mind.Session is not { } playerSession) return; + if (nukeops.OperativePlayers.ContainsValue(playerSession)) return; @@ -778,7 +779,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem SetupOperativeEntity(mob, spawnDetails.Name, spawnDetails.Gear, profile, component); var newMind = _mindSystem.CreateMind(session.UserId, spawnDetails.Name); _mindSystem.SetUserId(newMind, session.UserId); - _mindSystem.AddRole(newMind, new NukeopsRole(newMind, nukeOpsAntag)); + _roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = spawnDetails.Role }); _mindSystem.TransferTo(newMind, mob); } @@ -820,13 +821,13 @@ public sealed class NukeopsRuleSystem : GameRuleSystem } //For admins forcing someone to nukeOps. - public void MakeLoneNukie(Mind.Mind mind) + public void MakeLoneNukie(EntityUid mindId, MindComponent mind) { if (!mind.OwnedEntity.HasValue) return; //ok hardcoded value bad but so is everything else here - _mindSystem.AddRole(mind, new NukeopsRole(mind, _prototypeManager.Index("Nukeops"))); + _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = NukeopsId }, mind); SetOutfitCommand.SetOutfit(mind.OwnedEntity.Value, "SyndicateOperativeGearFull", EntityManager); } @@ -878,7 +879,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem var query = EntityQuery(true); foreach (var (_, mindComp, metaData) in query) { - if (!mindComp.HasMind || !_mindSystem.TryGetSession(mindComp.Mind, out var session)) + if (!mindComp.HasMind || !_mindSystem.TryGetSession(mindComp.Mind.Value, out var session)) continue; component.OperativePlayers.Add(metaData.EntityName, session); } diff --git a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs index ddaf387995..0410e0ff57 100644 --- a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs @@ -16,14 +16,12 @@ using Content.Shared.Roles; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Server.Player; -using Robust.Shared.Audio; using Robust.Shared.Configuration; +using Robust.Shared.Enums; using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; -using Robust.Shared.Enums; -using Robust.Shared.Player; namespace Content.Server.GameTicking.Rules; @@ -68,15 +66,14 @@ public sealed class PiratesRuleSystem : GameRuleSystem } else { - List<(double, EntityUid)> mostValuableThefts = new(); var comp1 = pirates; var finalValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid => { - foreach (var mind in comp1.Pirates) + foreach (var mindId in comp1.Pirates) { - if (mind.CurrentEntity == uid) + if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity == uid) return false; // Don't appraise the pirates twice, we count them in separately. } @@ -92,9 +89,9 @@ public sealed class PiratesRuleSystem : GameRuleSystem mostValuableThefts.Pop(); }); - foreach (var mind in pirates.Pirates) + foreach (var mindId in pirates.Pirates) { - if (mind.CurrentEntity is not null) + if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity is not null) finalValue += _pricingSystem.GetPrice(mind.CurrentEntity.Value); } @@ -119,7 +116,10 @@ public sealed class PiratesRuleSystem : GameRuleSystem ev.AddLine(Loc.GetString("pirates-list-start")); foreach (var pirate in pirates.Pirates) { - ev.AddLine($"- {pirate.CharacterName} ({pirate.Session?.Name})"); + if (TryComp(pirate, out MindComponent? mind)) + { + ev.AddLine($"- {mind.CharacterName} ({mind.Session?.Name})"); + } } } } @@ -239,7 +239,7 @@ public sealed class PiratesRuleSystem : GameRuleSystem } //Forcing one player to be a pirate. - public void MakePirate(Mind.Mind mind) + public void MakePirate(EntityUid mindId, MindComponent mind) { if (!mind.OwnedEntity.HasValue) return; diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 8778e29ec0..9446e2ad8c 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -1,19 +1,19 @@ using System.Linq; using Content.Server.Chat.Managers; using Content.Server.GameTicking.Rules.Components; -using Content.Server.NPC.Systems; using Content.Server.Mind; -using Content.Server.Objectives.Interfaces; +using Content.Server.NPC.Systems; +using Content.Server.Objectives; using Content.Server.PDA.Ringer; -using Content.Server.Players; using Content.Server.Roles; +using Content.Server.Roles.Jobs; using Content.Server.Shuttles.Components; using Content.Server.Traitor.Uplink; using Content.Shared.CCVar; using Content.Shared.Dataset; -using Content.Shared.Preferences; using Content.Shared.Mobs.Systems; using Content.Shared.PDA; +using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Configuration; @@ -28,16 +28,17 @@ public sealed class TraitorRuleSystem : GameRuleSystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly IObjectivesManager _objectivesManager = default!; [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IComponentFactory _component = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly UplinkSystem _uplink = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!; - - private ISawmill _sawmill = default!; + [Dependency] private readonly RoleSystem _roleSystem = default!; + [Dependency] private readonly JobSystem _jobs = default!; + [Dependency] private readonly ObjectivesSystem _objectives = default!; private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor); private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors); @@ -46,8 +47,6 @@ public sealed class TraitorRuleSystem : GameRuleSystem { base.Initialize(); - _sawmill = Logger.GetSawmill("preset"); - SubscribeLocalEvent(OnStartAttempt); SubscribeLocalEvent(OnPlayersSpawned); SubscribeLocalEvent(HandleLatejoin); @@ -107,7 +106,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem { if (!component.StartCandidates.Any()) { - _sawmill.Error("Tried to start Traitor mode without any candidates."); + Log.Error("Tried to start Traitor mode without any candidates."); return; } @@ -156,7 +155,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem foreach (var player in candidates.Keys) { // Role prevents antag. - if (!(player.Data.ContentData()?.Mind?.AllRoles.All(role => role is not Job { CanBeAntag: false }) ?? false)) + if (!_jobs.CanBeAntag(player)) { continue; } @@ -180,7 +179,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem } if (prefList.Count == 0) { - _sawmill.Info("Insufficient preferred traitors, picking at random."); + Log.Info("Insufficient preferred traitors, picking at random."); prefList = list; } return prefList; @@ -191,14 +190,14 @@ public sealed class TraitorRuleSystem : GameRuleSystem var results = new List(traitorCount); if (prefList.Count == 0) { - _sawmill.Info("Insufficient ready players to fill up with traitors, stopping the selection."); + Log.Info("Insufficient ready players to fill up with traitors, stopping the selection."); return results; } for (var i = 0; i < traitorCount; i++) { results.Add(_random.PickAndTake(prefList)); - _sawmill.Info("Selected a preferred traitor."); + Log.Info("Selected a preferred traitor."); } return results; } @@ -215,22 +214,28 @@ public sealed class TraitorRuleSystem : GameRuleSystem MakeCodewords(traitorRule); } - var mind = traitor.Data.ContentData()?.Mind; - if (mind == null) + if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) { - _sawmill.Info("Failed getting mind for picked traitor."); + Log.Info("Failed getting mind for picked traitor."); return false; } + + if (HasComp(mindId)) + { + Log.Error($"Player {traitor.Name} is already a traitor."); + return false; + } + if (mind.OwnedEntity is not { } entity) { - Logger.ErrorS("preset", "Mind picked for traitor did not have an attached entity."); + Log.Error("Mind picked for traitor did not have an attached entity."); return false; } // Calculate the amount of currency on the uplink. var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance); - if (mind.CurrentJob != null) - startingBalance = Math.Max(startingBalance - mind.CurrentJob.Prototype.AntagAdvantage, 0); + if (_jobs.MindTryGetJob(mindId, out _, out var prototype)) + startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0); // creadth: we need to create uplink for the antag. // PDA should be in place already @@ -241,22 +246,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem // Add the ringtone uplink and get its code for greeting var code = EnsureComp(pda.Value).Code; - // Prepare antagonist role - var antagPrototype = _prototypeManager.Index(traitorRule.TraitorPrototypeId); - var traitorRole = new TraitorRole(mind, antagPrototype); - + // Prepare traitor role // Give traitors their codewords and uplink code to keep in their character info menu - traitorRole.Mind.Briefing = string.Format( - "{0}\n{1}", - Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", traitorRule.Codewords))), - Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp","#")))); + var briefing = + $"{Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", traitorRule.Codewords)))}\n{Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))}"; + var traitorRole = new TraitorRoleComponent + { + PrototypeId = traitorRule.TraitorPrototypeId, + Briefing = briefing + }; // Assign traitor roles - _mindSystem.AddRole(mind, traitorRole); - SendTraitorBriefing(mind, traitorRule.Codewords, code); - traitorRule.Traitors.Add(traitorRole); + _roleSystem.MindAddRole(mindId, traitorRole); + SendTraitorBriefing(mindId, traitorRule.Codewords, code); + traitorRule.TraitorMinds.Add(mindId); - if (_mindSystem.TryGetSession(mind, out var session)) + if (_mindSystem.TryGetSession(mindId, out var session)) { // Notificate player about new role assignment _audioSystem.PlayGlobal(traitorRule.GreetSoundNotification, session); @@ -272,11 +277,11 @@ public sealed class TraitorRuleSystem : GameRuleSystem var difficulty = 0f; for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++) { - var objective = _objectivesManager.GetRandomObjective(traitorRole.Mind, "TraitorObjectiveGroups"); + var objective = _objectives.GetRandomObjective(mindId, mind, "TraitorObjectiveGroups"); if (objective == null) continue; - if (_mindSystem.TryAddObjective(traitorRole.Mind, objective)) + if (_mindSystem.TryAddObjective(mindId, mind, objective)) difficulty += objective.Difficulty; } @@ -289,7 +294,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem /// A mind (player) /// Codewords /// Uplink codes - private void SendTraitorBriefing(Mind.Mind mind, string[] codewords, Note[] code) + private void SendTraitorBriefing(EntityUid mind, string[] codewords, Note[] code) { if (_mindSystem.TryGetSession(mind, out var session)) { @@ -362,18 +367,21 @@ public sealed class TraitorRuleSystem : GameRuleSystem if (!GameTicker.IsGameRuleAdded(uid, gameRule)) continue; - var result = Loc.GetString("traitor-round-end-result", ("traitorCount", traitor.Traitors.Count)); + var result = Loc.GetString("traitor-round-end-result", ("traitorCount", traitor.TraitorMinds.Count)); result += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", traitor.Codewords))) + "\n"; - foreach (var t in traitor.Traitors) + foreach (var mindId in traitor.TraitorMinds) { - var name = t.Mind.CharacterName; - _mindSystem.TryGetSession(t.Mind, out var session); + if (!TryComp(mindId, out MindComponent? mind)) + continue; + + var name = mind.CharacterName; + _mindSystem.TryGetSession(mindId, out var session); var username = session?.Name; - var objectives = t.Mind.AllObjectives.ToArray(); + var objectives = mind.AllObjectives.ToArray(); if (objectives.Length == 0) { if (username != null) @@ -437,12 +445,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem } } - public List GetOtherTraitorsAliveAndConnected(Mind.Mind ourMind) + public List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind) { - List allTraitors = new(); + List<(EntityUid Id, MindComponent Mind)> allTraitors = new(); foreach (var traitor in EntityQuery()) { - foreach (var role in GetOtherTraitorsAliveAndConnected(ourMind, traitor)) + foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, traitor)) { if (!allTraitors.Contains(role)) allTraitors.Add(role); @@ -452,13 +460,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem return allTraitors; } - private List GetOtherTraitorsAliveAndConnected(Mind.Mind ourMind, TraitorRuleComponent component) + private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, TraitorRuleComponent component) { - return component.Traitors // don't want - .Where(t => t.Mind.OwnedEntity is not null) // no entity - .Where(t => t.Mind.Session is not null) // player disconnected - .Where(t => t.Mind != ourMind) // ourselves - .Where(t => _mobStateSystem.IsAlive((EntityUid) t.Mind.OwnedEntity!)) // dead - .Where(t => t.Mind.CurrentEntity == t.Mind.OwnedEntity).ToList(); // not in original body + var traitors = new List<(EntityUid Id, MindComponent Mind)>(); + foreach (var traitor in component.TraitorMinds) + { + if (TryComp(traitor, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + mind != ourMind && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor, mind)); + } + } + + return traitors; } } diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index 592f30adee..f281f14d54 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -5,8 +5,6 @@ using Content.Server.Chat.Managers; using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; -using Content.Server.Mind.Components; -using Content.Server.Players; using Content.Server.Popups; using Content.Server.Preferences.Managers; using Content.Server.Roles; @@ -21,7 +19,6 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Preferences; -using Content.Shared.Roles; using Content.Shared.Zombies; using Robust.Server.GameObjects; using Robust.Server.Player; @@ -48,6 +45,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly ZombieSystem _zombie = default!; [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly RoleSystem _roles = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -97,10 +95,9 @@ public sealed class ZombieRuleSystem : GameRuleSystem { var meta = MetaData(survivor); var username = string.Empty; - if (TryComp(survivor, out var mindcomp)) + if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null) { - if (mindcomp.Mind != null && mindcomp.Mind.Session != null) - username = mindcomp.Mind.Session.Name; + username = mind.Session.Name; } ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", @@ -312,12 +309,15 @@ public sealed class ZombieRuleSystem : GameRuleSystem prefList.Remove(zombie); playerList.Remove(zombie); - if (zombie.Data.ContentData()?.Mind is not { } mind || mind.OwnedEntity is not { } ownedEntity) + if (!_mindSystem.TryGetMind(zombie, out var mindId, out var mind) || + mind.OwnedEntity is not { } ownedEntity) + { continue; + } totalInfected++; - _mindSystem.AddRole(mind, new ZombieRole(mind, _prototypeManager.Index(component.PatientZeroPrototypeId))); + _roles.MindAddRole(mindId, new ZombieRoleComponent { PrototypeId = component.PatientZeroPrototypeId }); var pending = EnsureComp(ownedEntity); pending.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace); diff --git a/Content.Server/Ghost/Ghost.cs b/Content.Server/Ghost/Ghost.cs index c539c119fa..9204b107dd 100644 --- a/Content.Server/Ghost/Ghost.cs +++ b/Content.Server/Ghost/Ghost.cs @@ -1,5 +1,5 @@ using Content.Server.GameTicking; -using Content.Server.Players; +using Content.Server.Mind; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; @@ -9,6 +9,8 @@ namespace Content.Server.Ghost [AnyCommand] public sealed class Ghost : IConsoleCommand { + [Dependency] private readonly IEntityManager _entities = default!; + public string Command => "ghost"; public string Description => "Give up on life and become a ghost."; public string Help => "ghost"; @@ -22,14 +24,14 @@ namespace Content.Server.Ghost return; } - var mind = player.ContentData()?.Mind; - if (mind == null) + var minds = _entities.System(); + if (!minds.TryGetMind(player, out var mindId, out var mind)) { shell.WriteLine("You have no Mind, you can't ghost."); return; } - if (!EntitySystem.Get().OnGhostAttempt(mind, true, viaCommand:true)) + if (!EntitySystem.Get().OnGhostAttempt(mindId, true, true, mind)) { shell.WriteLine("You can't ghost right now."); } diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index af34f432ef..f66ec36b5f 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -4,7 +4,7 @@ using Content.Server.GameTicking; using Content.Server.Ghost.Components; using Content.Server.Mind; using Content.Server.Mind.Components; -using Content.Server.Players; +using Content.Server.Roles.Jobs; using Content.Server.Visible; using Content.Server.Warps; using Content.Shared.Actions; @@ -16,7 +16,6 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; using Content.Shared.Storage.Components; -using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Console; @@ -38,6 +37,8 @@ namespace Content.Server.Ghost [Dependency] private readonly FollowerSystem _followerSystem = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly MindSystem _minds = default!; + [Dependency] private readonly JobSystem _jobs = default!; public override void Initialize() { @@ -92,13 +93,13 @@ namespace Content.Server.Ghost if (EntityManager.HasComponent(uid)) return; - if (!EntityManager.TryGetComponent(uid, out var mind) || !mind.HasMind || mind.Mind.IsVisitingEntity) + if (!_minds.TryGetMind(uid, out var mindId, out var mind) || mind.IsVisitingEntity) return; if (component.MustBeDead && (_mobState.IsAlive(uid) || _mobState.IsCritical(uid))) return; - _ticker.OnGhostAttempt(mind.Mind, component.CanReturn); + _ticker.OnGhostAttempt(mindId, component.CanReturn, mind: mind); } private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentStartup args) @@ -199,7 +200,7 @@ namespace Content.Server.Ghost return; } - _mindSystem.UnVisit(actor.PlayerSession.ContentData()!.Mind); + _mindSystem.UnVisit(actor.PlayerSession); } private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args) @@ -260,7 +261,8 @@ namespace Content.Server.Ghost TryComp(attached, out var mind); - string playerInfo = $"{EntityManager.GetComponent(attached).EntityName} ({mind?.Mind?.CurrentJob?.Name ?? "Unknown"})"; + var jobName = _jobs.MindTryGetJobName(mind?.Mind); + var playerInfo = $"{EntityManager.GetComponent(attached).EntityName} ({jobName})"; if (_mobState.IsAlive(attached) || _mobState.IsCritical(attached)) yield return new GhostWarp(attached, playerInfo, false); diff --git a/Content.Server/Ghost/ObserverRole.cs b/Content.Server/Ghost/ObserverRole.cs deleted file mode 100644 index 86a422422b..0000000000 --- a/Content.Server/Ghost/ObserverRole.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Server.Roles; - -namespace Content.Server.Ghost -{ - /// - /// This is used to mark Observers properly, as they get Minds - /// - public sealed class ObserverRole : Role - { - public override string Name => Loc.GetString("observer-role-name"); - public override bool Antagonist => false; - - public ObserverRole(Mind.Mind mind) : base(mind) - { - } - } -} diff --git a/Content.Server/Ghost/ObserverRoleComponent.cs b/Content.Server/Ghost/ObserverRoleComponent.cs new file mode 100644 index 0000000000..9952da72a1 --- /dev/null +++ b/Content.Server/Ghost/ObserverRoleComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.Ghost +{ + /// + /// This is used to mark Observers properly, as they get Minds + /// + [RegisterComponent] + public sealed partial class ObserverRoleComponent : Component + { + public string Name => Loc.GetString("observer-role-name"); + } +} diff --git a/Content.Server/Ghost/ReturnToBodyEui.cs b/Content.Server/Ghost/ReturnToBodyEui.cs index ff7ba16a01..164d65f0f1 100644 --- a/Content.Server/Ghost/ReturnToBodyEui.cs +++ b/Content.Server/Ghost/ReturnToBodyEui.cs @@ -1,6 +1,5 @@ using Content.Server.EUI; using Content.Server.Mind; -using Content.Server.Players; using Content.Shared.Eui; using Content.Shared.Ghost; @@ -10,9 +9,9 @@ public sealed class ReturnToBodyEui : BaseEui { private readonly MindSystem _mindSystem; - private readonly Mind.Mind _mind; + private readonly MindComponent _mind; - public ReturnToBodyEui(Mind.Mind mind, MindSystem mindSystem) + public ReturnToBodyEui(MindComponent mind, MindSystem mindSystem) { _mind = mind; _mindSystem = mindSystem; @@ -29,8 +28,8 @@ public sealed class ReturnToBodyEui : BaseEui return; } - if (_mindSystem.TryGetSession(_mind, out var session)) - _mindSystem.UnVisit(session.ContentData()!.Mind); + _mindSystem.UnVisit(_mind.Session); + Close(); } } diff --git a/Content.Server/Ghost/Roles/GhostRoleMarkerRole.cs b/Content.Server/Ghost/Roles/GhostRoleMarkerRole.cs deleted file mode 100644 index 1f178765ab..0000000000 --- a/Content.Server/Ghost/Roles/GhostRoleMarkerRole.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Server.Roles; - -namespace Content.Server.Ghost.Roles -{ - /// - /// This is used for round end display of ghost roles. - /// It may also be used to ensure some ghost roles count as antagonists in future. - /// - public sealed class GhostRoleMarkerRole : Role - { - private readonly string _name; - public override string Name => _name; - public override bool Antagonist => false; - - public GhostRoleMarkerRole(Mind.Mind mind, string name) : base(mind) - { - _name = name; - } - } -} diff --git a/Content.Server/Ghost/Roles/GhostRoleMarkerRoleComponent.cs b/Content.Server/Ghost/Roles/GhostRoleMarkerRoleComponent.cs new file mode 100644 index 0000000000..bd276e6df7 --- /dev/null +++ b/Content.Server/Ghost/Roles/GhostRoleMarkerRoleComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.Ghost.Roles; + +/// +/// This is used for round end display of ghost roles. +/// It may also be used to ensure some ghost roles count as antagonists in future. +/// +[RegisterComponent] +public sealed partial class GhostRoleMarkerRoleComponent : Component +{ + [DataField("name")] public string? Name; +} diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index 843341c6d4..361c99dda2 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -3,10 +3,11 @@ using Content.Server.EUI; using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Events; using Content.Server.Ghost.Roles.UI; -using Content.Server.Mind.Commands; using Content.Server.Mind; +using Content.Server.Mind.Commands; using Content.Server.Mind.Components; using Content.Server.Players; +using Content.Server.Roles; using Content.Shared.Administration; using Content.Shared.Database; using Content.Shared.Follower; @@ -34,6 +35,7 @@ namespace Content.Server.Ghost.Roles [Dependency] private readonly FollowerSystem _followerSystem = default!; [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly RoleSystem _roleSystem = default!; private uint _nextRoleIdentifier; private bool _needsUpdateGhostRoleCount = true; @@ -219,7 +221,7 @@ namespace Content.Server.Ghost.Roles var newMind = _mindSystem.CreateMind(player.UserId, EntityManager.GetComponent(mob).EntityName); - _mindSystem.AddRole(newMind, new GhostRoleMarkerRole(newMind, role.RoleName)); + _roleSystem.MindAddRole(newMind, new GhostRoleMarkerRoleComponent { Name = role.RoleName }); _mindSystem.SetUserId(newMind, player.UserId); _mindSystem.TransferTo(newMind, mob); diff --git a/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs index 3655da6fd8..a1f3eb68b6 100644 --- a/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/ToggleableGhostRoleSystem.cs @@ -107,12 +107,12 @@ public sealed class ToggleableGhostRoleSystem : EntitySystem Text = Loc.GetString(component.WipeVerbText), Act = () => { - if (!TryComp(uid, out var mindComp) || mindComp.Mind == null) + if (!_mind.TryGetMind(uid, out var mindId, out var mind)) return; // Wiping device :( // The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved // EDIT: But it doesn't!!!! Wtf? Do stuff manually - _mind.TransferTo(mindComp.Mind, null); + _mind.TransferTo(mindId, null, mind: mind); _popup.PopupEntity(Loc.GetString(component.WipeVerbPopup), uid, args.User, PopupType.Large); UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); _pai.PAITurningOff(uid); diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index ade4ecea52..a69afc8657 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -13,8 +13,6 @@ using Content.Server.Info; using Content.Server.Maps; using Content.Server.MoMMI; using Content.Server.NodeContainer.NodeGroups; -using Content.Server.Objectives; -using Content.Server.Objectives.Interfaces; using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; using Content.Server.ServerInfo; @@ -40,7 +38,6 @@ namespace Content.Server.IoC IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); - IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index ff25b5354e..9a2a5cb391 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -4,8 +4,8 @@ using Content.Server.Chemistry.EntitySystems; using Content.Server.Construction; using Content.Server.Fluids.EntitySystems; using Content.Server.GameTicking; +using Content.Server.Mind; using Content.Server.Nutrition.Components; -using Content.Server.Players; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Stack; @@ -34,6 +34,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem [Dependency] private readonly SharedBodySystem _body = default!; //bobby [Dependency] private readonly PuddleSystem _puddle = default!; [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly MindSystem _mind = default!; /// public override void Initialize() @@ -105,9 +106,9 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem args.SetHandled(SuicideKind.Bloodloss); var victim = args.Victim; if (TryComp(victim, out ActorComponent? actor) && - actor.PlayerSession.ContentData()?.Mind is { } mind) + _mind.TryGetMind(actor.PlayerSession, out var mindId, out var mind)) { - _ticker.OnGhostAttempt(mind, false); + _ticker.OnGhostAttempt(mindId, false, mind: mind); if (mind.OwnedEntity is { Valid: true } entity) { _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), entity); diff --git a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs index 18d1d009f4..5b88b94fcf 100644 --- a/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs +++ b/Content.Server/Medical/BiomassReclaimer/BiomassReclaimerSystem.cs @@ -1,32 +1,32 @@ using System.Numerics; -using Content.Shared.Interaction; -using Content.Shared.Audio; -using Content.Shared.Jittering; -using Content.Shared.Chemistry.Components; -using Content.Shared.Throwing; -using Content.Shared.Construction.Components; -using Content.Shared.Nutrition.Components; -using Content.Shared.Administration.Logs; -using Content.Shared.CCVar; -using Content.Shared.Database; -using Content.Server.Power.Components; -using Content.Server.Fluids.EntitySystems; using Content.Server.Body.Components; using Content.Server.Climbing; using Content.Server.Construction; +using Content.Server.Fluids.EntitySystems; using Content.Server.Materials; -using Content.Server.Mind.Components; +using Content.Server.Mind; +using Content.Server.Power.Components; +using Content.Shared.Administration.Logs; +using Content.Shared.Audio; +using Content.Shared.CCVar; +using Content.Shared.Chemistry.Components; +using Content.Shared.Construction.Components; +using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.Humanoid; +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Jittering; +using Content.Shared.Medical; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; using Content.Shared.Popups; -using Robust.Shared.Random; -using Robust.Shared.Configuration; +using Content.Shared.Throwing; using Robust.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Physics.Components; -using Content.Shared.Medical; +using Robust.Shared.Random; namespace Content.Server.Medical.BiomassReclaimer { @@ -45,6 +45,7 @@ namespace Content.Server.Medical.BiomassReclaimer [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly MaterialStorageSystem _material = default!; + [Dependency] private readonly MindSystem _minds = default!; public override void Update(float frameTime) { @@ -244,9 +245,9 @@ namespace Content.Server.Medical.BiomassReclaimer // Reject souled bodies in easy mode. if (_configManager.GetCVar(CCVars.BiomassEasyMode) && HasComp(dragged) && - TryComp(dragged, out var mindComp)) + _minds.TryGetMind(dragged, out _, out var mind)) { - if (mindComp.Mind?.UserId != null && _playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out _)) + if (mind.UserId != null && _playerManager.TryGetSessionById(mind.UserId.Value, out _)) return false; } diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 5e4e3ca0f2..35b019257a 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -5,7 +5,6 @@ using Content.Server.Electrocution; using Content.Server.EUI; using Content.Server.Ghost; using Content.Server.Mind; -using Content.Server.Mind.Components; using Content.Server.Popups; using Content.Server.PowerCell; using Content.Shared.Damage; @@ -224,16 +223,16 @@ public sealed class DefibrillatorSystem : EntitySystem _mobState.ChangeMobState(target, MobState.Critical, mob, uid); _mobThreshold.SetAllowRevives(target, false, thresholds); - if (TryComp(target, out var mindComp) && - mindComp.Mind?.Session is { } playerSession) + if (_mind.TryGetMind(target, out var mindId, out var mind) && + mind.Session is { } playerSession) { session = playerSession; // notify them they're being revived. - if (mindComp.Mind.CurrentEntity != target) + if (mind.CurrentEntity != target) { _chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-ghosted"), InGameICChatType.Speak, true); - _euiManager.OpenEui(new ReturnToBodyEui(mindComp.Mind, _mind), session); + _euiManager.OpenEui(new ReturnToBodyEui(mind, _mind), session); } } else diff --git a/Content.Server/Mind/Commands/MindInfoCommand.cs b/Content.Server/Mind/Commands/MindInfoCommand.cs index 749135d260..0327657d97 100644 --- a/Content.Server/Mind/Commands/MindInfoCommand.cs +++ b/Content.Server/Mind/Commands/MindInfoCommand.cs @@ -1,6 +1,6 @@ using System.Text; using Content.Server.Administration; -using Content.Server.Players; +using Content.Server.Roles; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; @@ -10,10 +10,10 @@ namespace Content.Server.Mind.Commands [AdminCommand(AdminFlags.Admin)] public sealed class MindInfoCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _entities = default!; + public string Command => "mindinfo"; - public string Description => "Lists info for the mind of a specific player."; - public string Help => "mindinfo "; public void Execute(IConsoleShell shell, string argStr, string[] args) @@ -25,15 +25,14 @@ namespace Content.Server.Mind.Commands } var mgr = IoCManager.Resolve(); - if (!mgr.TryGetSessionByUsername(args[0], out var data)) + if (!mgr.TryGetSessionByUsername(args[0], out var session)) { shell.WriteLine("Can't find that mind"); return; } - var mind = data.ContentData()?.Mind; - - if (mind == null) + var minds = _entities.System(); + if (!minds.TryGetMind(session, out var mindId, out var mind)) { shell.WriteLine("Can't find that mind"); return; @@ -41,7 +40,9 @@ namespace Content.Server.Mind.Commands var builder = new StringBuilder(); builder.AppendFormat("player: {0}, mob: {1}\nroles: ", mind.UserId, mind.OwnedEntity); - foreach (var role in mind.AllRoles) + + var roles = _entities.System(); + foreach (var role in roles.MindGetAllRoles(mindId)) { builder.AppendFormat("{0} ", role.Name); } diff --git a/Content.Server/Mind/Commands/RenameCommand.cs b/Content.Server/Mind/Commands/RenameCommand.cs index 747d7b1125..b4e26cb6c2 100644 --- a/Content.Server/Mind/Commands/RenameCommand.cs +++ b/Content.Server/Mind/Commands/RenameCommand.cs @@ -1,7 +1,6 @@ using Content.Server.Access.Systems; using Content.Server.Administration; using Content.Server.Administration.Systems; -using Content.Server.Mind.Components; using Content.Server.PDA; using Content.Server.StationRecords.Systems; using Content.Shared.Access.Components; @@ -46,16 +45,16 @@ public sealed class RenameCommand : IConsoleCommand var oldName = metadata.EntityName; entMan.System().SetEntityName(entityUid, name, metadata); - var entSysMan = IoCManager.Resolve(); + var minds = entMan.System(); - if (entMan.TryGetComponent(entityUid, out MindContainerComponent? mind) && mind.Mind != null) + if (minds.TryGetMind(entityUid, out var mindId, out var mind)) { // Mind - mind.Mind.CharacterName = name; + mind.CharacterName = name; } // Id Cards - if (entSysMan.TryGetEntitySystem(out var idCardSystem)) + if (entMan.TrySystem(out var idCardSystem)) { if (idCardSystem.TryFindIdCard(entityUid, out var idCard)) { @@ -63,7 +62,7 @@ public sealed class RenameCommand : IConsoleCommand // Records // This is done here because ID cards are linked to station records - if (entSysMan.TryGetEntitySystem(out var recordsSystem) + if (entMan.TrySystem(out var recordsSystem) && entMan.TryGetComponent(idCard.Owner, out StationRecordKeyStorageComponent? keyStorage) && keyStorage.Key != null) { @@ -80,7 +79,7 @@ public sealed class RenameCommand : IConsoleCommand } // PDAs - if (entSysMan.TryGetEntitySystem(out var pdaSystem)) + if (entMan.TrySystem(out var pdaSystem)) { var query = entMan.EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var pda)) @@ -93,7 +92,7 @@ public sealed class RenameCommand : IConsoleCommand } // Admin Overlay - if (entSysMan.TryGetEntitySystem(out var adminSystem) + if (entMan.TrySystem(out var adminSystem) && entMan.TryGetComponent(entityUid, out var actorComp)) { adminSystem.UpdatePlayerList(actorComp.PlayerSession); diff --git a/Content.Server/Mind/Components/MindContainerComponent.cs b/Content.Server/Mind/Components/MindContainerComponent.cs index f5274e443f..c78b18f70c 100644 --- a/Content.Server/Mind/Components/MindContainerComponent.cs +++ b/Content.Server/Mind/Components/MindContainerComponent.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; namespace Content.Server.Mind.Components { /// - /// Stores a on a mob. + /// Stores a on a mob. /// [RegisterComponent, Access(typeof(MindSystem))] public sealed partial class MindContainerComponent : Component @@ -13,7 +13,7 @@ namespace Content.Server.Mind.Components /// [ViewVariables] [Access(typeof(MindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends - public Mind? Mind { get; set; } + public EntityUid? Mind { get; set; } /// /// True if we have a mind, false otherwise. @@ -40,10 +40,12 @@ namespace Content.Server.Mind.Components public sealed class MindRemovedMessage : EntityEventArgs { - public Mind OldMind; + public EntityUid OldMindId; + public MindComponent OldMind; - public MindRemovedMessage(Mind oldMind) + public MindRemovedMessage(EntityUid oldMindId, MindComponent oldMind) { + OldMindId = oldMindId; OldMind = oldMind; } } diff --git a/Content.Server/Mind/Components/VisitingMindComponent.cs b/Content.Server/Mind/Components/VisitingMindComponent.cs index e75d1f151d..d21a64959d 100644 --- a/Content.Server/Mind/Components/VisitingMindComponent.cs +++ b/Content.Server/Mind/Components/VisitingMindComponent.cs @@ -4,7 +4,10 @@ namespace Content.Server.Mind.Components public sealed partial class VisitingMindComponent : Component { [ViewVariables] - public Mind? Mind; + public EntityUid? MindId; + + [ViewVariables] + public MindComponent? Mind; } public sealed class MindUnvisitedMessage : EntityEventArgs diff --git a/Content.Server/Mind/Mind.cs b/Content.Server/Mind/MindComponent.cs similarity index 79% rename from Content.Server/Mind/Mind.cs rename to Content.Server/Mind/MindComponent.cs index 844d1ee17c..3017fbbbe2 100644 --- a/Content.Server/Mind/Mind.cs +++ b/Content.Server/Mind/MindComponent.cs @@ -1,15 +1,14 @@ -using System.Linq; -using Content.Server.GameTicking; +using Content.Server.GameTicking; using Content.Server.Mind.Components; using Content.Server.Objectives; -using Content.Server.Roles; using Robust.Server.Player; using Robust.Shared.Network; namespace Content.Server.Mind { /// - /// A mind represents the IC "mind" of a player. Stores roles currently. + /// A mind represents the IC "mind" of a player. + /// Roles are attached as components to its owning entity. /// /// /// Think of it like this: if a player is supposed to have their memories, @@ -18,23 +17,11 @@ namespace Content.Server.Mind /// Things such as respawning do not follow, because you're a new character. /// Getting borged, cloned, turned into a catbeast, etc... will keep it following you. /// - public sealed class Mind + [RegisterComponent] + public sealed partial class MindComponent : Component { - internal readonly ISet Roles = new HashSet(); - internal readonly List Objectives = new(); - public string Briefing = String.Empty; - - /// - /// Creates the new mind. - /// Note: the Mind is NOT initially attached! - /// The provided UserId is solely for tracking of intended owner. - /// - public Mind() - { - } - /// /// The session ID of the player owning this mind. /// @@ -87,12 +74,7 @@ namespace Content.Server.Mind [ViewVariables, Access(typeof(MindSystem))] public EntityUid? OwnedEntity { get; set; } - /// - /// An enumerable over all the roles this mind has. - /// - [ViewVariables] - public IEnumerable AllRoles => Roles; - + // TODO move objectives out of mind component /// /// An enumerable over all the objectives this mind has. /// @@ -119,10 +101,5 @@ namespace Content.Server.Mind /// [ViewVariables, Access(typeof(MindSystem), typeof(GameTicker))] public IPlayerSession? Session { get; internal set; } - - /// - /// Gets the current job - /// - public Job? CurrentJob => Roles.OfType().SingleOrDefault(); } } diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index b84713c15c..f486e0f9e1 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -1,19 +1,17 @@ using System.Diagnostics.CodeAnalysis; -using System.Linq; using Content.Server.Administration.Logs; using Content.Server.GameTicking; using Content.Server.Ghost; using Content.Server.Mind.Components; using Content.Server.Objectives; using Content.Server.Players; -using Content.Server.Roles; using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.GameTicking; using Content.Shared.Ghost; -using Content.Shared.Mobs.Systems; using Content.Shared.Interaction.Events; using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Map; @@ -36,7 +34,7 @@ public sealed class MindSystem : EntitySystem [Dependency] private readonly MetaDataSystem _metaData = default!; // This is dictionary is required to track the minds of disconnected players that may have had their entity deleted. - private readonly Dictionary _userMinds = new(); + private readonly Dictionary _userMinds = new(); public override void Initialize() { @@ -55,14 +53,6 @@ public sealed class MindSystem : EntitySystem WipeAllMinds(); } - public void SetGhostOnShutdown(EntityUid uid, bool value, MindContainerComponent? mind = null) - { - if (!Resolve(uid, ref mind)) - return; - - mind.GhostOnShutdown = value; - } - private void OnReset(RoundRestartCleanupEvent ev) { WipeAllMinds(); @@ -86,59 +76,35 @@ public sealed class MindSystem : EntitySystem } } - public Mind? GetMind(NetUserId user) + public EntityUid? GetMind(NetUserId user) { - TryGetMind(user, out var mind); + TryGetMind(user, out var mind, out _); return mind; } - public bool TryGetMind(NetUserId user, [NotNullWhen(true)] out Mind? mind) + public bool TryGetMind(NetUserId user, [NotNullWhen(true)] out EntityUid? mindId, [NotNullWhen(true)] out MindComponent? mind) { - if (_userMinds.TryGetValue(user, out mind)) + if (_userMinds.TryGetValue(user, out var mindIdValue) && + TryComp(mindIdValue, out mind)) { DebugTools.Assert(mind.UserId == user); DebugTools.Assert(_playerManager.GetPlayerData(user).ContentData() is not {} data - || data.Mind == mind); + || data.Mind == mindIdValue); + + mindId = mindIdValue; return true; } DebugTools.Assert(_playerManager.GetPlayerData(user).ContentData()?.Mind == null); + mindId = null; + mind = null; return false; } - /// - /// Don't call this unless you know what the hell you're doing. - /// Use instead. - /// If that doesn't cover it, make something to cover it. - /// - private void InternalAssignMind(EntityUid uid, Mind value, MindContainerComponent? mind = null) - { - if (!Resolve(uid, ref mind)) - return; - - mind.Mind = value; - RaiseLocalEvent(uid, new MindAddedMessage(), true); - } - - /// - /// Don't call this unless you know what the hell you're doing. - /// Use instead. - /// If that doesn't cover it, make something to cover it. - /// - private void InternalEjectMind(EntityUid uid, MindContainerComponent? mind = null) - { - if (!Resolve(uid, ref mind, false) || mind.Mind == null) - return; - - var oldMind = mind.Mind; - mind.Mind = null; - RaiseLocalEvent(uid, new MindRemovedMessage(oldMind), true); - } - private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args) { - if (component.Mind != null) - UnVisit(component.Mind); + if (component.MindId != null) + UnVisit(component.MindId.Value, component.Mind); } private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent component, ref EntityTerminatingEvent args) @@ -147,7 +113,7 @@ public sealed class MindSystem : EntitySystem if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby) return; - if (component.Mind is not { } mind) + if (!TryGetMind(uid, out var mindId, out var mind, component)) return; // If the player is currently visiting some other entity, simply attach to that entity. @@ -156,13 +122,13 @@ public sealed class MindSystem : EntitySystem && !Deleted(visiting) && !Terminating(visiting)) { - TransferTo(mind, visiting); + TransferTo(mindId, visiting, mind: mind); if (TryComp(visiting, out GhostComponent? ghost)) _ghostSystem.SetCanReturnToBody(ghost, false); return; } - TransferTo(mind, null, createGhost: false); + TransferTo(mindId, null, createGhost: false, mind: mind); if (component.GhostOnShutdown && mind.Session != null) { @@ -187,7 +153,7 @@ public sealed class MindSystem : EntitySystem { // This should be an error, if it didn't cause tests to start erroring when they delete a player. Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available."); - TransferTo(mind, null, createGhost: false); + TransferTo(mindId, null, createGhost: false, mind: mind); return; } @@ -200,7 +166,7 @@ public sealed class MindSystem : EntitySystem var val = mind.CharacterName ?? string.Empty; _metaData.SetEntityName(ghost, val); - TransferTo(mind, ghost); + TransferTo(mindId, ghost, mind: mind); }); } } @@ -211,7 +177,7 @@ public sealed class MindSystem : EntitySystem return; var dead = _mobStateSystem.IsDead(uid); - var hasSession = mindContainer.Mind?.Session; + var hasSession = CompOrNull(mindContainer.Mind)?.Session; if (dead && !mindContainer.HasMind) args.PushMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-dead-and-irrecoverable", ("ent", uid))}[/color]"); @@ -230,36 +196,40 @@ public sealed class MindSystem : EntitySystem if (args.Handled) return; - if (component.HasMind && component.Mind.PreventSuicide) + if (TryComp(component.Mind, out MindComponent? mind) && mind.PreventSuicide) { args.BlockSuicideAttempt(true); } } - public Mind? GetMind(EntityUid uid, MindContainerComponent? mind = null) + public EntityUid? GetMind(EntityUid uid, MindContainerComponent? mind = null) { if (!Resolve(uid, ref mind)) return null; if (mind.HasMind) return mind.Mind; + return null; } - public Mind CreateMind(NetUserId? userId, string? name = null) + public EntityUid CreateMind(NetUserId? userId, string? name = null) { - var mind = new Mind(); + var mindId = Spawn(null, MapCoordinates.Nullspace); + var mind = EnsureComp(mindId); mind.CharacterName = name; - SetUserId(mind, userId); + SetUserId(mindId, userId, mind); - return mind; + Dirty(mindId, MetaData(mindId)); + + return mindId; } /// /// True if the OwnedEntity of this mind is physically dead. /// This specific definition, as opposed to CharacterDeadIC, is used to determine if ghosting should allow return. /// - public bool IsCharacterDeadPhysically(Mind mind) + public bool IsCharacterDeadPhysically(MindComponent mind) { // This is written explicitly so that the logic can be understood. // But it's also weird and potentially situational. @@ -285,8 +255,11 @@ public sealed class MindSystem : EntitySystem return _mobStateSystem.IsDead(mind.OwnedEntity.Value, targetMobState); } - public void Visit(Mind mind, EntityUid entity) + public void Visit(EntityUid mindId, EntityUid entity, MindComponent? mind = null) { + if (!Resolve(mindId, ref mind)) + return; + if (mind.VisitingEntity != null) { Log.Error($"Attempted to visit an entity ({ToPrettyString(entity)}) while already visiting another ({ToPrettyString(mind.VisitingEntity.Value)})."); @@ -304,6 +277,7 @@ public sealed class MindSystem : EntitySystem // EnsureComp instead of AddComp to deal with deferred deletions. var comp = EnsureComp(entity); + comp.MindId = mindId; comp.Mind = mind; Log.Info($"Session {mind.Session?.Name} visiting entity {entity}."); } @@ -311,9 +285,12 @@ public sealed class MindSystem : EntitySystem /// /// Returns the mind to its original entity. /// - public void UnVisit(Mind? mind) + public void UnVisit(EntityUid mindId, MindComponent? mind = null) { - if (mind == null || mind.VisitingEntity == null) + if (!Resolve(mindId, ref mind)) + return; + + if (mind.VisitingEntity == null) return; RemoveVisitingEntity(mind); @@ -331,11 +308,22 @@ public sealed class MindSystem : EntitySystem } } + /// + /// Returns the mind to its original entity. + /// + public void UnVisit(IPlayerSession? player) + { + if (player == null || !TryGetMind(player, out var mindId, out var mind)) + return; + + UnVisit(mindId, mind); + } + /// /// Cleans up the VisitingEntity. /// /// - private void RemoveVisitingEntity(Mind mind) + private void RemoveVisitingEntity(MindComponent mind) { if (mind.VisitingEntity == null) return; @@ -363,19 +351,19 @@ public sealed class MindSystem : EntitySystem /// /// Detaches a mind from all entities and clears the user ID. /// - public void WipeMind(Mind? mind) + public void WipeMind(EntityUid? mindId, MindComponent? mind = null) { - if (mind == null) + if (mindId == null || !Resolve(mindId.Value, ref mind, false)) return; - TransferTo(mind, null); - SetUserId(mind, null); + TransferTo(mindId.Value, null, mind: mind); + SetUserId(mindId.Value, null, mind: mind); } /// /// Transfer this mind's control over to a new entity. /// - /// The mind to transfer + /// The mind to transfer /// /// The entity to control. /// Can be null, in which case it will simply detach the mind from any entity. @@ -384,10 +372,13 @@ public sealed class MindSystem : EntitySystem /// If true, skips ghost check for Visiting Entity /// /// - /// Thrown if is already owned by another mind. + /// Thrown if is already controlled by another player. /// - public void TransferTo(Mind mind, EntityUid? entity, bool ghostCheckOverride = false, bool createGhost = true) + public void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostCheckOverride = false, bool createGhost = true, MindComponent? mind = null) { + if (!Resolve(mindId, ref mind)) + return; + if (entity == mind.OwnedEntity) return; @@ -399,7 +390,7 @@ public sealed class MindSystem : EntitySystem component = EnsureComp(entity.Value); if (component.HasMind) - _gameTicker.OnGhostAttempt(component.Mind, false); + _gameTicker.OnGhostAttempt(component.Mind.Value, false); if (TryComp(entity.Value, out var actor)) { @@ -425,8 +416,11 @@ public sealed class MindSystem : EntitySystem var oldComp = mind.OwnedComponent; var oldEntity = mind.OwnedEntity; - if(oldComp != null && oldEntity != null) - InternalEjectMind(oldEntity.Value, oldComp); + if (oldComp != null && oldEntity != null) + { + oldComp.Mind = null; + RaiseLocalEvent(oldEntity.Value, new MindRemovedMessage(oldEntity.Value, mind), true); + } SetOwnedEntity(mind, entity, component); @@ -455,7 +449,8 @@ public sealed class MindSystem : EntitySystem if (mind.OwnedComponent != null) { - InternalAssignMind(mind.OwnedEntity!.Value, mind, mind.OwnedComponent); + mind.OwnedComponent.Mind = mindId; + RaiseLocalEvent(mind.OwnedEntity!.Value, new MindAddedMessage(), true); mind.OriginalOwnedEntity ??= mind.OwnedEntity; } } @@ -463,11 +458,11 @@ public sealed class MindSystem : EntitySystem /// /// Adds an objective to this mind. /// - public bool TryAddObjective(Mind mind, ObjectivePrototype objectivePrototype) + public bool TryAddObjective(EntityUid mindId, MindComponent mind, ObjectivePrototype objectivePrototype) { - if (!objectivePrototype.CanBeAssigned(mind)) + if (!objectivePrototype.CanBeAssigned(mindId, mind)) return false; - var objective = objectivePrototype.GetObjective(mind); + var objective = objectivePrototype.GetObjective(mindId, mind); if (mind.Objectives.Contains(objective)) return false; @@ -476,7 +471,6 @@ public sealed class MindSystem : EntitySystem _adminLogger.Add(LogType.Mind, LogImpact.Low, $"'{condition.Title}' added to mind of {MindOwnerLoggingString(mind)}"); } - mind.Objectives.Add(objective); return true; } @@ -485,9 +479,10 @@ public sealed class MindSystem : EntitySystem /// Removes an objective to this mind. /// /// Returns true if the removal succeeded. - public bool TryRemoveObjective(Mind mind, int index) + public bool TryRemoveObjective(MindComponent mind, int index) { - if (index < 0 || index >= mind.Objectives.Count) return false; + if (index < 0 || index >= mind.Objectives.Count) + return false; var objective = mind.Objectives[index]; @@ -500,90 +495,56 @@ public sealed class MindSystem : EntitySystem return true; } - /// - /// Gives this mind a new role. - /// - /// The mind to add the role to. - /// The type of the role to give. - /// The instance of the role. - /// - /// Thrown if we already have a role with this type. - /// - public void AddRole(Mind mind, Role role) + public bool TryGetSession(EntityUid? mindId, [NotNullWhen(true)] out IPlayerSession? session) { - if (mind.Roles.Contains(role)) - { - throw new ArgumentException($"We already have this role: {role}"); - } - - mind.Roles.Add(role); - role.Greet(); - - var message = new RoleAddedEvent(mind, role); - if (mind.OwnedEntity != null) - { - RaiseLocalEvent(mind.OwnedEntity.Value, message, true); - } - - _adminLogger.Add(LogType.Mind, LogImpact.Low, - $"'{role.Name}' added to mind of {MindOwnerLoggingString(mind)}"); - } - - /// - /// Removes a role from this mind. - /// - /// The mind to remove the role from. - /// The type of the role to remove. - /// - /// Thrown if we do not have this role. - /// - public void RemoveRole(Mind mind, Role role) - { - if (!mind.Roles.Contains(role)) - { - throw new ArgumentException($"We do not have this role: {role}"); - } - - mind.Roles.Remove(role); - - var message = new RoleRemovedEvent(mind, role); - - if (mind.OwnedEntity != null) - { - RaiseLocalEvent(mind.OwnedEntity.Value, message, true); - } - _adminLogger.Add(LogType.Mind, LogImpact.Low, - $"'{role.Name}' removed from mind of {MindOwnerLoggingString(mind)}"); - } - - public bool HasRole(Mind mind) where T : Role - { - return mind.Roles.Any(role => role is T); - } - - public bool TryGetSession(Mind mind, [NotNullWhen(true)] out IPlayerSession? session) - { - return (session = mind.Session) != null; + session = null; + return TryComp(mindId, out MindComponent? mind) && (session = mind.Session) != null; } /// /// Gets a mind from uid and/or MindContainerComponent. Used for null checks. /// /// Entity UID that owns the mind. + /// The mind id. /// The returned mind. - /// Mind component on to get the mind from. + /// Mind component on to get the mind from. /// True if mind found. False if not. - public bool TryGetMind(EntityUid uid, [NotNullWhen(true)] out Mind? mind, MindContainerComponent? mindContainerComponent = null) + public bool TryGetMind( + EntityUid uid, + out EntityUid mindId, + [NotNullWhen(true)] out MindComponent? mind, + MindContainerComponent? container = null) { + mindId = default; mind = null; - if (!Resolve(uid, ref mindContainerComponent, false)) + + if (!Resolve(uid, ref container, false)) return false; - if (!mindContainerComponent.HasMind) + if (!container.HasMind) return false; - mind = mindContainerComponent.Mind; - return true; + mindId = container.Mind ?? default; + return TryComp(mindId, out mind); + } + + public bool TryGetMind( + PlayerData player, + out EntityUid mindId, + [NotNullWhen(true)] out MindComponent? mind) + { + mindId = player.Mind ?? default; + return TryComp(mindId, out mind); + } + + public bool TryGetMind( + IPlayerSession? player, + out EntityUid mindId, + [NotNullWhen(true)] out MindComponent? mind) + { + mindId = default; + mind = null; + return player?.ContentData() is { } data && TryGetMind(data, out mindId, out mind); } /// @@ -592,7 +553,7 @@ public sealed class MindSystem : EntitySystem /// Mind to set OwnedComponent and OwnedEntity on /// Entity owned by /// MindContainerComponent owned by - private void SetOwnedEntity(Mind mind, EntityUid? uid, MindContainerComponent? mindContainerComponent) + private void SetOwnedEntity(MindComponent mind, EntityUid? uid, MindContainerComponent? mindContainerComponent) { if (uid != null) Resolve(uid.Value, ref mindContainerComponent); @@ -606,8 +567,11 @@ public sealed class MindSystem : EntitySystem /// entity that any mind is connected to, except as a side effect of the fact that it may change a player's /// attached entity. E.g., ghosts get deleted. /// - public void SetUserId(Mind mind, NetUserId? userId) + public void SetUserId(EntityUid mindId, NetUserId? userId, MindComponent? mind = null) { + if (!Resolve(mindId, ref mind)) + return; + if (mind.UserId == userId) return; @@ -637,12 +601,15 @@ public sealed class MindSystem : EntitySystem return; } - if (_userMinds.TryGetValue(userId.Value, out var oldMind)) - SetUserId(oldMind, null); + if (_userMinds.TryGetValue(userId.Value, out var oldMindId) && + TryComp(oldMindId, out MindComponent? oldMind)) + { + SetUserId(oldMindId, null, oldMind); + } DebugTools.AssertNull(_playerManager.GetPlayerData(userId.Value).ContentData()?.Mind); - _userMinds[userId.Value] = mind; + _userMinds[userId.Value] = mindId; mind.UserId = userId; mind.OriginalOwnerUserId ??= userId; @@ -654,7 +621,7 @@ public sealed class MindSystem : EntitySystem // session may be null, but user data may still exist for disconnected players. if (_playerManager.GetPlayerData(userId.Value).ContentData() is { } data) - data.Mind = mind; + data.Mind = mindId; } /// @@ -663,7 +630,7 @@ public sealed class MindSystem : EntitySystem /// "If administrators decide that zombies are dead, this returns true for zombies." /// (Maybe you were looking for the action blocker system?) /// - public bool IsCharacterDeadIc(Mind mind) + public bool IsCharacterDeadIc(MindComponent mind) { if (mind.OwnedEntity is { } owned) { @@ -680,7 +647,7 @@ public sealed class MindSystem : EntitySystem /// /// A string to represent the mind for logging /// - private string MindOwnerLoggingString(Mind mind) + public string MindOwnerLoggingString(MindComponent mind) { if (mind.OwnedEntity != null) return ToPrettyString(mind.OwnedEntity.Value); @@ -688,6 +655,11 @@ public sealed class MindSystem : EntitySystem return mind.UserId.Value.ToString(); return "(originally " + mind.OriginalOwnerUserId + ")"; } + + public string? GetCharacterName(NetUserId userId) + { + return TryGetMind(userId, out _, out var mind) ? mind.CharacterName : null; + } } /// diff --git a/Content.Server/Mind/MindTrackerSystem.cs b/Content.Server/Mind/MindTrackerSystem.cs deleted file mode 100644 index 2ab76ce9af..0000000000 --- a/Content.Server/Mind/MindTrackerSystem.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Content.Server.Mind.Components; -using Content.Shared.GameTicking; - -namespace Content.Server.Mind -{ - /// - /// This is absolutely evil. - /// It tracks all mind changes and logs all the Mind objects. - /// This is so that when round end comes around, there's a coherent list of all Minds that were in play during the round. - /// The Minds themselves contain metadata about their owners. - /// Anyway, this is because disconnected people and ghost roles have been breaking round end statistics for way too long. - /// - public sealed class MindTrackerSystem : EntitySystem - { - [ViewVariables] - public readonly HashSet AllMinds = new(); - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(Reset); - SubscribeLocalEvent(OnMindAdded); - } - - void Reset(RoundRestartCleanupEvent ev) - { - AllMinds.Clear(); - } - - void OnMindAdded(EntityUid uid, MindContainerComponent mc, MindAddedMessage args) - { - var mind = mc.Mind; - if (mind != null) - AllMinds.Add(mind); - } - } -} - diff --git a/Content.Server/Mind/Toolshed/MindCommand.cs b/Content.Server/Mind/Toolshed/MindCommand.cs index ea5bfa2c6a..3d8ffe8629 100644 --- a/Content.Server/Mind/Toolshed/MindCommand.cs +++ b/Content.Server/Mind/Toolshed/MindCommand.cs @@ -1,7 +1,4 @@ -using Content.Server.Mind.Components; -using Content.Server.Players; -using Robust.Server.Player; -using Robust.Shared.Players; +using Robust.Server.Player; using Robust.Shared.Toolshed; using Robust.Shared.Toolshed.Errors; using Robust.Shared.Toolshed.Syntax; @@ -17,20 +14,17 @@ public sealed class MindCommand : ToolshedCommand private MindSystem? _mind; [CommandImplementation("get")] - public Mind? Get([PipedArgument] IPlayerSession session) + public MindComponent? Get([PipedArgument] IPlayerSession session) { - return session.ContentData()?.Mind; + _mind ??= GetSys(); + return _mind.TryGetMind(session, out _, out var mind) ? mind : null; } [CommandImplementation("get")] - public Mind? Get([PipedArgument] EntityUid ent) + public MindComponent? Get([PipedArgument] EntityUid ent) { - if (!TryComp(ent, out var container)) - { - return null; - } - - return container.Mind; + _mind ??= GetSys(); + return _mind.TryGetMind(ent, out _, out var mind) ? mind : null; } [CommandImplementation("control")] @@ -48,15 +42,13 @@ public sealed class MindCommand : ToolshedCommand return target; } - var mind = player.ContentData()?.Mind; - - if (mind is null) + if (!_mind.TryGetMind(player, out var mindId, out var mind)) { ctx.ReportError(new SessionHasNoEntityError(player)); return target; } - _mind.TransferTo(mind, target); + _mind.TransferTo(mindId, target, mind: mind); return target; } } diff --git a/Content.Server/Mind/TransferMindOnGibSystem.cs b/Content.Server/Mind/TransferMindOnGibSystem.cs index d499d6a4dd..c1d4d82341 100644 --- a/Content.Server/Mind/TransferMindOnGibSystem.cs +++ b/Content.Server/Mind/TransferMindOnGibSystem.cs @@ -25,7 +25,7 @@ public sealed class TransferMindOnGibSystem : EntitySystem private void OnGib(EntityUid uid, TransferMindOnGibComponent component, BeingGibbedEvent args) { - if (!TryComp(uid, out var mindcomp) || mindcomp.Mind == null) + if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind)) return; var validParts = args.GibbedParts.Where(p => _tag.HasTag(p, component.TargetTag)).ToHashSet(); @@ -33,6 +33,6 @@ public sealed class TransferMindOnGibSystem : EntitySystem return; var ent = _random.Pick(validParts); - _mindSystem.TransferTo(mindcomp.Mind, ent); + _mindSystem.TransferTo(mindId, ent, mind: mind); } } diff --git a/Content.Server/Morgue/CrematoriumSystem.cs b/Content.Server/Morgue/CrematoriumSystem.cs index ef1f8c9a2a..f285dd424b 100644 --- a/Content.Server/Morgue/CrematoriumSystem.cs +++ b/Content.Server/Morgue/CrematoriumSystem.cs @@ -1,20 +1,20 @@ -using Content.Server.Morgue.Components; -using Content.Shared.Morgue; -using Robust.Server.GameObjects; -using Robust.Shared.Player; -using Content.Server.Storage.Components; -using Content.Shared.Verbs; -using Content.Shared.Database; -using Content.Shared.Interaction.Events; -using Content.Server.Players; using Content.Server.GameTicking; -using Content.Shared.Popups; +using Content.Server.Mind; +using Content.Server.Morgue.Components; +using Content.Server.Storage.Components; using Content.Server.Storage.EntitySystems; +using Content.Shared.Database; using Content.Shared.Examine; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction.Events; +using Content.Shared.Morgue; +using Content.Shared.Popups; using Content.Shared.Standing; using Content.Shared.Storage; -using Content.Shared.IdentityManagement; using Content.Shared.Storage.Components; +using Content.Shared.Verbs; +using Robust.Server.GameObjects; +using Robust.Shared.Player; namespace Content.Server.Morgue; @@ -26,6 +26,7 @@ public sealed class CrematoriumSystem : EntitySystem [Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly MindSystem _minds = default!; public override void Initialize() { @@ -141,9 +142,9 @@ public sealed class CrematoriumSystem : EntitySystem args.SetHandled(SuicideKind.Heat); var victim = args.Victim; - if (TryComp(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is { } mind) + if (TryComp(victim, out ActorComponent? actor) && _minds.TryGetMind(victim, out var mindId, out var mind)) { - _ticker.OnGhostAttempt(mind, false); + _ticker.OnGhostAttempt(mindId, false, mind: mind); if (mind.OwnedEntity is { Valid: true } entity) { diff --git a/Content.Server/Objectives/Commands/AddObjectiveCommand.cs b/Content.Server/Objectives/Commands/AddObjectiveCommand.cs index 7353763b63..7ee0d8b38e 100644 --- a/Content.Server/Objectives/Commands/AddObjectiveCommand.cs +++ b/Content.Server/Objectives/Commands/AddObjectiveCommand.cs @@ -1,6 +1,5 @@ using Content.Server.Administration; using Content.Server.Mind; -using Content.Server.Players; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; @@ -12,7 +11,7 @@ namespace Content.Server.Objectives.Commands public sealed class AddObjectiveCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entityManager = default!; - + public string Command => "addobjective"; public string Description => "Adds an objective to the player's mind."; public string Help => "addobjective "; @@ -25,15 +24,14 @@ namespace Content.Server.Objectives.Commands } var mgr = IoCManager.Resolve(); - if (!mgr.TryGetPlayerDataByUsername(args[0], out var data)) + if (!mgr.TryGetSessionByUsername(args[0], out var data)) { shell.WriteLine("Can't find the playerdata."); return; } - - var mind = data.ContentData()?.Mind; - if (mind == null) + var minds = _entityManager.System(); + if (!minds.TryGetMind(data, out var mindId, out var mind)) { shell.WriteLine("Can't find the mind."); return; @@ -45,14 +43,12 @@ namespace Content.Server.Objectives.Commands shell.WriteLine($"Can't find matching ObjectivePrototype {objectivePrototype}"); return; } - - var mindSystem = _entityManager.System(); - if (!mindSystem.TryAddObjective(mind, objectivePrototype)) + var mindSystem = _entityManager.System(); + if (!mindSystem.TryAddObjective(mindId, mind, objectivePrototype)) { shell.WriteLine("Objective requirements dont allow that objective to be added."); } - } } } diff --git a/Content.Server/Objectives/Commands/ListObjectivesCommand.cs b/Content.Server/Objectives/Commands/ListObjectivesCommand.cs index 1126cac24f..0351bf291c 100644 --- a/Content.Server/Objectives/Commands/ListObjectivesCommand.cs +++ b/Content.Server/Objectives/Commands/ListObjectivesCommand.cs @@ -1,6 +1,6 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.Players; +using Content.Server.Mind; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; @@ -10,39 +10,38 @@ namespace Content.Server.Objectives.Commands [AdminCommand(AdminFlags.Logs)] public sealed class ListObjectivesCommand : LocalizedCommands { + [Dependency] private readonly IEntityManager _entities = default!; + [Dependency] private readonly IPlayerManager _players = default!; + public override string Command => "lsobjectives"; + public override void Execute(IConsoleShell shell, string argStr, string[] args) { var player = shell.Player as IPlayerSession; - IPlayerData? data; - if (args.Length == 0 && player != null) - { - data = player.Data; - } - else if (player == null || !IoCManager.Resolve().TryGetPlayerDataByUsername(args[0], out data)) + if (player == null || !_players.TryGetSessionByUsername(args[0], out player)) { shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist")); return; } - var mind = data.ContentData()?.Mind; - if (mind == null) + var minds = _entities.System(); + if (!minds.TryGetMind(player, out _, out var mind)) { shell.WriteError(LocalizationManager.GetString("shell-target-entity-does-not-have-message", ("missing", "mind"))); return; } - shell.WriteLine($"Objectives for player {data.UserId}:"); + shell.WriteLine($"Objectives for player {player.UserId}:"); var objectives = mind.AllObjectives.ToList(); if (objectives.Count == 0) { shell.WriteLine("None."); } + for (var i = 0; i < objectives.Count; i++) { shell.WriteLine($"- [{i}] {objectives[i].Conditions[0].Title}"); } - } public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) diff --git a/Content.Server/Objectives/Commands/RemoveObjectiveCommand.cs b/Content.Server/Objectives/Commands/RemoveObjectiveCommand.cs index c0846a1e43..68af65fd1d 100644 --- a/Content.Server/Objectives/Commands/RemoveObjectiveCommand.cs +++ b/Content.Server/Objectives/Commands/RemoveObjectiveCommand.cs @@ -1,6 +1,5 @@ using Content.Server.Administration; using Content.Server.Mind; -using Content.Server.Players; using Content.Shared.Administration; using Robust.Server.Player; using Robust.Shared.Console; @@ -11,7 +10,7 @@ namespace Content.Server.Objectives.Commands public sealed class RemoveObjectiveCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entityManager = default!; - + public string Command => "rmobjective"; public string Description => "Removes an objective from the player's mind."; public string Help => "rmobjective "; @@ -24,30 +23,29 @@ namespace Content.Server.Objectives.Commands } var mgr = IoCManager.Resolve(); - if (mgr.TryGetPlayerDataByUsername(args[0], out var data)) + var minds = _entityManager.System(); + if (!mgr.TryGetSessionByUsername(args[0], out var session)) { - var mind = data.ContentData()?.Mind; - if (mind == null) - { - shell.WriteLine("Can't find the mind."); - return; - } + shell.WriteLine("Can't find the playerdata."); + return; + } - if (int.TryParse(args[1], out var i)) - { - var mindSystem = _entityManager.System(); - shell.WriteLine(mindSystem.TryRemoveObjective(mind, i) - ? "Objective successfully removed!" - : "Objective removing failed. Maybe the index is out of bounds? Check lsobjectives!"); - } - else - { - shell.WriteLine($"Invalid index {args[1]}!"); - } + if (!minds.TryGetMind(session, out _, out var mind)) + { + shell.WriteLine("Can't find the mind."); + return; + } + + if (int.TryParse(args[1], out var i)) + { + var mindSystem = _entityManager.System(); + shell.WriteLine(mindSystem.TryRemoveObjective(mind, i) + ? "Objective successfully removed!" + : "Objective removing failed. Maybe the index is out of bounds? Check lsobjectives!"); } else { - shell.WriteLine("Can't find the playerdata."); + shell.WriteLine($"Invalid index {args[1]}!"); } } } diff --git a/Content.Server/Objectives/Conditions/DieCondition.cs b/Content.Server/Objectives/Conditions/DieCondition.cs index 888f6490cd..48fc9fc8b3 100644 --- a/Content.Server/Objectives/Conditions/DieCondition.cs +++ b/Content.Server/Objectives/Conditions/DieCondition.cs @@ -9,11 +9,11 @@ namespace Content.Server.Objectives.Conditions [DataDefinition] public sealed partial class DieCondition : IObjectiveCondition { - private Mind.Mind? _mind; + private MindComponent? _mind; - public IObjectiveCondition GetAssigned(Mind.Mind mind) + public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { - return new DieCondition {_mind = mind}; + return new DieCondition { _mind = mind }; } public string Title => Loc.GetString("objective-condition-die-title"); diff --git a/Content.Server/Objectives/Conditions/EscapeShuttleCondition.cs b/Content.Server/Objectives/Conditions/EscapeShuttleCondition.cs index 8d67e1066f..8848a15085 100644 --- a/Content.Server/Objectives/Conditions/EscapeShuttleCondition.cs +++ b/Content.Server/Objectives/Conditions/EscapeShuttleCondition.cs @@ -11,11 +11,13 @@ namespace Content.Server.Objectives.Conditions [DataDefinition] public sealed partial class EscapeShuttleCondition : IObjectiveCondition { - private Mind.Mind? _mind; + // TODO refactor all of this to be ecs + private MindComponent? _mind; - public IObjectiveCondition GetAssigned(Mind.Mind mind) + public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { - return new EscapeShuttleCondition { + return new EscapeShuttleCondition + { _mind = mind, }; } diff --git a/Content.Server/Objectives/Conditions/KillPersonCondition.cs b/Content.Server/Objectives/Conditions/KillPersonCondition.cs index 8543fd9547..e8568f0534 100644 --- a/Content.Server/Objectives/Conditions/KillPersonCondition.cs +++ b/Content.Server/Objectives/Conditions/KillPersonCondition.cs @@ -1,5 +1,6 @@ using Content.Server.Mind; using Content.Server.Objectives.Interfaces; +using Content.Server.Roles.Jobs; using Content.Server.Shuttles.Systems; using Content.Shared.CCVar; using Content.Shared.Mobs.Systems; @@ -10,10 +11,14 @@ namespace Content.Server.Objectives.Conditions { public abstract class KillPersonCondition : IObjectiveCondition { + // TODO refactor all of this to be ecs protected IEntityManager EntityManager => IoCManager.Resolve(); - protected MobStateSystem MobStateSystem => EntityManager.EntitySysManager.GetEntitySystem(); - protected Mind.Mind? Target; - public abstract IObjectiveCondition GetAssigned(Mind.Mind mind); + protected MindSystem Minds => EntityManager.System(); + protected JobSystem Jobs => EntityManager.System(); + protected MobStateSystem MobStateSystem => EntityManager.System(); + protected EntityUid? TargetMindId; + protected MindComponent? TargetMind => EntityManager.GetComponentOrNull(TargetMindId); + public abstract IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind); /// /// Whether the target must be truly dead, ignores missing evac. @@ -24,10 +29,11 @@ namespace Content.Server.Objectives.Conditions { get { - var targetName = Target?.CharacterName ?? "Unknown"; - var jobName = Target?.CurrentJob?.Name ?? "Unknown"; + var mind = TargetMind; + var targetName = mind?.CharacterName ?? "Unknown"; + var jobName = Jobs.MindTryGetJobName(TargetMindId); - if (Target == null) + if (TargetMind == null) return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName)); @@ -42,12 +48,12 @@ namespace Content.Server.Objectives.Conditions { get { - if (Target == null || Target.OwnedEntity == null) + if (TargetMindId == null || TargetMind?.OwnedEntity == null) return 1f; var entMan = IoCManager.Resolve(); var mindSystem = entMan.System(); - if (mindSystem.IsCharacterDeadIc(Target)) + if (mindSystem.IsCharacterDeadIc(TargetMind)) return 1f; if (RequireDead) @@ -60,7 +66,7 @@ namespace Content.Server.Objectives.Conditions // target is escaping so you fail var emergencyShuttle = entMan.System(); - if (emergencyShuttle.IsTargetEscaping(Target.OwnedEntity.Value)) + if (emergencyShuttle.IsTargetEscaping(TargetMind.OwnedEntity.Value)) return 0f; // evac has left without the target, greentext since the target is afk in space with a full oxygen tank and coordinates off. @@ -76,7 +82,7 @@ namespace Content.Server.Objectives.Conditions public bool Equals(IObjectiveCondition? other) { - return other is KillPersonCondition kpc && Equals(Target, kpc.Target); + return other is KillPersonCondition kpc && Equals(TargetMindId, kpc.TargetMindId); } public override bool Equals(object? obj) @@ -89,7 +95,7 @@ namespace Content.Server.Objectives.Conditions public override int GetHashCode() { - return Target?.GetHashCode() ?? 0; + return TargetMindId?.GetHashCode() ?? 0; } } } diff --git a/Content.Server/Objectives/Conditions/KillRandomHeadCondition.cs b/Content.Server/Objectives/Conditions/KillRandomHeadCondition.cs index f068b76725..b5e7f5c8cc 100644 --- a/Content.Server/Objectives/Conditions/KillRandomHeadCondition.cs +++ b/Content.Server/Objectives/Conditions/KillRandomHeadCondition.cs @@ -1,46 +1,43 @@ +using System.Linq; +using Content.Server.Mind; using Content.Server.Mind.Components; using Content.Server.Objectives.Interfaces; -using Content.Server.Roles; using Content.Shared.Mobs.Components; using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Conditions; [DataDefinition] public sealed partial class KillRandomHeadCondition : KillPersonCondition { - public override IObjectiveCondition GetAssigned(Mind.Mind mind) + // TODO refactor all of this to be ecs + public override IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { RequireDead = true; var allHumans = EntityManager.EntityQuery(true).Where(mc => { - var entity = mc.Mind?.OwnedEntity; + var entity = EntityManager.GetComponentOrNull(mc.Mind)?.OwnedEntity; if (entity == default) return false; return EntityManager.TryGetComponent(entity, out MobStateComponent? mobState) && - MobStateSystem.IsAlive(entity.Value, mobState) && - mc.Mind != mind; + MobStateSystem.IsAlive(entity.Value, mobState) && + mc.Mind != mindId; }).Select(mc => mc.Mind).ToList(); if (allHumans.Count == 0) return new DieCondition(); // I guess I'll die - var allHeads = allHumans.Where(mind => mind?.AllRoles.Any(role => { - if (role is not Job job) - return false; - - // basically a command department check, pretty sussy but whatever - return job.Prototype.RequireAdminNotify; - }) ?? false).ToList(); + var allHeads = allHumans + .Where(mind => Jobs.MindTryGetJob(mind, out _, out var prototype) && prototype.RequireAdminNotify) + .ToList(); if (allHeads.Count == 0) allHeads = allHumans; // fallback to non-head target - return new KillRandomHeadCondition {Target = IoCManager.Resolve().Pick(allHeads)}; + return new KillRandomHeadCondition { TargetMindId = IoCManager.Resolve().Pick(allHeads) }; } public string Description => Loc.GetString("objective-condition-kill-head-description"); diff --git a/Content.Server/Objectives/Conditions/KillRandomPersonCondition.cs b/Content.Server/Objectives/Conditions/KillRandomPersonCondition.cs index 5e591ceac7..1d27e72b27 100644 --- a/Content.Server/Objectives/Conditions/KillRandomPersonCondition.cs +++ b/Content.Server/Objectives/Conditions/KillRandomPersonCondition.cs @@ -1,3 +1,4 @@ +using Content.Server.Mind; using Content.Server.Mind.Components; using Content.Server.Objectives.Interfaces; using Content.Shared.Humanoid; @@ -9,27 +10,27 @@ namespace Content.Server.Objectives.Conditions; [DataDefinition] public sealed partial class KillRandomPersonCondition : KillPersonCondition { - public override IObjectiveCondition GetAssigned(Mind.Mind mind) + public override IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { - var allHumans = new List(); + var allHumans = new List(); var query = EntityManager.EntityQuery(true); foreach (var (mc, _) in query) { - var entity = mc.Mind?.OwnedEntity; + var entity = EntityManager.GetComponentOrNull(mc.Mind)?.OwnedEntity; if (entity == default) continue; if (EntityManager.TryGetComponent(entity, out MobStateComponent? mobState) && MobStateSystem.IsAlive(entity.Value, mobState) && - mc.Mind != mind && mc.Mind != null) + mc.Mind != mindId && mc.Mind != null) { - allHumans.Add(mc.Mind); + allHumans.Add(mc.Mind.Value); } } if (allHumans.Count == 0) return new DieCondition(); // I guess I'll die - return new KillRandomPersonCondition {Target = IoCManager.Resolve().Pick(allHumans)}; + return new KillRandomPersonCondition {TargetMindId = IoCManager.Resolve().Pick(allHumans)}; } } diff --git a/Content.Server/Objectives/Conditions/RandomTraitorAliveCondition.cs b/Content.Server/Objectives/Conditions/RandomTraitorAliveCondition.cs index 1a284962cc..c11e4cebb9 100644 --- a/Content.Server/Objectives/Conditions/RandomTraitorAliveCondition.cs +++ b/Content.Server/Objectives/Conditions/RandomTraitorAliveCondition.cs @@ -1,25 +1,27 @@ using System.Linq; -using Content.Server.Objectives.Interfaces; -using Robust.Shared.Random; -using Robust.Shared.Utility; using Content.Server.GameTicking.Rules; using Content.Server.Mind; +using Content.Server.Objectives.Interfaces; +using Content.Server.Roles.Jobs; +using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Server.Objectives.Conditions { [DataDefinition] public sealed partial class RandomTraitorAliveCondition : IObjectiveCondition { - private Mind.Mind? _target; + private EntityUid? _target; - public IObjectiveCondition GetAssigned(Mind.Mind mind) + public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { var entityMgr = IoCManager.Resolve(); - var traitors = entityMgr.EntitySysManager.GetEntitySystem().GetOtherTraitorsAliveAndConnected(mind).ToList(); + var traitors = entityMgr.System().GetOtherTraitorMindsAliveAndConnected(mind).ToList(); + if (traitors.Count == 0) return new EscapeShuttleCondition(); //You were made a traitor by admins, and are the first/only. - return new RandomTraitorAliveCondition { _target = IoCManager.Resolve().Pick(traitors).Mind }; + return new RandomTraitorAliveCondition { _target = IoCManager.Resolve().Pick(traitors).Id }; } public string Title @@ -27,13 +29,19 @@ namespace Content.Server.Objectives.Conditions get { var targetName = string.Empty; - var jobName = _target?.CurrentJob?.Name ?? "Unknown"; + var ents = IoCManager.Resolve(); + var jobs = ents.System(); + var jobName = jobs.MindTryGetJobName(_target); if (_target == null) return Loc.GetString("objective-condition-other-traitor-alive-title", ("targetName", targetName), ("job", jobName)); - if (_target.OwnedEntity is {Valid: true} owned) - targetName = IoCManager.Resolve().GetComponent(owned).EntityName; + var minds = ents.System(); + if (minds.TryGetMind(_target.Value, out _, out var mind) && + mind.OwnedEntity is { Valid: true } owned) + { + targetName = ents.GetComponent(owned).EntityName; + } return Loc.GetString("objective-condition-other-traitor-alive-title", ("targetName", targetName), ("job", jobName)); } @@ -49,7 +57,11 @@ namespace Content.Server.Objectives.Conditions { var entityManager = IoCManager.Resolve(); var mindSystem = entityManager.System(); - return _target == null || !mindSystem.IsCharacterDeadIc(_target) ? 1f : 0f; + return _target == null || + !mindSystem.TryGetMind(_target.Value, out _, out var mind) || + !mindSystem.IsCharacterDeadIc(mind) + ? 1f + : 0f; } } diff --git a/Content.Server/Objectives/Conditions/RandomTraitorProgressCondition.cs b/Content.Server/Objectives/Conditions/RandomTraitorProgressCondition.cs index 5c34202f67..cc13245f6b 100644 --- a/Content.Server/Objectives/Conditions/RandomTraitorProgressCondition.cs +++ b/Content.Server/Objectives/Conditions/RandomTraitorProgressCondition.cs @@ -1,24 +1,26 @@ using System.Linq; +using Content.Server.GameTicking.Rules; +using Content.Server.Mind; using Content.Server.Objectives.Interfaces; +using Content.Server.Roles.Jobs; using Robust.Shared.Random; using Robust.Shared.Utility; -using Content.Server.GameTicking.Rules; -using Content.Server.Roles; namespace Content.Server.Objectives.Conditions { [DataDefinition] public sealed partial class RandomTraitorProgressCondition : IObjectiveCondition { - private Mind.Mind? _target; + // TODO ecs all of this + private EntityUid? _target; - public IObjectiveCondition GetAssigned(Mind.Mind mind) + public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { //todo shit of a fuck var entityMgr = IoCManager.Resolve(); - var traitors = entityMgr.EntitySysManager.GetEntitySystem().GetOtherTraitorsAliveAndConnected(mind).ToList(); - List removeList = new(); + var traitors = entityMgr.System().GetOtherTraitorMindsAliveAndConnected(mind).ToList(); + List removeList = new(); foreach (var traitor in traitors) { @@ -28,7 +30,7 @@ namespace Content.Server.Objectives.Conditions { if (condition is RandomTraitorProgressCondition) { - removeList.Add(traitor); + removeList.Add(traitor.Id); } } } @@ -36,11 +38,11 @@ namespace Content.Server.Objectives.Conditions foreach (var traitor in removeList) { - traitors.Remove(traitor); + traitors.RemoveAll(t => t.Id == traitor); } if (traitors.Count == 0) return new EscapeShuttleCondition{}; //You were made a traitor by admins, and are the first/only. - return new RandomTraitorProgressCondition { _target = IoCManager.Resolve().Pick(traitors).Mind }; + return new RandomTraitorProgressCondition { _target = IoCManager.Resolve().Pick(traitors).Id }; } public string Title @@ -48,13 +50,18 @@ namespace Content.Server.Objectives.Conditions get { var targetName = string.Empty; - var jobName = _target?.CurrentJob?.Name ?? "Unknown"; + var entities = IoCManager.Resolve(); + var jobs = entities.System(); + var jobName = jobs.MindTryGetJobName(_target); if (_target == null) return Loc.GetString("objective-condition-other-traitor-progress-title", ("targetName", targetName), ("job", jobName)); - if (_target.OwnedEntity is {Valid: true} owned) - targetName = IoCManager.Resolve().GetComponent(owned).EntityName; + if (entities.TryGetComponent(_target, out MindComponent? mind) && + mind.OwnedEntity is {Valid: true} owned) + { + targetName = entities.GetComponent(owned).EntityName; + } return Loc.GetString("objective-condition-other-traitor-progress-title", ("targetName", targetName), ("job", jobName)); } @@ -66,9 +73,8 @@ namespace Content.Server.Objectives.Conditions public float Progress { - get { - var entMan = IoCManager.Resolve(); - + get + { float total = 0f; // how much progress they have float max = 0f; // how much progress is needed for 100% @@ -78,12 +84,16 @@ namespace Content.Server.Objectives.Conditions return 1f; } - foreach (var objective in _target.AllObjectives) + var entities = IoCManager.Resolve(); + if (entities.TryGetComponent(_target, out MindComponent? mind)) { - foreach (var condition in objective.Conditions) + foreach (var objective in mind.AllObjectives) { - max++; // things can only be up to 100% complete yeah - total += condition.Progress; + foreach (var condition in objective.Conditions) + { + max++; // things can only be up to 100% complete yeah + total += condition.Progress; + } } } diff --git a/Content.Server/Objectives/Conditions/StealCondition.cs b/Content.Server/Objectives/Conditions/StealCondition.cs index a5f0c9d66e..77f8852ddb 100644 --- a/Content.Server/Objectives/Conditions/StealCondition.cs +++ b/Content.Server/Objectives/Conditions/StealCondition.cs @@ -1,3 +1,4 @@ +using Content.Server.Mind; using Content.Server.Objectives.Interfaces; using JetBrains.Annotations; using Robust.Shared.Containers; @@ -12,7 +13,7 @@ namespace Content.Server.Objectives.Conditions [DataDefinition] public sealed partial class StealCondition : IObjectiveCondition, ISerializationHooks { - private Mind.Mind? _mind; + private EntityUid? _mind; [DataField("prototype")] private string _prototypeId = string.Empty; /// @@ -21,11 +22,11 @@ namespace Content.Server.Objectives.Conditions /// [DataField("owner")] private string? _owner = null; - public IObjectiveCondition GetAssigned(Mind.Mind mind) + public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind) { return new StealCondition { - _mind = mind, + _mind = mindId, _prototypeId = _prototypeId, _owner = _owner }; @@ -49,7 +50,6 @@ namespace Content.Server.Objectives.Conditions { get { - var uid = _mind?.OwnedEntity; var entMan = IoCManager.Resolve(); // TODO make this a container system function @@ -59,13 +59,16 @@ namespace Content.Server.Objectives.Conditions var managerQuery = entMan.GetEntityQuery(); var stack = new Stack(); - if (!metaQuery.TryGetComponent(_mind?.OwnedEntity, out var meta)) + if (!entMan.TryGetComponent(_mind, out MindComponent? mind)) + return 0; + + if (!metaQuery.TryGetComponent(mind.OwnedEntity, out var meta)) return 0; if (meta.EntityPrototype?.ID == _prototypeId) return 1; - if (!managerQuery.TryGetComponent(uid, out var currentManager)) + if (!managerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager)) return 0; do diff --git a/Content.Server/Objectives/Interfaces/IObjectiveCondition.cs b/Content.Server/Objectives/Interfaces/IObjectiveCondition.cs index 315ed76c2b..ad55984502 100644 --- a/Content.Server/Objectives/Interfaces/IObjectiveCondition.cs +++ b/Content.Server/Objectives/Interfaces/IObjectiveCondition.cs @@ -1,15 +1,18 @@ -using Robust.Shared.Utility; +using Content.Server.Mind; +using Robust.Shared.Utility; namespace Content.Server.Objectives.Interfaces { + // TODO refactor all of this to be ecs public interface IObjectiveCondition : IEquatable { /// /// Returns a copy of the IObjectiveCondition which is assigned to the mind. /// + /// Mind id to assign to. /// Mind to assign to. /// The new IObjectiveCondition. - IObjectiveCondition GetAssigned(Mind.Mind mind); + IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind); /// /// Returns the title of the condition. diff --git a/Content.Server/Objectives/Interfaces/IObjectiveRequirement.cs b/Content.Server/Objectives/Interfaces/IObjectiveRequirement.cs index 3024275f7b..f76ade0463 100644 --- a/Content.Server/Objectives/Interfaces/IObjectiveRequirement.cs +++ b/Content.Server/Objectives/Interfaces/IObjectiveRequirement.cs @@ -1,11 +1,14 @@ -namespace Content.Server.Objectives.Interfaces +using Content.Server.Mind; + +namespace Content.Server.Objectives.Interfaces { + // TODO refactor all of this to be ecs public interface IObjectiveRequirement { /// /// Checks whether or not the entity & its surroundings are valid to be given the objective. /// /// Returns true if objective can be given. - bool CanBeAssigned(Mind.Mind mind); + bool CanBeAssigned(EntityUid mindId, MindComponent mind); } } diff --git a/Content.Server/Objectives/Interfaces/IObjectivesManager.cs b/Content.Server/Objectives/Interfaces/IObjectivesManager.cs deleted file mode 100644 index bee98bc80b..0000000000 --- a/Content.Server/Objectives/Interfaces/IObjectivesManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Server.Objectives.Interfaces -{ - public interface IObjectivesManager - { - /// - /// Returns a randomly picked objective the provided mind is valid for. - /// - ObjectivePrototype? GetRandomObjective(Mind.Mind mind, string objectiveGroupProto); - } -} diff --git a/Content.Server/Objectives/Objective.cs b/Content.Server/Objectives/Objective.cs index 456502ef83..27ddd87e33 100644 --- a/Content.Server/Objectives/Objective.cs +++ b/Content.Server/Objectives/Objective.cs @@ -1,24 +1,28 @@ -using Content.Server.Objectives.Interfaces; +using Content.Server.Mind; +using Content.Server.Objectives.Interfaces; namespace Content.Server.Objectives { public sealed class Objective : IEquatable { [ViewVariables] - public readonly Mind.Mind Mind; + public readonly EntityUid MindId; + [ViewVariables] + public readonly MindComponent Mind; [ViewVariables] public readonly ObjectivePrototype Prototype; private readonly List _conditions = new(); [ViewVariables] public IReadOnlyList Conditions => _conditions; - public Objective(ObjectivePrototype prototype, Mind.Mind mind) + public Objective(ObjectivePrototype prototype, EntityUid mindId, MindComponent mind) { Prototype = prototype; + MindId = mindId; Mind = mind; foreach (var condition in prototype.Conditions) { - _conditions.Add(condition.GetAssigned(mind)); + _conditions.Add(condition.GetAssigned(mindId, mind)); } } diff --git a/Content.Server/Objectives/ObjectivePrototype.cs b/Content.Server/Objectives/ObjectivePrototype.cs index 4235e3df35..f8ede98538 100644 --- a/Content.Server/Objectives/ObjectivePrototype.cs +++ b/Content.Server/Objectives/ObjectivePrototype.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.Mind; using Content.Server.Objectives.Interfaces; using Robust.Shared.Prototypes; @@ -34,27 +35,29 @@ namespace Content.Server.Objectives [DataField("difficultyOverride")] private float? _difficultyOverride = null; - public bool CanBeAssigned(Mind.Mind mind) + public bool CanBeAssigned(EntityUid mindId, MindComponent mind) { foreach (var requirement in _requirements) { - if (!requirement.CanBeAssigned(mind)) return false; + if (!requirement.CanBeAssigned(mindId, mind)) + return false; } if (!CanBeDuplicateAssignment) { foreach (var objective in mind.AllObjectives) { - if (objective.Prototype.ID == ID) return false; + if (objective.Prototype.ID == ID) + return false; } } return true; } - public Objective GetObjective(Mind.Mind mind) + public Objective GetObjective(EntityUid mindId, MindComponent mind) { - return new(this, mind); + return new Objective(this, mindId, mind); } } } diff --git a/Content.Server/Objectives/ObjectivesManager.cs b/Content.Server/Objectives/ObjectivesManager.cs deleted file mode 100644 index 11563f9934..0000000000 --- a/Content.Server/Objectives/ObjectivesManager.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.Objectives.Interfaces; -using Content.Shared.Random.Helpers; -using Content.Shared.Random; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; - -namespace Content.Server.Objectives -{ - public sealed class ObjectivesManager : IObjectivesManager - { - [Dependency] private IPrototypeManager _prototypeManager = default!; - [Dependency] private IRobustRandom _random = default!; - - public ObjectivePrototype? GetRandomObjective(Mind.Mind mind, string objectiveGroupProto) - { - if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groups)) - { - Logger.Error("Tried to get a random objective, but can't index WeightedRandomPrototype " + objectiveGroupProto); - return null; - } - - // yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did - var tries = 0; - while (tries < 20) - { - var groupName = groups.Pick(_random); - - if (!_prototypeManager.TryIndex(groupName, out var group)) - { - Logger.Error("Couldn't index objective group prototype" + groupName); - return null; - } - - if (_prototypeManager.TryIndex(group.Pick(_random), out var objective) - && objective.CanBeAssigned(mind)) - return objective; - else - tries++; - } - - return null; - } - } -} diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs new file mode 100644 index 0000000000..9536578f9a --- /dev/null +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -0,0 +1,44 @@ +using Content.Server.Mind; +using Content.Shared.Random; +using Content.Shared.Random.Helpers; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.Objectives; + +public sealed class ObjectivesSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + public ObjectivePrototype? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto) + { + if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groups)) + { + Log.Error("Tried to get a random objective, but can't index WeightedRandomPrototype " + objectiveGroupProto); + return null; + } + + // TODO replace whatever the fuck this is with a proper objective selection system + // yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did + var tries = 0; + while (tries < 20) + { + var groupName = groups.Pick(_random); + + if (!_prototypeManager.TryIndex(groupName, out var group)) + { + Log.Error("Couldn't index objective group prototype" + groupName); + return null; + } + + if (_prototypeManager.TryIndex(group.Pick(_random), out var objective) + && objective.CanBeAssigned(mindId, mind)) + return objective; + else + tries++; + } + + return null; + } +} diff --git a/Content.Server/Objectives/Requirements/IncompatibleConditionsRequirement.cs b/Content.Server/Objectives/Requirements/IncompatibleConditionsRequirement.cs index 42c72da6b9..ad4f2f768b 100644 --- a/Content.Server/Objectives/Requirements/IncompatibleConditionsRequirement.cs +++ b/Content.Server/Objectives/Requirements/IncompatibleConditionsRequirement.cs @@ -1,4 +1,5 @@ -using Content.Server.Objectives.Interfaces; +using Content.Server.Mind; +using Content.Server.Objectives.Interfaces; namespace Content.Server.Objectives.Requirements { @@ -8,7 +9,7 @@ namespace Content.Server.Objectives.Requirements [DataField("conditions")] private List _incompatibleConditions = new(); - public bool CanBeAssigned(Mind.Mind mind) + public bool CanBeAssigned(EntityUid mindId, MindComponent mind) { foreach (var objective in mind.AllObjectives) { diff --git a/Content.Server/Objectives/Requirements/IncompatibleObjectivesRequirement.cs b/Content.Server/Objectives/Requirements/IncompatibleObjectivesRequirement.cs index 152d739341..ab71366708 100644 --- a/Content.Server/Objectives/Requirements/IncompatibleObjectivesRequirement.cs +++ b/Content.Server/Objectives/Requirements/IncompatibleObjectivesRequirement.cs @@ -1,4 +1,5 @@ -using Content.Server.Objectives.Interfaces; +using Content.Server.Mind; +using Content.Server.Objectives.Interfaces; namespace Content.Server.Objectives.Requirements { @@ -8,7 +9,7 @@ namespace Content.Server.Objectives.Requirements [DataField("objectives")] private List _incompatibleObjectives = new(); - public bool CanBeAssigned(Mind.Mind mind) + public bool CanBeAssigned(EntityUid mindId, MindComponent mind) { foreach (var objective in mind.AllObjectives) { diff --git a/Content.Server/Objectives/Requirements/MultipleTraitorsRequirement.cs b/Content.Server/Objectives/Requirements/MultipleTraitorsRequirement.cs index 6a12980dd7..0797d95b35 100644 --- a/Content.Server/Objectives/Requirements/MultipleTraitorsRequirement.cs +++ b/Content.Server/Objectives/Requirements/MultipleTraitorsRequirement.cs @@ -1,5 +1,6 @@ -using Content.Server.Objectives.Interfaces; using Content.Server.GameTicking.Rules; +using Content.Server.Mind; +using Content.Server.Objectives.Interfaces; namespace Content.Server.Objectives.Requirements { @@ -9,9 +10,9 @@ namespace Content.Server.Objectives.Requirements [DataField("traitors")] private int _requiredTraitors = 2; - public bool CanBeAssigned(Mind.Mind mind) + public bool CanBeAssigned(EntityUid mindId, MindComponent mind) { - return EntitySystem.Get().GetOtherTraitorsAliveAndConnected(mind).Count >= _requiredTraitors; + return EntitySystem.Get().GetOtherTraitorMindsAliveAndConnected(mind).Count >= _requiredTraitors; } } } diff --git a/Content.Server/Objectives/Requirements/NotRoleRequirement.cs b/Content.Server/Objectives/Requirements/NotRoleRequirement.cs index 5e0ecdab19..5043d3dc80 100644 --- a/Content.Server/Objectives/Requirements/NotRoleRequirement.cs +++ b/Content.Server/Objectives/Requirements/NotRoleRequirement.cs @@ -1,10 +1,11 @@ -using Content.Shared.Roles; +using Content.Server.Mind; using Content.Server.Objectives.Interfaces; +using Content.Server.Roles.Jobs; +using Content.Shared.Roles; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Objectives.Requirements { - [DataDefinition] public sealed partial class NotRoleRequirement : IObjectiveRequirement { @@ -14,12 +15,14 @@ namespace Content.Server.Objectives.Requirements /// /// This requirement is met if the traitor is NOT the roleId, and fails if they are. /// - public bool CanBeAssigned(Mind.Mind mind) + public bool CanBeAssigned(EntityUid mindId, MindComponent mind) { - if (mind.CurrentJob == null) // no job no problems + // TODO ECS this shit i keep seeing shitcode everywhere + var entities = IoCManager.Resolve(); + if (!entities.TryGetComponent(mindId, out JobComponent? job)) return true; - return (mind.CurrentJob.Prototype.ID != _roleId); + return job.PrototypeId != _roleId; } } } diff --git a/Content.Server/Objectives/Requirements/TraitorRequirement.cs b/Content.Server/Objectives/Requirements/TraitorRequirement.cs index 263718232a..d4dec52a2e 100644 --- a/Content.Server/Objectives/Requirements/TraitorRequirement.cs +++ b/Content.Server/Objectives/Requirements/TraitorRequirement.cs @@ -1,7 +1,7 @@ using Content.Server.Mind; using Content.Server.Objectives.Interfaces; +using Content.Server.Roles; using JetBrains.Annotations; -using TraitorRole = Content.Server.Roles.TraitorRole; namespace Content.Server.Objectives.Requirements { @@ -9,11 +9,10 @@ namespace Content.Server.Objectives.Requirements [DataDefinition] public sealed partial class TraitorRequirement : IObjectiveRequirement { - public bool CanBeAssigned(Mind.Mind mind) + public bool CanBeAssigned(EntityUid mindId, MindComponent mind) { - var entityManager = IoCManager.Resolve(); - var mindSystem = entityManager.System(); - return mindSystem.HasRole(mind); + var roleSystem = IoCManager.Resolve().System(); + return roleSystem.MindHasRole(mindId); } } } diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs index 73e7e01f15..c501025fe9 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs @@ -77,14 +77,17 @@ public sealed class PlayTimeTrackingSystem : EntitySystem return state.CurrentState is MobState.Alive or MobState.Critical; } - public IEnumerable GetTimedRoles(Mind.Mind mind) + public IEnumerable GetTimedRoles(EntityUid mindId) { - foreach (var role in mind.AllRoles) + var ev = new MindGetAllRolesEvent(new List()); + RaiseLocalEvent(ref ev); + + foreach (var role in ev.Roles) { - if (role is not IRoleTimer timer) + if (string.IsNullOrWhiteSpace(role.PlayTimeTrackerId)) continue; - yield return _prototypes.Index(timer.Timer).ID; + yield return _prototypes.Index(role.PlayTimeTrackerId).ID; } } @@ -95,7 +98,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem if (contentData?.Mind == null) return Enumerable.Empty(); - return GetTimedRoles(contentData.Mind); + return GetTimedRoles(contentData.Mind.Value); } private void OnRoleRemove(RoleRemovedEvent ev) diff --git a/Content.Server/Players/PlayerData.cs b/Content.Server/Players/PlayerData.cs index c144ff4115..aadaeb225c 100644 --- a/Content.Server/Players/PlayerData.cs +++ b/Content.Server/Players/PlayerData.cs @@ -29,7 +29,7 @@ namespace Content.Server.Players /// DO NOT DIRECTLY SET THIS UNLESS YOU KNOW WHAT YOU'RE DOING. /// [ViewVariables, Access(typeof(MindSystem), typeof(GameTicker))] - public Mind.Mind? Mind { get; set; } + public EntityUid? Mind { get; set; } /// /// If true, the player is an admin and they explicitly de-adminned mid-game, @@ -65,7 +65,7 @@ namespace Content.Server.Players /// /// Gets the mind that is associated with this player. /// - public static Mind.Mind? GetMind(this IPlayerSession session) + public static EntityUid? GetMind(this IPlayerSession session) { return session.Data.ContentData()?.Mind; } diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 89700c4f5b..8194b72927 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -1,6 +1,6 @@ using System.Linq; using Content.Server.Administration.Logs; -using Content.Server.Players; +using Content.Server.Mind; using Content.Server.Pointing.Components; using Content.Server.Visible; using Content.Shared.Bed.Sleep; @@ -38,6 +38,7 @@ namespace Content.Server.Pointing.EntitySystems [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; + [Dependency] private readonly MindSystem _minds = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; private static readonly TimeSpan PointDelay = TimeSpan.FromSeconds(0.5f); @@ -169,7 +170,8 @@ namespace Content.Server.Pointing.EntitySystems // Get players that are in range and whose visibility layer matches the arrow's. bool ViewerPredicate(IPlayerSession playerSession) { - if (playerSession.ContentData()?.Mind?.CurrentEntity is not {Valid: true} ent || + if (!_minds.TryGetMind(playerSession, out _, out var mind) || + mind.CurrentEntity is not { Valid: true } ent || !TryComp(ent, out EyeComponent? eyeComp) || (eyeComp.VisibilityMask & layer) == 0) return false; diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index 43531d9cbb..fce8b694f7 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -3,7 +3,6 @@ using Content.Server.Humanoid; using Content.Server.Inventory; using Content.Server.Mind; using Content.Server.Mind.Commands; -using Content.Server.Mind.Components; using Content.Server.Nutrition; using Content.Server.Polymorph.Components; using Content.Shared.Actions; @@ -229,8 +228,8 @@ namespace Content.Server.Polymorph.Systems _humanoid.CloneAppearance(uid, child); } - if (_mindSystem.TryGetMind(uid, out var mind)) - _mindSystem.TransferTo(mind, child); + if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + _mindSystem.TransferTo(mindId, child, mind: mind); //Ensures a map to banish the entity to EnsurePausesdMap(); @@ -303,8 +302,8 @@ namespace Content.Server.Polymorph.Systems } } - if (_mindSystem.TryGetMind(uid, out var mind)) - _mindSystem.TransferTo(mind, parent); + if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) + _mindSystem.TransferTo(mindId, parent, mind: mind); // if an item polymorph was picked up, put it back down after reverting Transform(parent).AttachToGridOrMap(); diff --git a/Content.Server/Roles/AddRoleCommand.cs b/Content.Server/Roles/AddRoleCommand.cs index b172873fdf..21d7adfed5 100644 --- a/Content.Server/Roles/AddRoleCommand.cs +++ b/Content.Server/Roles/AddRoleCommand.cs @@ -1,12 +1,11 @@ using Content.Server.Administration; using Content.Server.Players; +using Content.Server.Roles.Jobs; using Content.Shared.Administration; using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.Prototypes; -using System.Linq; -using Content.Server.Mind; namespace Content.Server.Roles { @@ -14,7 +13,7 @@ namespace Content.Server.Roles public sealed class AddRoleCommand : IConsoleCommand { [Dependency] private readonly EntityManager _entityManager = default!; - + public string Command => "addrole"; public string Description => "Adds a role to a player's mind."; @@ -50,15 +49,14 @@ namespace Content.Server.Roles return; } - if (mind.AllRoles.Any(r => r.Name == jobPrototype.Name)) + var jobs = _entityManager.System(); + if (jobs.MindHasJobWithId(mind, jobPrototype.Name)) { shell.WriteLine("Mind already has that role"); return; } - var role = new Job(mind, jobPrototype); - var mindSystem = _entityManager.System(); - mindSystem.AddRole(mind, role); + jobs.MindAddJob(mind.Value, args[1]); } } } diff --git a/Content.Server/Roles/AntagonistRole.cs b/Content.Server/Roles/AntagonistRole.cs deleted file mode 100644 index 9eb6146365..0000000000 --- a/Content.Server/Roles/AntagonistRole.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -public abstract class AntagonistRole : Role -{ - public AntagPrototype Prototype { get; } - - public override string Name { get; } - - public override bool Antagonist { get; } - - /// - /// .ctor - /// - /// A mind (player) - /// Antagonist prototype - protected AntagonistRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind) - { - Prototype = antagPrototype; - Name = Loc.GetString(antagPrototype.Name); - Antagonist = antagPrototype.Antagonist; - } -} diff --git a/Content.Server/Roles/AntagonistRoleComponent.cs b/Content.Server/Roles/AntagonistRoleComponent.cs new file mode 100644 index 0000000000..be29dc278f --- /dev/null +++ b/Content.Server/Roles/AntagonistRoleComponent.cs @@ -0,0 +1,10 @@ +using Content.Shared.Roles; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Roles; + +public abstract partial class AntagonistRoleComponent : Component +{ + [DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? PrototypeId; +} diff --git a/Content.Server/Roles/Job.cs b/Content.Server/Roles/Job.cs deleted file mode 100644 index c5c7403600..0000000000 --- a/Content.Server/Roles/Job.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Content.Server.Chat.Managers; -using Content.Server.Chat.Systems; -using Content.Shared.Roles; -using System.Globalization; -using Content.Server.Mind; - -namespace Content.Server.Roles -{ - public sealed class Job : Role, IRoleTimer - { - [ViewVariables] public string Timer => Prototype.PlayTimeTracker; - - [ViewVariables] - public JobPrototype Prototype { get; } - - public override string Name { get; } - - public override bool Antagonist => false; - - [ViewVariables] - public string? StartingGear => Prototype.StartingGear; - - [ViewVariables] - public string? JobEntity => Prototype.JobEntity; - - [ViewVariables] - public bool CanBeAntag; - - public Job(Mind.Mind mind, JobPrototype jobPrototype) : base(mind) - { - Prototype = jobPrototype; - Name = jobPrototype.LocalizedName; - CanBeAntag = jobPrototype.CanBeAntag; - } - - public override void Greet() - { - base.Greet(); - - var entityManager = IoCManager.Resolve(); - var mindSystem = entityManager.System(); - - if (mindSystem.TryGetSession(Mind, out var session)) - { - var chatMgr = IoCManager.Resolve(); - chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name", - ("jobName", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Name)))); - - if(Prototype.RequireAdminNotify) - chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-important-disconnect-admin-notify")); - - chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-supervisors-warning", ("jobName", Name), ("supervisors", Loc.GetString(Prototype.Supervisors)))); - } - } - } -} diff --git a/Content.Server/Roles/Jobs/JobComponent.cs b/Content.Server/Roles/Jobs/JobComponent.cs new file mode 100644 index 0000000000..e3878cc725 --- /dev/null +++ b/Content.Server/Roles/Jobs/JobComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Roles; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Roles.Jobs; + +/// +/// Added to mind entities to hold the data for the player's current job. +/// +[RegisterComponent] +public sealed partial class JobComponent : Component +{ + [DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? PrototypeId; +} diff --git a/Content.Server/Roles/Jobs/JobSystem.cs b/Content.Server/Roles/Jobs/JobSystem.cs new file mode 100644 index 0000000000..a97aaf0776 --- /dev/null +++ b/Content.Server/Roles/Jobs/JobSystem.cs @@ -0,0 +1,108 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Content.Server.Chat.Managers; +using Content.Server.Mind; +using Content.Server.Players; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Roles.Jobs; + +/// +/// Handles the job data on mind entities. +/// +public sealed class JobSystem : EntitySystem +{ + [Dependency] private readonly IChatManager _chat = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly RoleSystem _roles = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + public override void Initialize() + { + SubscribeLocalEvent(MindOnDoGreeting); + } + + private void MindOnDoGreeting(EntityUid mindId, JobComponent component, RoleAddedEvent args) + { + if (!_mind.TryGetSession(mindId, out var session)) + return; + + if (!MindTryGetJob(mindId, out _, out var prototype)) + return; + + _chat.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name", + ("jobName", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(prototype.LocalizedName)))); + + if (prototype.RequireAdminNotify) + _chat.DispatchServerMessage(session, Loc.GetString("job-greet-important-disconnect-admin-notify")); + + _chat.DispatchServerMessage(session, Loc.GetString("job-greet-supervisors-warning", + ("jobName", Name), + ("supervisors", Loc.GetString(prototype.Supervisors)))); + } + + public void MindAddJob(EntityUid mindId, string jobPrototypeId) + { + if (MindHasJobWithId(mindId, jobPrototypeId)) + return; + + _roles.MindAddRole(mindId, new JobComponent { PrototypeId = jobPrototypeId }); + } + + public bool MindHasJobWithId(EntityUid? mindId, string prototypeId) + { + return CompOrNull(mindId)?.PrototypeId == prototypeId; + } + + public bool MindTryGetJob( + [NotNullWhen(true)] EntityUid? mindId, + [NotNullWhen(true)] out JobComponent? comp, + [NotNullWhen(true)] out JobPrototype? prototype) + { + comp = null; + prototype = null; + + return TryComp(mindId, out comp) && + comp.PrototypeId != null && + _prototypes.TryIndex(comp.PrototypeId, out prototype); + } + + /// + /// Tries to get the job name for this mind. + /// Returns unknown if not found. + /// + public bool MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId, out string name) + { + if (MindTryGetJob(mindId, out _, out var prototype)) + { + name = prototype.LocalizedName; + return true; + } + + name = Loc.GetString("generic-unknown-title"); + return false; + } + + /// + /// Tries to get the job name for this mind. + /// Returns unknown if not found. + /// + public string MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId) + { + MindTryGetJobName(mindId, out var name); + return name; + } + + public bool CanBeAntag(IPlayerSession player) + { + if (player.ContentData() is not { Mind: { } mindId }) + return false; + + if (!MindTryGetJob(mindId, out _, out var prototype)) + return true; + + return prototype.CanBeAntag; + } +} diff --git a/Content.Server/Roles/MindGetAllRolesEvent.cs b/Content.Server/Roles/MindGetAllRolesEvent.cs new file mode 100644 index 0000000000..bdbfb49fc3 --- /dev/null +++ b/Content.Server/Roles/MindGetAllRolesEvent.cs @@ -0,0 +1,19 @@ +using Content.Shared.Players.PlayTimeTracking; + +namespace Content.Server.Roles; + +/// +/// Event raised on a mind entity to get all roles that a player has. +/// +/// The list of roles on the player. +[ByRefEvent] +public readonly record struct MindGetAllRolesEvent(List Roles); + +/// +/// Returned by to give some information about a player's role. +/// +/// Role component associated with the mind entity id. +/// Name of the role. +/// Whether or not this role makes this player an antagonist. +/// The id associated with the role. +public readonly record struct RoleInfo(Component Component, string Name, bool Antagonist, string? PlayTimeTrackerId = null); diff --git a/Content.Server/Roles/MindIsAntagonistEvent.cs b/Content.Server/Roles/MindIsAntagonistEvent.cs new file mode 100644 index 0000000000..b5012823f0 --- /dev/null +++ b/Content.Server/Roles/MindIsAntagonistEvent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Roles; + +/// +/// Event raised on a mind entity id to get whether or not the player is considered an antagonist, +/// depending on their roles. +/// +/// Whether or not the player is an antagonist. +[ByRefEvent] +public record struct MindIsAntagonistEvent(bool IsAntagonist); diff --git a/Content.Server/Roles/NukeopsRole.cs b/Content.Server/Roles/NukeopsRole.cs deleted file mode 100644 index bbb8ca1da3..0000000000 --- a/Content.Server/Roles/NukeopsRole.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -public sealed class NukeopsRole : AntagonistRole -{ - public NukeopsRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind, antagPrototype) { } -} diff --git a/Content.Server/Roles/NukeopsRoleComponent.cs b/Content.Server/Roles/NukeopsRoleComponent.cs new file mode 100644 index 0000000000..1f11100e0c --- /dev/null +++ b/Content.Server/Roles/NukeopsRoleComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Roles; + +/// +/// Added to mind entities to tag that they are a nuke operative. +/// +[RegisterComponent] +public sealed partial class NukeopsRoleComponent : AntagonistRoleComponent +{ +} diff --git a/Content.Server/Roles/RemoveRoleCommand.cs b/Content.Server/Roles/RemoveRoleCommand.cs index 00b6492404..58cea3e23d 100644 --- a/Content.Server/Roles/RemoveRoleCommand.cs +++ b/Content.Server/Roles/RemoveRoleCommand.cs @@ -1,8 +1,7 @@ using Content.Server.Administration; -using Content.Server.Mind; using Content.Server.Players; +using Content.Server.Roles.Jobs; using Content.Shared.Administration; -using Content.Shared.Roles; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.Prototypes; @@ -44,9 +43,10 @@ namespace Content.Server.Roles return; } - var role = new Job(mind, _prototypeManager.Index(args[1])); - var mindSystem = _entityManager.System(); - mindSystem.RemoveRole(mind, role); + var roles = _entityManager.System(); + var jobs = _entityManager.System(); + if (jobs.MindHasJobWithId(mind, args[1])) + roles.MindRemoveRole(mind.Value); } } } diff --git a/Content.Server/Roles/Role.cs b/Content.Server/Roles/Role.cs deleted file mode 100644 index e1d58446e1..0000000000 --- a/Content.Server/Roles/Role.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Hey look, -// Antag Datums. - -namespace Content.Server.Roles -{ - /// - /// The Role is a basic building block for, - /// well, IC roles. - /// This can be anything and is not necessarily limited to antagonists. - /// - public abstract class Role - { - /// - /// The mind owning this role instance. - /// - [ViewVariables] - public Mind.Mind Mind { get; } - - /// - /// A friendly name for this role type. - /// - [ViewVariables] - public abstract string Name { get; } - - /// - /// Whether this role should be considered antagonistic or not. - /// - [ViewVariables] - public abstract bool Antagonist { get; } - - protected Role(Mind.Mind mind) - { - Mind = mind; - } - - /// - /// Called when a mind (player) first gets this role, to greet them. - /// - public virtual void Greet() - { - - } - } -} diff --git a/Content.Server/Roles/RoleAddedEvent.cs b/Content.Server/Roles/RoleAddedEvent.cs index 789ea25869..3874946a3d 100644 --- a/Content.Server/Roles/RoleAddedEvent.cs +++ b/Content.Server/Roles/RoleAddedEvent.cs @@ -1,7 +1,11 @@ -namespace Content.Server.Roles -{ - public sealed class RoleAddedEvent : RoleEvent - { - public RoleAddedEvent(Mind.Mind mind, Role role) : base(mind, role) { } - } -} +using Content.Server.Mind; + +namespace Content.Server.Roles; + +/// +/// Event raised on player entities to indicate that a role was added to their mind. +/// +/// The mind id associated with the player. +/// The mind component associated with the mind id. +/// Whether or not the role makes the player an antagonist. +public sealed record RoleAddedEvent(EntityUid MindId, MindComponent Mind, bool Antagonist) : RoleEvent(MindId, Mind, Antagonist); diff --git a/Content.Server/Roles/RoleEvent.cs b/Content.Server/Roles/RoleEvent.cs index 6bd5a34d9d..a7b28379b2 100644 --- a/Content.Server/Roles/RoleEvent.cs +++ b/Content.Server/Roles/RoleEvent.cs @@ -1,14 +1,11 @@ -namespace Content.Server.Roles -{ - public abstract class RoleEvent : EntityEventArgs - { - public readonly Mind.Mind Mind; - public readonly Role Role; +using Content.Server.Mind; - public RoleEvent(Mind.Mind mind, Role role) - { - Mind = mind; - Role = role; - } - } -} +namespace Content.Server.Roles; + +/// +/// Base event raised on player entities to indicate that something changed about one of their roles. +/// +/// The mind id associated with the player. +/// The mind component associated with the mind id. +/// Whether or not the role makes the player an antagonist. +public abstract record RoleEvent(EntityUid MindId, MindComponent Mind, bool Antagonist); diff --git a/Content.Server/Roles/RoleRemovedEvent.cs b/Content.Server/Roles/RoleRemovedEvent.cs index dcacdae6ad..11104a5db1 100644 --- a/Content.Server/Roles/RoleRemovedEvent.cs +++ b/Content.Server/Roles/RoleRemovedEvent.cs @@ -1,7 +1,14 @@ -namespace Content.Server.Roles -{ - public sealed class RoleRemovedEvent : RoleEvent - { - public RoleRemovedEvent(Mind.Mind mind, Role role) : base(mind, role) { } - } -} +using Content.Server.Mind; + +namespace Content.Server.Roles; + +/// +/// Event raised on player entities to indicate that a role was removed from their mind. +/// +/// The mind id associated with the player. +/// The mind component associated with the mind id. +/// +/// Whether or not the role made the player an antagonist. +/// They may still be one due to one of their other roles. +/// +public sealed record RoleRemovedEvent(EntityUid MindId, MindComponent Mind, bool Antagonist) : RoleEvent(MindId, Mind, Antagonist); diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs new file mode 100644 index 0000000000..c63a44155f --- /dev/null +++ b/Content.Server/Roles/RoleSystem.cs @@ -0,0 +1,162 @@ +using Content.Server.Administration.Logs; +using Content.Server.Mind; +using Content.Server.Roles.Jobs; +using Content.Shared.Database; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; + +namespace Content.Server.Roles; + +public sealed class RoleSystem : EntitySystem +{ + [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + [Dependency] private readonly MindSystem _minds = default!; + + // TODO please lord make role entities + private readonly HashSet _antagTypes = new(); + + public override void Initialize() + { + // TODO make roles entities + SubscribeLocalEvent(OnJobGetAllRoles); + + SubscribeAntagEvents(); + SubscribeAntagEvents(); + SubscribeAntagEvents(); + SubscribeAntagEvents(); + } + + private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args) + { + var name = "game-ticker-unknown-role"; + if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out JobPrototype? job)) + { + name = job.Name; + } + + name = Loc.GetString(name); + + args.Roles.Add(new RoleInfo(component, name, false)); + } + + private void SubscribeAntagEvents() where T : AntagonistRoleComponent + { + SubscribeLocalEvent((EntityUid _, T component, ref MindGetAllRolesEvent args) => + { + var name = "game-ticker-unknown-role"; + if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out AntagPrototype? antag)) + { + name = antag.Name; + } + name = Loc.GetString(name); + + args.Roles.Add(new RoleInfo(component, name, true)); + }); + + SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => args.IsAntagonist = true); + _antagTypes.Add(typeof(T)); + } + + /// + /// Gives this mind a new role. + /// + /// The mind to add the role to. + /// The role instance to add. + /// The role type to add. + /// The instance of the role. + /// + /// Thrown if we already have a role with this type. + /// + public void MindAddRole(EntityUid mindId, T component, MindComponent? mind = null) where T : Component, new() + { + if (!Resolve(mindId, ref mind)) + return; + + if (HasComp(mindId)) + { + throw new ArgumentException($"We already have this role: {typeof(T)}"); + } + + AddComp(mindId, component); + var antagonist = IsAntagonistRole(); + + var message = new RoleAddedEvent(mindId, mind, antagonist); + if (mind.OwnedEntity != null) + { + RaiseLocalEvent(mind.OwnedEntity.Value, message, true); + } + + _adminLogger.Add(LogType.Mind, LogImpact.Low, + $"'Role {typeof(T).Name}' added to mind of {_minds.MindOwnerLoggingString(mind)}"); + } + + /// + /// Removes a role from this mind. + /// + /// The mind to remove the role from. + /// The type of the role to remove. + /// + /// Thrown if we do not have this role. + /// + public void MindRemoveRole(EntityUid mindId) where T : Component + { + if (!RemComp(mindId)) + { + throw new ArgumentException($"We do not have this role: {typeof(T)}"); + } + + var mind = Comp(mindId); + var antagonist = IsAntagonistRole(); + var message = new RoleRemovedEvent(mindId, mind, antagonist); + + if (mind.OwnedEntity != null) + { + RaiseLocalEvent(mind.OwnedEntity.Value, message, true); + } + _adminLogger.Add(LogType.Mind, LogImpact.Low, + $"'Role {typeof(T).Name}' removed from mind of {_minds.MindOwnerLoggingString(mind)}"); + } + + public bool MindTryRemoveRole(EntityUid mindId) where T : Component + { + if (!MindHasRole(mindId)) + return false; + + MindRemoveRole(mindId); + return true; + } + + public bool MindHasRole(EntityUid mindId) where T : Component + { + return HasComp(mindId); + } + + public List MindGetAllRoles(EntityUid mindId) + { + var ev = new MindGetAllRolesEvent(new List()); + RaiseLocalEvent(mindId, ref ev); + return ev.Roles; + } + + public bool MindIsAntagonist(EntityUid? mindId) + { + if (mindId == null) + return false; + + var ev = new MindIsAntagonistEvent(); + RaiseLocalEvent(mindId.Value, ref ev); + return ev.IsAntagonist; + } + + public string? MindGetBriefing(EntityUid? mindId) + { + // TODO this should be an event + return CompOrNull(mindId)?.Briefing; + } + + public bool IsAntagonistRole() + { + return _antagTypes.Contains(typeof(T)); + } +} diff --git a/Content.Server/Roles/SubvertedSiliconRole.cs b/Content.Server/Roles/SubvertedSiliconRole.cs deleted file mode 100644 index 1da518d021..0000000000 --- a/Content.Server/Roles/SubvertedSiliconRole.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -public sealed class SubvertedSiliconRole : AntagonistRole -{ - public SubvertedSiliconRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind, antagPrototype) { } -} diff --git a/Content.Server/Roles/SubvertedSiliconRoleComponent.cs b/Content.Server/Roles/SubvertedSiliconRoleComponent.cs new file mode 100644 index 0000000000..76d294cebf --- /dev/null +++ b/Content.Server/Roles/SubvertedSiliconRoleComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.Roles; + +[RegisterComponent] +public sealed partial class SubvertedSiliconRoleComponent : AntagonistRoleComponent +{ +} diff --git a/Content.Server/Roles/TraitorRole.cs b/Content.Server/Roles/TraitorRole.cs deleted file mode 100644 index adab41eed2..0000000000 --- a/Content.Server/Roles/TraitorRole.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Server.Chat.Managers; -using Content.Shared.PDA; -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -public sealed class TraitorRole : AntagonistRole -{ - public TraitorRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind, antagPrototype) { } -} diff --git a/Content.Server/Roles/TraitorRoleComponent.cs b/Content.Server/Roles/TraitorRoleComponent.cs new file mode 100644 index 0000000000..61af9a4390 --- /dev/null +++ b/Content.Server/Roles/TraitorRoleComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server.Roles; + +[RegisterComponent] +public sealed partial class TraitorRoleComponent : AntagonistRoleComponent +{ + public string? Briefing; +} diff --git a/Content.Server/Roles/ZombieRole.cs b/Content.Server/Roles/ZombieRole.cs deleted file mode 100644 index 75a4807664..0000000000 --- a/Content.Server/Roles/ZombieRole.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -public sealed class ZombieRole : AntagonistRole -{ - public ZombieRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind, antagPrototype) { } -} diff --git a/Content.Server/Roles/ZombieRoleComponent.cs b/Content.Server/Roles/ZombieRoleComponent.cs new file mode 100644 index 0000000000..7f8d9eb44b --- /dev/null +++ b/Content.Server/Roles/ZombieRoleComponent.cs @@ -0,0 +1,6 @@ +namespace Content.Server.Roles; + +[RegisterComponent] +public sealed partial class ZombieRoleComponent : AntagonistRoleComponent +{ +} diff --git a/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs b/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs index 6e70052060..8a03bd7589 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.MMI.cs @@ -40,8 +40,8 @@ public sealed partial class BorgSystem linked.LinkedMMI = uid; Dirty(uid, component); - if (_mind.TryGetMind(ent, out var mind)) - _mind.TransferTo(mind, uid, true); + if (_mind.TryGetMind(ent, out var mindId, out var mind)) + _mind.TransferTo(mindId, uid, true, mind: mind); _appearance.SetData(uid, MMIVisuals.BrainPresent, true); } @@ -58,9 +58,11 @@ public sealed partial class BorgSystem private void OnMMILinkedMindAdded(EntityUid uid, MMILinkedComponent component, MindAddedMessage args) { - if (!_mind.TryGetMind(uid, out var mind) || component.LinkedMMI == null) + if (!_mind.TryGetMind(uid, out var mindId, out var mind) || + component.LinkedMMI == null) return; - _mind.TransferTo(mind, component.LinkedMMI, true); + + _mind.TransferTo(mindId, component.LinkedMMI, true, mind: mind); } private void OnMMILinkedRemoved(EntityUid uid, MMILinkedComponent component, EntGotRemovedFromContainerMessage args) @@ -72,8 +74,8 @@ public sealed partial class BorgSystem return; RemComp(uid, component); - if (_mind.TryGetMind(linked, out var mind)) - _mind.TransferTo(mind, uid, true); + if (_mind.TryGetMind(linked, out var mindId, out var mind)) + _mind.TransferTo(mindId, uid, true, mind: mind); _appearance.SetData(linked, MMIVisuals.BrainPresent, false); } diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index 9b9284c2a9..24608f849b 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -96,7 +96,7 @@ public sealed partial class BorgSystem : SharedBorgSystem brain != null && component.BrainWhitelist?.IsValid(used) != false) { - if (_mind.TryGetMind(used, out var mind) && mind.Session != null) + if (_mind.TryGetMind(used, out _, out var mind) && mind.Session != null) { if (!CanPlayerBeBorgged(mind.Session)) { @@ -127,9 +127,9 @@ public sealed partial class BorgSystem : SharedBorgSystem { base.OnInserted(uid, component, args); - if (HasComp(args.Entity) && _mind.TryGetMind(args.Entity, out var mind)) + if (HasComp(args.Entity) && _mind.TryGetMind(args.Entity, out var mindId, out var mind)) { - _mind.TransferTo(mind, uid); + _mind.TransferTo(mindId, uid, mind: mind); } } @@ -137,9 +137,10 @@ public sealed partial class BorgSystem : SharedBorgSystem { base.OnRemoved(uid, component, args); - if (HasComp(args.Entity) && _mind.TryGetMind(uid, out var mind)) + if (HasComp(args.Entity) & + _mind.TryGetMind(uid, out var mindId, out var mind)) { - _mind.TransferTo(mind, args.Entity); + _mind.TransferTo(mindId, args.Entity, mind: mind); } } @@ -171,7 +172,7 @@ public sealed partial class BorgSystem : SharedBorgSystem if (_powerCell.HasDrawCharge(uid, draw)) { // only reenable the powerdraw if a player has the role. - if (!draw.Drawing && _mind.TryGetMind(uid, out _)) + if (!draw.Drawing && _mind.TryGetMind(uid, out _, out _)) _powerCell.SetPowerCellDrawEnabled(uid, true); EnableBorgAbilities(uid, component); @@ -209,7 +210,7 @@ public sealed partial class BorgSystem : SharedBorgSystem container.ID != chassisComponent.BrainContainerId) return; - if (!_mind.TryGetMind(uid, out var mind) || mind.Session == null) + if (!_mind.TryGetMind(uid, out var mindId, out var mind) || mind.Session == null) return; if (!CanPlayerBeBorgged(mind.Session)) @@ -220,7 +221,7 @@ public sealed partial class BorgSystem : SharedBorgSystem return; } - _mind.TransferTo(mind, containerEnt); + _mind.TransferTo(mindId, containerEnt, mind: mind); } private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotComponent = null) diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index e10c1d95ca..fd1e38ff2c 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Server.Administration; using Content.Server.Chat.Managers; using Content.Server.GameTicking; @@ -14,15 +13,14 @@ using Content.Shared.Chat; using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.Examine; -using Content.Shared.Roles; using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; +using Content.Shared.Stunnable; using Content.Shared.Wires; using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; -using Content.Shared.Stunnable; namespace Content.Server.Silicons.Laws; @@ -37,6 +35,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem [Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!; [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly RoleSystem _roles = default!; /// public override void Initialize() @@ -177,20 +176,18 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem if (component.AntagonistRole == null) return; - if (args.OldMind.Roles.FirstOrDefault(r => r is SubvertedSiliconRole) is not { } role) - return; - - _mind.RemoveRole(args.OldMind, role); + _roles.MindTryRemoveRole(args.OldMindId); } private void EnsureEmaggedRole(EntityUid uid, EmagSiliconLawComponent component) { - if (component.AntagonistRole == null || !_mind.TryGetMind(uid, out var mind)) + if (component.AntagonistRole == null || !_mind.TryGetMind(uid, out var mindId, out _)) return; - if (_mind.HasRole(mind)) + if (_roles.MindHasRole(mindId)) return; - _mind.AddRole(mind, new SubvertedSiliconRole(mind, _prototype.Index(component.AntagonistRole))); + + _roles.MindAddRole(mindId, new SubvertedSiliconRoleComponent { PrototypeId = component.AntagonistRole }); } public List GetLaws(EntityUid uid, SiliconLawBoundComponent? component = null) diff --git a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs index 516fc10d48..755153d309 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs @@ -1,6 +1,4 @@ -using System.Linq; -using Content.Server.GameTicking; -using Content.Server.Shuttles.Components; +using Content.Server.GameTicking; using Content.Server.Spawners.Components; using Content.Server.Station.Systems; using Robust.Shared.Map; @@ -39,7 +37,9 @@ public sealed class SpawnPointSystem : EntitySystem possiblePositions.Add(xform.Coordinates); } - if (_gameTicker.RunLevel != GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.Job && (args.Job == null || spawnPoint.Job?.ID == args.Job.Prototype.ID)) + if (_gameTicker.RunLevel != GameRunLevel.InRound && + spawnPoint.SpawnType == SpawnPointType.Job && + (args.Job == null || spawnPoint.Job?.ID == args.Job.PrototypeId)) { possiblePositions.Add(xform.Coordinates); } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index fa54778b26..4229c514db 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -1,26 +1,23 @@ using Content.Server.Access.Systems; using Content.Server.DetailExaminable; -using Content.Server.Hands.Systems; using Content.Server.Humanoid; using Content.Server.IdentityManagement; using Content.Server.Mind.Commands; using Content.Server.PDA; -using Content.Server.Roles; +using Content.Server.Roles.Jobs; using Content.Server.Station.Components; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.CCVar; -using Content.Shared.Hands.Components; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; -using Content.Shared.Inventory; using Content.Shared.PDA; using Content.Shared.Preferences; using Content.Shared.Random; using Content.Shared.Random.Helpers; using Content.Shared.Roles; -using Content.Shared.StatusIcon; using Content.Shared.Station; +using Content.Shared.StatusIcon; using JetBrains.Annotations; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -67,7 +64,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem /// /// This only spawns the character, and does none of the mind-related setup you'd need for it to be playable. /// - public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, Job? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null) + public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null) { if (station != null && !Resolve(station.Value, ref stationSpawning)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); @@ -95,16 +92,18 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem /// The spawned entity public EntityUid SpawnPlayerMob( EntityCoordinates coordinates, - Job? job, + JobComponent? job, HumanoidCharacterProfile? profile, EntityUid? station, EntityUid? entity = null) { + _prototypeManager.TryIndex(job?.PrototypeId ?? string.Empty, out JobPrototype? prototype); + // If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff. - if (job?.JobEntity != null) + if (prototype?.JobEntity != null) { DebugTools.Assert(entity is null); - var jobEntity = EntityManager.SpawnEntity(job.JobEntity, coordinates); + var jobEntity = EntityManager.SpawnEntity(prototype.JobEntity, coordinates); MakeSentientCommand.MakeSentient(jobEntity, EntityManager); DoJobSpecials(job, jobEntity); _identity.QueueIdentityUpdate(jobEntity); @@ -137,12 +136,12 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem profile = HumanoidCharacterProfile.RandomWithSpecies(speciesId); } - if (job?.StartingGear != null) + if (prototype?.StartingGear != null) { - var startingGear = _prototypeManager.Index(job.StartingGear); + var startingGear = _prototypeManager.Index(prototype.StartingGear); EquipStartingGear(entity.Value, startingGear, profile); if (profile != null) - EquipIdCard(entity.Value, profile.Name, job.Prototype, station); + EquipIdCard(entity.Value, profile.Name, prototype, station); } if (profile != null) @@ -160,9 +159,12 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem return entity.Value; } - private static void DoJobSpecials(Job? job, EntityUid entity) + private void DoJobSpecials(JobComponent? job, EntityUid entity) { - foreach (var jobSpecial in job?.Prototype.Special ?? Array.Empty()) + if (!_prototypeManager.TryIndex(job?.PrototypeId ?? string.Empty, out JobPrototype? prototype)) + return; + + foreach (var jobSpecial in prototype.Special) { jobSpecial.AfterEquip(entity); } @@ -224,7 +226,7 @@ public sealed class PlayerSpawningEvent : EntityEventArgs /// /// The job to use, if any. /// - public readonly Job? Job; + public readonly JobComponent? Job; /// /// The profile to use, if any. /// @@ -234,7 +236,7 @@ public sealed class PlayerSpawningEvent : EntityEventArgs /// public readonly EntityUid? Station; - public PlayerSpawningEvent(Job? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station) + public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station) { Job = job; HumanoidCharacterProfile = humanoidCharacterProfile; diff --git a/Content.Server/Store/Conditions/BuyerAntagCondition.cs b/Content.Server/Store/Conditions/BuyerAntagCondition.cs index 28e146934b..0330a40300 100644 --- a/Content.Server/Store/Conditions/BuyerAntagCondition.cs +++ b/Content.Server/Store/Conditions/BuyerAntagCondition.cs @@ -1,10 +1,10 @@ -using Content.Server.Mind.Components; +using Content.Server.Mind; using Content.Server.Roles; -using Content.Server.Traitor; using Content.Shared.Roles; +using Content.Shared.Store; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; -namespace Content.Shared.Store.Conditions; +namespace Content.Server.Store.Conditions; /// /// Allows a store entry to be filtered out based on the user's antag role. @@ -28,18 +28,22 @@ public sealed partial class BuyerAntagCondition : ListingCondition public override bool Condition(ListingConditionArgs args) { var ent = args.EntityManager; + var minds = ent.System(); - if (!ent.TryGetComponent(args.Buyer, out var mind) || mind.Mind == null) + if (!minds.TryGetMind(args.Buyer, out var mindId, out var mind)) return true; + var roleSystem = ent.System(); + var roles = roleSystem.MindGetAllRoles(mindId); + if (Blacklist != null) { - foreach (var role in mind.Mind.AllRoles) + foreach (var role in roles) { - if (role is not AntagonistRole blacklistantag) + if (role.Component is not AntagonistRoleComponent blacklistantag) continue; - if (Blacklist.Contains(blacklistantag.Prototype.ID)) + if (blacklistantag.PrototypeId != null && Blacklist.Contains(blacklistantag.PrototypeId)) return false; } } @@ -47,12 +51,12 @@ public sealed partial class BuyerAntagCondition : ListingCondition if (Whitelist != null) { var found = false; - foreach (var role in mind.Mind.AllRoles) + foreach (var role in roles) { - if (role is not AntagonistRole antag) + if (role.Component is not AntagonistRoleComponent antag) continue; - if (Whitelist.Contains(antag.Prototype.ID)) + if (antag.PrototypeId != null && Whitelist.Contains(antag.PrototypeId)) found = true; } if (!found) diff --git a/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs b/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs index 6d7f50f2c4..a5ed3f2f9a 100644 --- a/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs +++ b/Content.Server/Store/Conditions/BuyerDepartmentCondition.cs @@ -1,9 +1,9 @@ -using Content.Server.Mind.Components; -using Content.Server.Roles; +using Content.Server.Mind; +using Content.Server.Roles.Jobs; using Content.Shared.Roles; using Content.Shared.Store; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; namespace Content.Server.Store.Conditions; @@ -30,45 +30,45 @@ public sealed partial class BuyerDepartmentCondition : ListingCondition var prototypeManager = IoCManager.Resolve(); var ent = args.EntityManager; + var minds = ent.System(); - if (!ent.TryGetComponent(args.Buyer, out var mind) || mind.Mind == null) - return true; //this is for things like surplus crate + // this is for things like surplus crate + if (!minds.TryGetMind(args.Buyer, out var mindId, out _)) + return true; - if (Blacklist != null) + var jobs = ent.System(); + if (jobs.MindTryGetJob(mindId, out var job, out _)) { - foreach (var role in mind.Mind.AllRoles) + if (Blacklist != null) { - if (role is not Job job) - continue; - foreach (var department in prototypeManager.EnumeratePrototypes()) - if (department.Roles.Contains(job.Prototype.ID)) - if (Blacklist.Contains(department.ID)) - return false; + { + if (job.PrototypeId == null || + !department.Roles.Contains(job.PrototypeId) || + !Blacklist.Contains(department.ID)) + continue; + + return false; + } } - } - if (Whitelist != null) - { - var found = false; - foreach (var role in mind.Mind.AllRoles) + if (Whitelist != null) { - if (role is not Job job) - continue; - + var found = false; foreach (var department in prototypeManager.EnumeratePrototypes()) - if (department.Roles.Contains(job.Prototype.ID)) - if (Whitelist.Contains(department.ID)) - { - found = true; - break; - } + { + if (job.PrototypeId == null || + !department.Roles.Contains(job.PrototypeId) || + !Whitelist.Contains(department.ID)) + continue; - if (found) + found = true; break; + } + + if (!found) + return false; } - if (!found) - return false; } return true; diff --git a/Content.Server/Store/Conditions/BuyerJobCondition.cs b/Content.Server/Store/Conditions/BuyerJobCondition.cs index f163305f41..c85e6f1144 100644 --- a/Content.Server/Store/Conditions/BuyerJobCondition.cs +++ b/Content.Server/Store/Conditions/BuyerJobCondition.cs @@ -1,5 +1,5 @@ -using Content.Server.Mind.Components; -using Content.Server.Roles; +using Content.Server.Mind; +using Content.Server.Roles.Jobs; using Content.Shared.Roles; using Content.Shared.Store; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; @@ -27,35 +27,26 @@ public sealed partial class BuyerJobCondition : ListingCondition public override bool Condition(ListingConditionArgs args) { var ent = args.EntityManager; + var minds = ent.System(); - if (!ent.TryGetComponent(args.Buyer, out var mind) || mind.Mind == null) - return true; //this is for things like surplus crate + // this is for things like surplus crate + if (!minds.TryGetMind(args.Buyer, out var mindId, out _)) + return true; - if (Blacklist != null) + var jobs = ent.System(); + if (jobs.MindTryGetJob(mindId, out var job, out _)) { - foreach (var role in mind.Mind.AllRoles) + if (Blacklist != null) { - if (role is not Job job) - continue; - - if (Blacklist.Contains(job.Prototype.ID)) + if (job.PrototypeId != null && Blacklist.Contains(job.PrototypeId)) return false; } - } - if (Whitelist != null) - { - var found = false; - foreach (var role in mind.Mind.AllRoles) + if (Whitelist != null) { - if (role is not Job job) - continue; - - if (Whitelist.Contains(job.Prototype.ID)) - found = true; + if (job.PrototypeId == null || !Whitelist.Contains(job.PrototypeId)) + return false; } - if (!found) - return false; } return true; diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index d504ca1a11..9cfc4c9e26 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -6,8 +6,12 @@ using Content.Server.Ghost.Roles.Components; using Content.Server.Humanoid; using Content.Server.IdentityManagement; using Content.Server.Inventory; +using Content.Server.Mind; using Content.Server.Mind.Commands; -using Content.Server.Mind.Components; +using Content.Server.NPC; +using Content.Server.NPC.Components; +using Content.Server.NPC.HTN; +using Content.Server.NPC.Systems; using Content.Server.Nutrition.Components; using Content.Server.Roles; using Content.Server.Speech.Components; @@ -16,11 +20,6 @@ using Content.Shared.CombatMode; using Content.Shared.Damage; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; -using Content.Server.Mind; -using Content.Server.NPC; -using Content.Server.NPC.Components; -using Content.Server.NPC.HTN; -using Content.Server.NPC.Systems; using Content.Shared.Humanoid; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; @@ -28,12 +27,10 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Systems; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; -using Content.Shared.Roles; using Content.Shared.Tools.Components; using Content.Shared.Weapons.Melee; using Content.Shared.Zombies; using Robust.Shared.Audio; -using System.Linq; namespace Content.Server.Zombies { @@ -55,6 +52,7 @@ namespace Content.Server.Zombies [Dependency] private readonly SharedCombatModeSystem _combat = default!; [Dependency] private readonly IChatManager _chatMan = default!; [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly RoleSystem _roles = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -218,11 +216,11 @@ namespace Content.Server.Zombies _identity.QueueIdentityUpdate(target); //He's gotta have a mind - var mindComp = EnsureComp(target); - if (_mind.TryGetMind(target, out var mind, mindComp) && _mind.TryGetSession(mind, out var session)) + var hasMind = _mind.TryGetMind(target, out var mindId, out _); + if (hasMind && _mind.TryGetSession(mindId, out var session)) { //Zombie role for player manifest - _mind.AddRole(mind, new ZombieRole(mind, _protoManager.Index(zombiecomp.ZombieRoleId))); + _roles.MindAddRole(mindId, new ZombieRoleComponent { PrototypeId = zombiecomp.ZombieRoleId }); //Greeting message for new bebe zombers _chatMan.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting")); @@ -238,7 +236,7 @@ namespace Content.Server.Zombies _npc.WakeNPC(target, htn); } - if (!HasComp(target) && !mindComp.HasMind) //this specific component gives build test trouble so pop off, ig + if (!HasComp(target) && !hasMind) //this specific component gives build test trouble so pop off, ig { //yet more hardcoding. Visit zombie.ftl for more information. var ghostRole = EnsureComp(target); diff --git a/Content.Shared/Roles/AntagPrototype.cs b/Content.Shared/Roles/AntagPrototype.cs index 24c5218145..0bdb4d3a96 100644 --- a/Content.Shared/Roles/AntagPrototype.cs +++ b/Content.Shared/Roles/AntagPrototype.cs @@ -8,10 +8,6 @@ namespace Content.Shared.Roles [Prototype("antag")] public sealed class AntagPrototype : IPrototype { - private string _name = string.Empty; - private string _objective = string.Empty; - private string? _description = string.Empty; - [ViewVariables] [IdDataField] public string ID { get; private set; } = default!; diff --git a/Content.Shared/Roles/IRoleTimer.cs b/Content.Shared/Roles/IRoleTimer.cs deleted file mode 100644 index 5361139bb0..0000000000 --- a/Content.Shared/Roles/IRoleTimer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Roles; - -public interface IRoleTimer -{ - string Timer { get; } -} diff --git a/Resources/Locale/en-US/generic.ftl b/Resources/Locale/en-US/generic.ftl index 41d80e3c41..0ecfefdd1c 100644 --- a/Resources/Locale/en-US/generic.ftl +++ b/Resources/Locale/en-US/generic.ftl @@ -5,5 +5,6 @@ generic-article-a = a generic-article-an = an generic-unknown = unknown +generic-unknown-title = Unknown generic-error = error generic-invalid = invalid