Skip to content

Commit

Permalink
Mind tweaks & fixes (space-wizards#21203)
Browse files Browse the repository at this point in the history
  • Loading branch information
ElectroJr authored Oct 24, 2023
1 parent a2bbda4 commit 0880145
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 111 deletions.
20 changes: 20 additions & 0 deletions Content.Client/Mind/MindSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,24 @@ namespace Content.Client.Mind;

public sealed class MindSystem : SharedMindSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MindComponent, AfterAutoHandleStateEvent>(OnHandleState);
}

private void OnHandleState(EntityUid uid, MindComponent component, ref AfterAutoHandleStateEvent args)
{
// Because minds are generally not networked, there might be weird situations were a client thinks multiple
// users share a mind? E.g., if an admin periodical gets sent all minds via some PVS override, but doesn't get
// sent intermediate states? Not sure if this is actually possible, but better to be safe.
foreach (var (user, mind) in UserMinds)
{
if (mind == uid)
UserMinds.Remove(user);
}

if (component.UserId != null)
UserMinds[component.UserId.Value] = uid;
}
}
19 changes: 9 additions & 10 deletions Content.IntegrationTests/Tests/Minds/MindTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,12 @@ await server.WaitAssertion(() =>
var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
var mindId = mindSystem.CreateMind(null);
var mind = entMan.GetComponent<MindComponent>(mindId);
var mind = mindSystem.CreateMind(null);
Assert.That(mind.UserId, Is.EqualTo(null));
Assert.That(mind.Comp.UserId, Is.EqualTo(null));
mindSystem.TransferTo(mindId, entity, mind: mind);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
mindSystem.TransferTo(mind, entity, mind: mind);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind.Owner));
});

await pair.CleanReturnAsync();
Expand All @@ -94,11 +93,11 @@ await server.WaitAssertion(() =>
var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
var mindId = mindSystem.CreateMind(null);
var mindId = mindSystem.CreateMind(null).Owner;
mindSystem.TransferTo(mindId, entity);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
var mind2 = mindSystem.CreateMind(null);
var mind2 = mindSystem.CreateMind(null).Owner;
mindSystem.TransferTo(mind2, entity);
Assert.Multiple(() =>
{
Expand Down Expand Up @@ -184,7 +183,7 @@ await server.WaitAssertion(() =>
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
entMan.EnsureComponent<MindContainerComponent>(targetEntity);
var mind = mindSystem.CreateMind(null);
var mind = mindSystem.CreateMind(null).Owner;
mindSystem.TransferTo(mind, entity);
Expand Down Expand Up @@ -276,7 +275,7 @@ await server.WaitAssertion(() =>
var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
var mindId = mindSystem.CreateMind(null);
var mindId = mindSystem.CreateMind(null).Owner;
var mind = entMan.EnsureComponent<MindComponent>(mindId);
Assert.That(mind.UserId, Is.EqualTo(null));
Expand Down Expand Up @@ -334,7 +333,7 @@ await server.WaitAssertion(() =>
public async Task TestPlayerCanGhost()
{
// Client is needed to spawn session
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true });
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false });
var server = pair.Server;

var entMan = server.ResolveDependency<IServerEntityManager>();
Expand Down
11 changes: 2 additions & 9 deletions Content.Server/Administration/Commands/ControlMob.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Content.Server.Mind;
using Content.Shared.Administration;
using Content.Shared.Mind;
using Robust.Server.Player;
using Robust.Shared.Console;

Expand Down Expand Up @@ -42,14 +42,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args)
return;
}

var mindSystem = _entities.System<SharedMindSystem>();
if (!mindSystem.TryGetMind(target, out var mindId, out var mind))
{
shell.WriteLine(Loc.GetString("shell-entity-is-not-mob"));
return;
}

mindSystem.TransferTo(mindId, target, mind: mind);
_entities.System<MindSystem>().ControlMob(player.UserId, target);
}
}
}
13 changes: 4 additions & 9 deletions Content.Server/Administration/Systems/AdminVerbSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Content.Server.Disposal.Tube.Components;
using Content.Server.EUI;
using Content.Server.Ghost.Roles;
using Content.Server.Mind;
using Content.Server.Mind.Commands;
using Content.Server.Prayer;
using Content.Server.Xenoarchaeology.XenoArtifacts;
Expand All @@ -18,7 +19,6 @@
using Content.Shared.Examine;
using Content.Shared.GameTicking;
using Content.Shared.Inventory;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Popups;
using Content.Shared.Verbs;
Expand Down Expand Up @@ -56,7 +56,7 @@ public sealed partial class AdminVerbSystem : EntitySystem
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly PrayerSystem _prayerSystem = default!;
[Dependency] private readonly EuiManager _eui = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly ToolshedManager _toolshed = default!;
[Dependency] private readonly RejuvenateSystem _rejuvenate = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
Expand Down Expand Up @@ -277,12 +277,7 @@ private void AddDebugVerbs(GetVerbsEvent<Verb> args)
// TODO VERB ICON control mob icon
Act = () =>
{
MakeSentientCommand.MakeSentient(args.Target, EntityManager);
if (!_minds.TryGetMind(player, out var mindId, out var mind))
return;
_mindSystem.TransferTo(mindId, args.Target, ghostCheckOverride: true, mind: mind);
_mindSystem.ControlMob(args.User, args.Target);
},
Impact = LogImpact.High,
ConfirmationPopup = true
Expand Down Expand Up @@ -358,7 +353,7 @@ private void AddDebugVerbs(GetVerbsEvent<Verb> args)
var message = ExamineSystemShared.InRangeUnOccluded(args.User, args.Target)
? Loc.GetString("in-range-unoccluded-verb-on-activate-not-occluded")
: Loc.GetString("in-range-unoccluded-verb-on-activate-occluded");
_popup.PopupEntity(message, args.Target, args.User);
}
};
Expand Down
3 changes: 1 addition & 2 deletions Content.Server/GameTicking/GameTicker.RoundFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,7 @@ public void ShowRoundEndScoreboard(string text = "")
else if (mind.CurrentEntity != null && TryName(mind.CurrentEntity.Value, out var icName))
playerIcName = icName;

var entity = mind.OriginalOwnedEntity;
if (Exists(entity))
if (TryGetEntity(mind.OriginalOwnedEntity, out var entity))
_pvsOverride.AddGlobalOverride(entity.Value, recursive: true);

var roles = _roles.MindGetAllRoles(mindId);
Expand Down
1 change: 1 addition & 0 deletions Content.Server/Mind/Commands/RenameCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args)
{
// Mind
mind.CharacterName = name;
_entManager.Dirty(mindId, mind);
}

// Id Cards
Expand Down
82 changes: 58 additions & 24 deletions Content.Server/Mind/MindSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Administration.Logs;
using Content.Server.GameTicking;
using Content.Server.Mind.Commands;
using Content.Shared.Database;
using Content.Shared.Ghost;
using Content.Shared.Mind;
Expand Down Expand Up @@ -46,20 +47,14 @@ private void OnMindShutdown(EntityUid uid, MindComponent mind, ComponentShutdown
mind.UserId = null;
}

if (!TryComp(mind.OwnedEntity, out MetaDataComponent? meta) || meta.EntityLifeStage >= EntityLifeStage.Terminating)
return;
if (mind.OwnedEntity != null && !TerminatingOrDeleted(mind.OwnedEntity.Value))
TransferTo(uid, null, mind: mind, createGhost: false);

RaiseLocalEvent(mind.OwnedEntity.Value, new MindRemovedMessage(uid, mind), true);
mind.OwnedEntity = null;
mind.OwnedComponent = null;
}

private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent component, ref EntityTerminatingEvent args)
{
// Let's not create ghosts if not in the middle of the round.
if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby)
return;

if (!TryGetMind(uid, out var mindId, out var mind, component))
return;

Expand All @@ -77,6 +72,11 @@ private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent co

TransferTo(mindId, null, createGhost: false, mind: mind);

// Let's not create ghosts if not in the middle of the round.
if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby)
return;

// I just love convoluted entity shutdown logic that results in more entities being spawned.
if (component.GhostOnShutdown && mind.Session != null)
{
var xform = Transform(uid);
Expand Down Expand Up @@ -198,7 +198,7 @@ public override void UnVisit(EntityUid mindId, MindComponent? mind = null)
if (mind.VisitingEntity == null)
return;

RemoveVisitingEntity(mind);
RemoveVisitingEntity(mindId, mind);

if (mind.Session == null || mind.Session.AttachedEntity == mind.VisitingEntity)
return;
Expand All @@ -219,11 +219,10 @@ public override void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostC
if (mind == null && !Resolve(mindId, ref mind))
return;

base.TransferTo(mindId, entity, ghostCheckOverride, createGhost, mind);

if (entity == mind.OwnedEntity)
return;

Dirty(mindId, mind);
MindContainerComponent? component = null;
var alreadyAttached = false;

Expand All @@ -247,27 +246,33 @@ public override void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostC
}
else if (createGhost)
{
// TODO remove this option.
// Transfer-to-null should just detach a mind.
// If people want to create a ghost, that should be done explicitly via some TransferToGhost() method, not
// not implicitly via optional arguments.

var position = Deleted(mind.OwnedEntity)
? _gameTicker.GetObserverSpawnPoint().ToMap(EntityManager, _transform)
: Transform(mind.OwnedEntity.Value).MapPosition;

entity = Spawn("MobObserver", position);
component = EnsureComp<MindContainerComponent>(entity.Value);
var ghostComponent = Comp<GhostComponent>(entity.Value);
_ghosts.SetCanReturnToBody(ghostComponent, false);
}

var oldComp = mind.OwnedComponent;
var oldEntity = mind.OwnedEntity;
if (oldComp != null && oldEntity != null)
if (TryComp(oldEntity, out MindContainerComponent? oldContainer))
{
if (oldComp.Mind != null)
_pvsOverride.ClearOverride(oldComp.Mind.Value);
oldComp.Mind = null;
RaiseLocalEvent(oldEntity.Value, new MindRemovedMessage(oldEntity.Value, mind), true);
oldContainer.Mind = null;
mind.OwnedEntity = null;
Entity<MindComponent> mindEnt = (mindId, mind);
Entity<MindContainerComponent> containerEnt = (oldEntity.Value, oldContainer);
RaiseLocalEvent(oldEntity.Value, new MindRemovedMessage(mindEnt, containerEnt));
RaiseLocalEvent(mindId, new MindGotRemovedEvent(mindEnt, containerEnt));
Dirty(oldEntity.Value, oldContainer);
}

SetOwnedEntity(mind, entity, component);

// Don't do the full deletion cleanup if we're transferring to our VisitingEntity
if (alreadyAttached)
{
Expand All @@ -281,7 +286,7 @@ public override void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostC
|| !TryComp(mind.VisitingEntity!, out GhostComponent? ghostComponent) // visiting entity is not a Ghost
|| !ghostComponent.CanReturnToBody)) // it is a ghost, but cannot return to body anyway, so it's okay
{
RemoveVisitingEntity(mind);
RemoveVisitingEntity(mindId, mind);
}

// Player is CURRENTLY connected.
Expand All @@ -292,11 +297,16 @@ public override void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostC
Log.Info($"Session {session.Name} transferred to entity {entity}.");
}

if (mind.OwnedComponent != null)
if (entity != null)
{
mind.OwnedComponent.Mind = mindId;
RaiseLocalEvent(mind.OwnedEntity!.Value, new MindAddedMessage(), true);
mind.OriginalOwnedEntity ??= mind.OwnedEntity;
component!.Mind = mindId;
mind.OwnedEntity = entity;
mind.OriginalOwnedEntity ??= GetNetEntity(mind.OwnedEntity);
Entity<MindComponent> mindEnt = (mindId, mind);
Entity<MindContainerComponent> containerEnt = (entity.Value, component);
RaiseLocalEvent(entity.Value, new MindAddedMessage(mindEnt, containerEnt));
RaiseLocalEvent(mindId, new MindGotAddedEvent(mindEnt, containerEnt));
Dirty(entity.Value, component);
}
}

Expand All @@ -313,6 +323,7 @@ public override void SetUserId(EntityUid mindId, NetUserId? userId, MindComponen
if (mind.UserId == userId)
return;

Dirty(mindId, mind);
_pvsOverride.ClearOverride(mindId);
if (userId != null && !_players.TryGetPlayerData(userId.Value, out _))
{
Expand Down Expand Up @@ -363,4 +374,27 @@ public override void SetUserId(EntityUid mindId, NetUserId? userId, MindComponen
if (_players.GetPlayerData(userId.Value).ContentData() is { } data)
data.Mind = mindId;
}

public void ControlMob(EntityUid user, EntityUid target)
{
if (TryComp(user, out ActorComponent? actor))
ControlMob(actor.PlayerSession.UserId, target);
}

public void ControlMob(NetUserId user, EntityUid target)
{
var (mindId, mind) = GetOrCreateMind(user);

if (mind.CurrentEntity == target)
return;

if (mind.OwnedEntity == target)
{
UnVisit(mindId, mind);
return;
}

MakeSentientCommand.MakeSentient(target, EntityManager);
TransferTo(mindId, target, ghostCheckOverride: true, mind: mind);
}
}
2 changes: 1 addition & 1 deletion Content.Server/Silicons/Laws/SiliconLawSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private void OnEmagMindRemoved(EntityUid uid, EmagSiliconLawComponent component,
if (component.AntagonistRole == null)
return;

_roles.MindTryRemoveRole<SubvertedSiliconRoleComponent>(args.OldMindId);
_roles.MindTryRemoveRole<SubvertedSiliconRoleComponent>(args.Mind);
}

private void EnsureEmaggedRole(EntityUid uid, EmagSiliconLawComponent component)
Expand Down
Loading

0 comments on commit 0880145

Please sign in to comment.