Mind tweaks & fixes (#21203)
This commit is contained in:
@@ -4,4 +4,24 @@ namespace Content.Client.Mind;
|
|||||||
|
|
||||||
public sealed class MindSystem : SharedMindSystem
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,13 +67,12 @@ public sealed partial class MindTests
|
|||||||
var entity = entMan.SpawnEntity(null, new MapCoordinates());
|
var entity = entMan.SpawnEntity(null, new MapCoordinates());
|
||||||
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
||||||
|
|
||||||
var mindId = mindSystem.CreateMind(null);
|
var mind = mindSystem.CreateMind(null);
|
||||||
var mind = entMan.GetComponent<MindComponent>(mindId);
|
|
||||||
|
|
||||||
Assert.That(mind.UserId, Is.EqualTo(null));
|
Assert.That(mind.Comp.UserId, Is.EqualTo(null));
|
||||||
|
|
||||||
mindSystem.TransferTo(mindId, entity, mind: mind);
|
mindSystem.TransferTo(mind, entity, mind: mind);
|
||||||
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
|
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind.Owner));
|
||||||
});
|
});
|
||||||
|
|
||||||
await pair.CleanReturnAsync();
|
await pair.CleanReturnAsync();
|
||||||
@@ -94,11 +93,11 @@ public sealed partial class MindTests
|
|||||||
var entity = entMan.SpawnEntity(null, new MapCoordinates());
|
var entity = entMan.SpawnEntity(null, new MapCoordinates());
|
||||||
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
||||||
|
|
||||||
var mindId = mindSystem.CreateMind(null);
|
var mindId = mindSystem.CreateMind(null).Owner;
|
||||||
mindSystem.TransferTo(mindId, entity);
|
mindSystem.TransferTo(mindId, entity);
|
||||||
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
|
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
|
||||||
|
|
||||||
var mind2 = mindSystem.CreateMind(null);
|
var mind2 = mindSystem.CreateMind(null).Owner;
|
||||||
mindSystem.TransferTo(mind2, entity);
|
mindSystem.TransferTo(mind2, entity);
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
@@ -184,7 +183,7 @@ public sealed partial class MindTests
|
|||||||
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
||||||
entMan.EnsureComponent<MindContainerComponent>(targetEntity);
|
entMan.EnsureComponent<MindContainerComponent>(targetEntity);
|
||||||
|
|
||||||
var mind = mindSystem.CreateMind(null);
|
var mind = mindSystem.CreateMind(null).Owner;
|
||||||
|
|
||||||
mindSystem.TransferTo(mind, entity);
|
mindSystem.TransferTo(mind, entity);
|
||||||
|
|
||||||
@@ -276,7 +275,7 @@ public sealed partial class MindTests
|
|||||||
var entity = entMan.SpawnEntity(null, new MapCoordinates());
|
var entity = entMan.SpawnEntity(null, new MapCoordinates());
|
||||||
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
|
||||||
|
|
||||||
var mindId = mindSystem.CreateMind(null);
|
var mindId = mindSystem.CreateMind(null).Owner;
|
||||||
var mind = entMan.EnsureComponent<MindComponent>(mindId);
|
var mind = entMan.EnsureComponent<MindComponent>(mindId);
|
||||||
|
|
||||||
Assert.That(mind.UserId, Is.EqualTo(null));
|
Assert.That(mind.UserId, Is.EqualTo(null));
|
||||||
@@ -334,7 +333,7 @@ public sealed partial class MindTests
|
|||||||
public async Task TestPlayerCanGhost()
|
public async Task TestPlayerCanGhost()
|
||||||
{
|
{
|
||||||
// Client is needed to spawn session
|
// 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 server = pair.Server;
|
||||||
|
|
||||||
var entMan = server.ResolveDependency<IServerEntityManager>();
|
var entMan = server.ResolveDependency<IServerEntityManager>();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
using Content.Server.Mind;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Mind;
|
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
@@ -42,14 +42,7 @@ namespace Content.Server.Administration.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mindSystem = _entities.System<SharedMindSystem>();
|
_entities.System<MindSystem>().ControlMob(player.UserId, target);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Content.Server.Disposal.Tube;
|
|||||||
using Content.Server.Disposal.Tube.Components;
|
using Content.Server.Disposal.Tube.Components;
|
||||||
using Content.Server.EUI;
|
using Content.Server.EUI;
|
||||||
using Content.Server.Ghost.Roles;
|
using Content.Server.Ghost.Roles;
|
||||||
|
using Content.Server.Mind;
|
||||||
using Content.Server.Mind.Commands;
|
using Content.Server.Mind.Commands;
|
||||||
using Content.Server.Prayer;
|
using Content.Server.Prayer;
|
||||||
using Content.Server.Xenoarchaeology.XenoArtifacts;
|
using Content.Server.Xenoarchaeology.XenoArtifacts;
|
||||||
@@ -18,7 +19,6 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Mind;
|
|
||||||
using Content.Shared.Mind.Components;
|
using Content.Shared.Mind.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
@@ -56,7 +56,7 @@ namespace Content.Server.Administration.Systems
|
|||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
[Dependency] private readonly PrayerSystem _prayerSystem = default!;
|
[Dependency] private readonly PrayerSystem _prayerSystem = default!;
|
||||||
[Dependency] private readonly EuiManager _eui = 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 ToolshedManager _toolshed = default!;
|
||||||
[Dependency] private readonly RejuvenateSystem _rejuvenate = default!;
|
[Dependency] private readonly RejuvenateSystem _rejuvenate = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
@@ -277,12 +277,7 @@ namespace Content.Server.Administration.Systems
|
|||||||
// TODO VERB ICON control mob icon
|
// TODO VERB ICON control mob icon
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
MakeSentientCommand.MakeSentient(args.Target, EntityManager);
|
_mindSystem.ControlMob(args.User, args.Target);
|
||||||
|
|
||||||
if (!_minds.TryGetMind(player, out var mindId, out var mind))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_mindSystem.TransferTo(mindId, args.Target, ghostCheckOverride: true, mind: mind);
|
|
||||||
},
|
},
|
||||||
Impact = LogImpact.High,
|
Impact = LogImpact.High,
|
||||||
ConfirmationPopup = true
|
ConfirmationPopup = true
|
||||||
|
|||||||
@@ -360,8 +360,7 @@ namespace Content.Server.GameTicking
|
|||||||
else if (mind.CurrentEntity != null && TryName(mind.CurrentEntity.Value, out var icName))
|
else if (mind.CurrentEntity != null && TryName(mind.CurrentEntity.Value, out var icName))
|
||||||
playerIcName = icName;
|
playerIcName = icName;
|
||||||
|
|
||||||
var entity = mind.OriginalOwnedEntity;
|
if (TryGetEntity(mind.OriginalOwnedEntity, out var entity))
|
||||||
if (Exists(entity))
|
|
||||||
_pvsOverride.AddGlobalOverride(entity.Value, recursive: true);
|
_pvsOverride.AddGlobalOverride(entity.Value, recursive: true);
|
||||||
|
|
||||||
var roles = _roles.MindGetAllRoles(mindId);
|
var roles = _roles.MindGetAllRoles(mindId);
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public sealed class RenameCommand : IConsoleCommand
|
|||||||
{
|
{
|
||||||
// Mind
|
// Mind
|
||||||
mind.CharacterName = name;
|
mind.CharacterName = name;
|
||||||
|
_entManager.Dirty(mindId, mind);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Id Cards
|
// Id Cards
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Mind.Commands;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Ghost;
|
using Content.Shared.Ghost;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
@@ -46,20 +47,14 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
mind.UserId = null;
|
mind.UserId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(mind.OwnedEntity, out MetaDataComponent? meta) || meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
if (mind.OwnedEntity != null && !TerminatingOrDeleted(mind.OwnedEntity.Value))
|
||||||
return;
|
TransferTo(uid, null, mind: mind, createGhost: false);
|
||||||
|
|
||||||
RaiseLocalEvent(mind.OwnedEntity.Value, new MindRemovedMessage(uid, mind), true);
|
|
||||||
mind.OwnedEntity = null;
|
mind.OwnedEntity = null;
|
||||||
mind.OwnedComponent = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent component, ref EntityTerminatingEvent args)
|
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))
|
if (!TryGetMind(uid, out var mindId, out var mind, component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -77,6 +72,11 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
|
|
||||||
TransferTo(mindId, null, createGhost: false, mind: mind);
|
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)
|
if (component.GhostOnShutdown && mind.Session != null)
|
||||||
{
|
{
|
||||||
var xform = Transform(uid);
|
var xform = Transform(uid);
|
||||||
@@ -198,7 +198,7 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
if (mind.VisitingEntity == null)
|
if (mind.VisitingEntity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RemoveVisitingEntity(mind);
|
RemoveVisitingEntity(mindId, mind);
|
||||||
|
|
||||||
if (mind.Session == null || mind.Session.AttachedEntity == mind.VisitingEntity)
|
if (mind.Session == null || mind.Session.AttachedEntity == mind.VisitingEntity)
|
||||||
return;
|
return;
|
||||||
@@ -219,11 +219,10 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
if (mind == null && !Resolve(mindId, ref mind))
|
if (mind == null && !Resolve(mindId, ref mind))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
base.TransferTo(mindId, entity, ghostCheckOverride, createGhost, mind);
|
|
||||||
|
|
||||||
if (entity == mind.OwnedEntity)
|
if (entity == mind.OwnedEntity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Dirty(mindId, mind);
|
||||||
MindContainerComponent? component = null;
|
MindContainerComponent? component = null;
|
||||||
var alreadyAttached = false;
|
var alreadyAttached = false;
|
||||||
|
|
||||||
@@ -247,27 +246,33 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
}
|
}
|
||||||
else if (createGhost)
|
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)
|
var position = Deleted(mind.OwnedEntity)
|
||||||
? _gameTicker.GetObserverSpawnPoint().ToMap(EntityManager, _transform)
|
? _gameTicker.GetObserverSpawnPoint().ToMap(EntityManager, _transform)
|
||||||
: Transform(mind.OwnedEntity.Value).MapPosition;
|
: Transform(mind.OwnedEntity.Value).MapPosition;
|
||||||
|
|
||||||
entity = Spawn("MobObserver", position);
|
entity = Spawn("MobObserver", position);
|
||||||
|
component = EnsureComp<MindContainerComponent>(entity.Value);
|
||||||
var ghostComponent = Comp<GhostComponent>(entity.Value);
|
var ghostComponent = Comp<GhostComponent>(entity.Value);
|
||||||
_ghosts.SetCanReturnToBody(ghostComponent, false);
|
_ghosts.SetCanReturnToBody(ghostComponent, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var oldComp = mind.OwnedComponent;
|
|
||||||
var oldEntity = mind.OwnedEntity;
|
var oldEntity = mind.OwnedEntity;
|
||||||
if (oldComp != null && oldEntity != null)
|
if (TryComp(oldEntity, out MindContainerComponent? oldContainer))
|
||||||
{
|
{
|
||||||
if (oldComp.Mind != null)
|
oldContainer.Mind = null;
|
||||||
_pvsOverride.ClearOverride(oldComp.Mind.Value);
|
mind.OwnedEntity = null;
|
||||||
oldComp.Mind = null;
|
Entity<MindComponent> mindEnt = (mindId, mind);
|
||||||
RaiseLocalEvent(oldEntity.Value, new MindRemovedMessage(oldEntity.Value, mind), true);
|
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
|
// Don't do the full deletion cleanup if we're transferring to our VisitingEntity
|
||||||
if (alreadyAttached)
|
if (alreadyAttached)
|
||||||
{
|
{
|
||||||
@@ -281,7 +286,7 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
|| !TryComp(mind.VisitingEntity!, out GhostComponent? ghostComponent) // visiting entity is not a Ghost
|
|| !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
|
|| !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.
|
// Player is CURRENTLY connected.
|
||||||
@@ -292,11 +297,16 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
Log.Info($"Session {session.Name} transferred to entity {entity}.");
|
Log.Info($"Session {session.Name} transferred to entity {entity}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mind.OwnedComponent != null)
|
if (entity != null)
|
||||||
{
|
{
|
||||||
mind.OwnedComponent.Mind = mindId;
|
component!.Mind = mindId;
|
||||||
RaiseLocalEvent(mind.OwnedEntity!.Value, new MindAddedMessage(), true);
|
mind.OwnedEntity = entity;
|
||||||
mind.OriginalOwnedEntity ??= mind.OwnedEntity;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +323,7 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
if (mind.UserId == userId)
|
if (mind.UserId == userId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Dirty(mindId, mind);
|
||||||
_pvsOverride.ClearOverride(mindId);
|
_pvsOverride.ClearOverride(mindId);
|
||||||
if (userId != null && !_players.TryGetPlayerData(userId.Value, out _))
|
if (userId != null && !_players.TryGetPlayerData(userId.Value, out _))
|
||||||
{
|
{
|
||||||
@@ -363,4 +374,27 @@ public sealed class MindSystem : SharedMindSystem
|
|||||||
if (_players.GetPlayerData(userId.Value).ContentData() is { } data)
|
if (_players.GetPlayerData(userId.Value).ContentData() is { } data)
|
||||||
data.Mind = mindId;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
|||||||
if (component.AntagonistRole == null)
|
if (component.AntagonistRole == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_roles.MindTryRemoveRole<SubvertedSiliconRoleComponent>(args.OldMindId);
|
_roles.MindTryRemoveRole<SubvertedSiliconRoleComponent>(args.Mind);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnsureEmaggedRole(EntityUid uid, EmagSiliconLawComponent component)
|
private void EnsureEmaggedRole(EntityUid uid, EmagSiliconLawComponent component)
|
||||||
|
|||||||
@@ -1,24 +1,25 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Mind.Components
|
namespace Content.Shared.Mind.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores a <see cref="MindComponent"/> on a mob.
|
/// This component indicates that this entity may have mind, which is simply an entity with a <see cref="MindComponent"/>.
|
||||||
|
/// The mind entity is not actually stored in a "container", but is simply stored in nullspace.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, Access(typeof(SharedMindSystem))]
|
[RegisterComponent, Access(typeof(SharedMindSystem)), NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class MindContainerComponent : Component
|
public sealed partial class MindContainerComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mind controlling this mob. Can be null.
|
/// The mind controlling this mob. Can be null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[DataField, AutoNetworkedField]
|
||||||
[Access(typeof(SharedMindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
|
[Access(typeof(SharedMindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
|
||||||
public EntityUid? Mind { get; set; }
|
public EntityUid? Mind { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if we have a mind, false otherwise.
|
/// True if we have a mind, false otherwise.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
|
||||||
[MemberNotNullWhen(true, nameof(Mind))]
|
[MemberNotNullWhen(true, nameof(Mind))]
|
||||||
public bool HasMind => Mind != null;
|
public bool HasMind => Mind != null;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ namespace Content.Shared.Mind.Components
|
|||||||
/// Whether examining should show information about the mind or not.
|
/// Whether examining should show information about the mind or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("showExamineInfo")]
|
[DataField("showExamineInfo"), AutoNetworkedField]
|
||||||
public bool ShowExamineInfo { get; set; }
|
public bool ShowExamineInfo { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -38,19 +39,59 @@ namespace Content.Shared.Mind.Components
|
|||||||
public bool GhostOnShutdown { get; set; } = true;
|
public bool GhostOnShutdown { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class MindRemovedMessage : EntityEventArgs
|
public abstract class MindEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public EntityUid OldMindId;
|
public readonly Entity<MindComponent> Mind;
|
||||||
public MindComponent OldMind;
|
public readonly Entity<MindContainerComponent> Container;
|
||||||
|
|
||||||
public MindRemovedMessage(EntityUid oldMindId, MindComponent oldMind)
|
public MindEvent(Entity<MindComponent> mind, Entity<MindContainerComponent> container)
|
||||||
{
|
{
|
||||||
OldMindId = oldMindId;
|
Mind = mind;
|
||||||
OldMind = oldMind;
|
Container = container;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class MindAddedMessage : EntityEventArgs
|
/// <summary>
|
||||||
|
/// Event raised directed at a mind-container when a mind gets removed.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MindRemovedMessage : MindEvent
|
||||||
|
{
|
||||||
|
public MindRemovedMessage(Entity<MindComponent> mind, Entity<MindContainerComponent> container)
|
||||||
|
: base(mind, container)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised directed at a mind when it gets removed from a mind-container.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MindGotRemovedEvent : MindEvent
|
||||||
|
{
|
||||||
|
public MindGotRemovedEvent(Entity<MindComponent> mind, Entity<MindContainerComponent> container)
|
||||||
|
: base(mind, container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised directed at a mind-container when a mind gets added.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MindAddedMessage : MindEvent
|
||||||
|
{
|
||||||
|
public MindAddedMessage(Entity<MindComponent> mind, Entity<MindContainerComponent> container)
|
||||||
|
: base(mind, container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event raised directed at a mind when it gets added to a mind-container.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MindGotAddedEvent : MindEvent
|
||||||
|
{
|
||||||
|
public MindGotAddedEvent(Entity<MindComponent> mind, Entity<MindContainerComponent> container)
|
||||||
|
: base(mind, container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,84 +1,86 @@
|
|||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.Mind.Components;
|
using Content.Shared.Mind.Components;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Players;
|
||||||
|
|
||||||
namespace Content.Shared.Mind
|
namespace Content.Shared.Mind
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is added as a component to mind entities, not to player entities.
|
/// This component stores information about a player/mob mind. The component will be attached to a mind-entity
|
||||||
/// <see cref="MindContainerComponent"/> for the one that is added to players.
|
/// which is stored in null-space. The entity that is currently "possessed" by the mind will have a
|
||||||
/// A mind represents the IC "mind" of a player.
|
/// <see cref="MindContainerComponent"/>.
|
||||||
/// Roles are attached as components to its owning entity.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
/// Roles are attached as components on the mind-entity entity.
|
||||||
/// Think of it like this: if a player is supposed to have their memories,
|
/// Think of it like this: if a player is supposed to have their memories,
|
||||||
/// their mind follows along.
|
/// their mind follows along.
|
||||||
///
|
///
|
||||||
/// Things such as respawning do not follow, because you're a new character.
|
/// 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.
|
/// Getting borged, cloned, turned into a catbeast, etc... will keep it following you.
|
||||||
|
///
|
||||||
|
/// Minds are stored in null-space, and are thus generally not set to players unless that player is the owner
|
||||||
|
/// of the mind. As a result it should be safe to network "secret" information like roles & objectives
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||||
public sealed partial class MindComponent : Component
|
public sealed partial class MindComponent : Component
|
||||||
{
|
{
|
||||||
internal readonly List<EntityUid> Objectives = new();
|
[DataField, AutoNetworkedField]
|
||||||
|
public List<EntityUid> Objectives = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session ID of the player owning this mind.
|
/// The session ID of the player owning this mind.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, Access(typeof(SharedMindSystem))]
|
[DataField, AutoNetworkedField, Access(typeof(SharedMindSystem))]
|
||||||
public NetUserId? UserId { get; set; }
|
public NetUserId? UserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The session ID of the original owner, if any.
|
/// The session ID of the original owner, if any.
|
||||||
/// May end up used for round-end information (as the owner may have abandoned Mind since)
|
/// May end up used for round-end information (as the owner may have abandoned Mind since)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, Access(typeof(SharedMindSystem))]
|
[DataField, AutoNetworkedField, Access(typeof(SharedMindSystem))]
|
||||||
public NetUserId? OriginalOwnerUserId { get; set; }
|
public NetUserId? OriginalOwnerUserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Entity UID for the first entity that this mind controlled. Used for round end.
|
/// The first entity that this mind controlled. Used for round end information.
|
||||||
/// Might be relevant if the player has ghosted since.
|
/// Might be relevant if the player has ghosted since.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables] public EntityUid? OriginalOwnedEntity;
|
[DataField, AutoNetworkedField]
|
||||||
|
public NetEntity? OriginalOwnedEntity;
|
||||||
|
// This is a net entity, because this field currently ddoes not get set to null when this entity is deleted.
|
||||||
|
// This is a lazy way to ensure that people check that the entity still exists.
|
||||||
|
// TODO MIND Fix this properly by adding an OriginalMindContainerComponent or something like that.
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public bool IsVisitingEntity => VisitingEntity != null;
|
public bool IsVisitingEntity => VisitingEntity != null;
|
||||||
|
|
||||||
[ViewVariables, Access(typeof(SharedMindSystem))]
|
[DataField, AutoNetworkedField, Access(typeof(SharedMindSystem))]
|
||||||
public EntityUid? VisitingEntity { get; set; }
|
public EntityUid? VisitingEntity { get; set; }
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public EntityUid? CurrentEntity => VisitingEntity ?? OwnedEntity;
|
public EntityUid? CurrentEntity => VisitingEntity ?? OwnedEntity;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public string? CharacterName { get; set; }
|
public string? CharacterName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time of death for this Mind.
|
/// The time of death for this Mind.
|
||||||
/// Can be null - will be null if the Mind is not considered "dead".
|
/// Can be null - will be null if the Mind is not considered "dead".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[DataField]
|
||||||
public TimeSpan? TimeOfDeath { get; set; }
|
public TimeSpan? TimeOfDeath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The component currently owned by this mind.
|
|
||||||
/// Can be null.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables] public MindContainerComponent? OwnedComponent;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity currently owned by this mind.
|
/// The entity currently owned by this mind.
|
||||||
/// Can be null.
|
/// Can be null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, Access(typeof(SharedMindSystem))]
|
[DataField, AutoNetworkedField, Access(typeof(SharedMindSystem))]
|
||||||
public EntityUid? OwnedEntity { get; set; }
|
public EntityUid? OwnedEntity { get; set; }
|
||||||
|
|
||||||
// TODO move objectives out of mind component
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An enumerable over all the objective entities this mind has.
|
/// An enumerable over all the objective entities this mind has.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables, Obsolete("Use Objectives field")]
|
||||||
public IEnumerable<EntityUid> AllObjectives => Objectives;
|
public IEnumerable<EntityUid> AllObjectives => Objectives;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,6 +102,7 @@ namespace Content.Shared.Mind
|
|||||||
/// Can be null, in which case the player is currently not logged in.
|
/// Can be null, in which case the player is currently not logged in.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, Access(typeof(SharedMindSystem), typeof(SharedGameTicker))]
|
[ViewVariables, Access(typeof(SharedMindSystem), typeof(SharedGameTicker))]
|
||||||
|
// TODO remove this after moving IPlayerManager functions to shared
|
||||||
public ICommonSession? Session { get; set; }
|
public ICommonSession? Session { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
@@ -25,7 +26,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedPlayerSystem _player = default!;
|
[Dependency] private readonly SharedPlayerSystem _player = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
[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.
|
[ViewVariables]
|
||||||
protected readonly Dictionary<NetUserId, EntityUid> UserMinds = new();
|
protected readonly Dictionary<NetUserId, EntityUid> UserMinds = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -36,6 +37,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<MindContainerComponent, SuicideEvent>(OnSuicide);
|
SubscribeLocalEvent<MindContainerComponent, SuicideEvent>(OnSuicide);
|
||||||
SubscribeLocalEvent<VisitingMindComponent, EntityTerminatingEvent>(OnVisitingTerminating);
|
SubscribeLocalEvent<VisitingMindComponent, EntityTerminatingEvent>(OnVisitingTerminating);
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnReset);
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnReset);
|
||||||
|
SubscribeLocalEvent<MindComponent, ComponentStartup>(OnMindStartup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
@@ -44,6 +46,29 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
WipeAllMinds();
|
WipeAllMinds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnMindStartup(EntityUid uid, MindComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (component.UserId == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (UserMinds.TryAdd(component.UserId.Value, uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var existing = UserMinds[component.UserId.Value];
|
||||||
|
if (existing == uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Exists(existing))
|
||||||
|
{
|
||||||
|
Log.Error($"Found deleted entity in mind dictionary while initializing mind {ToPrettyString(uid)}");
|
||||||
|
UserMinds[component.UserId.Value] = uid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Error($"Encountered a user {component.UserId} that is already assigned to a mind while initializing mind {ToPrettyString(uid)}. Ignoring user field.");
|
||||||
|
component.UserId = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnReset(RoundRestartCleanupEvent ev)
|
private void OnReset(RoundRestartCleanupEvent ev)
|
||||||
{
|
{
|
||||||
WipeAllMinds();
|
WipeAllMinds();
|
||||||
@@ -51,12 +76,22 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
|
|
||||||
public virtual void WipeAllMinds()
|
public virtual void WipeAllMinds()
|
||||||
{
|
{
|
||||||
foreach (var mind in UserMinds.Values)
|
Log.Info($"Wiping all minds");
|
||||||
|
foreach (var mind in UserMinds.Values.ToArray())
|
||||||
{
|
{
|
||||||
WipeMind(mind);
|
WipeMind(mind);
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugTools.Assert(UserMinds.Count == 0);
|
if (UserMinds.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var mind in UserMinds.Values)
|
||||||
|
{
|
||||||
|
if (Exists(mind))
|
||||||
|
Log.Error($"Failed to wipe mind: {ToPrettyString(mind)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserMinds.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityUid? GetMind(NetUserId user)
|
public EntityUid? GetMind(NetUserId user)
|
||||||
@@ -80,6 +115,26 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryGetMind(NetUserId user, [NotNullWhen(true)] out Entity<MindComponent>? mind)
|
||||||
|
{
|
||||||
|
if (!TryGetMind(user, out var mindId, out var mindComp))
|
||||||
|
{
|
||||||
|
mind = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mind = (mindId.Value, mindComp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity<MindComponent> GetOrCreateMind(NetUserId user)
|
||||||
|
{
|
||||||
|
if (!TryGetMind(user, out var mind))
|
||||||
|
mind = CreateMind(user);
|
||||||
|
|
||||||
|
return mind.Value;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args)
|
private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args)
|
||||||
{
|
{
|
||||||
if (component.MindId != null)
|
if (component.MindId != null)
|
||||||
@@ -128,7 +183,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityUid CreateMind(NetUserId? userId, string? name = null)
|
public Entity<MindComponent> CreateMind(NetUserId? userId, string? name = null)
|
||||||
{
|
{
|
||||||
var mindId = Spawn(null, MapCoordinates.Nullspace);
|
var mindId = Spawn(null, MapCoordinates.Nullspace);
|
||||||
_metadata.SetEntityName(mindId, name == null ? "mind" : $"mind ({name})");
|
_metadata.SetEntityName(mindId, name == null ? "mind" : $"mind ({name})");
|
||||||
@@ -136,7 +191,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
mind.CharacterName = name;
|
mind.CharacterName = name;
|
||||||
SetUserId(mindId, userId, mind);
|
SetUserId(mindId, userId, mind);
|
||||||
|
|
||||||
return mindId;
|
return (mindId, mind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -195,7 +250,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
/// Cleans up the VisitingEntity.
|
/// Cleans up the VisitingEntity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mind"></param>
|
/// <param name="mind"></param>
|
||||||
protected void RemoveVisitingEntity(MindComponent mind)
|
protected void RemoveVisitingEntity(EntityUid mindId, MindComponent mind)
|
||||||
{
|
{
|
||||||
if (mind.VisitingEntity == null)
|
if (mind.VisitingEntity == null)
|
||||||
return;
|
return;
|
||||||
@@ -210,6 +265,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
RemCompDeferred(oldVisitingEnt, visitComp);
|
RemCompDeferred(oldVisitingEnt, visitComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dirty(mindId, mind);
|
||||||
RaiseLocalEvent(oldVisitingEnt, new MindUnvisitedMessage(), true);
|
RaiseLocalEvent(oldVisitingEnt, new MindUnvisitedMessage(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +284,7 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
if (mindId == null || !Resolve(mindId.Value, ref mind, false))
|
if (mindId == null || !Resolve(mindId.Value, ref mind, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TransferTo(mindId.Value, null, mind: mind);
|
TransferTo(mindId.Value, null, createGhost:false, mind: mind);
|
||||||
SetUserId(mindId.Value, null, mind: mind);
|
SetUserId(mindId.Value, null, mind: mind);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,21 +447,6 @@ public abstract class SharedMindSystem : EntitySystem
|
|||||||
return TryComp(mindContainer.Mind, out role);
|
return TryComp(mindContainer.Mind, out role);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the Mind's OwnedComponent and OwnedEntity
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mind">Mind to set OwnedComponent and OwnedEntity on</param>
|
|
||||||
/// <param name="uid">Entity owned by <paramref name="mind"/></param>
|
|
||||||
/// <param name="mindContainerComponent">MindContainerComponent owned by <paramref name="mind"/></param>
|
|
||||||
protected void SetOwnedEntity(MindComponent mind, EntityUid? uid, MindContainerComponent? mindContainerComponent)
|
|
||||||
{
|
|
||||||
if (uid != null)
|
|
||||||
Resolve(uid.Value, ref mindContainerComponent);
|
|
||||||
|
|
||||||
mind.OwnedEntity = uid;
|
|
||||||
mind.OwnedComponent = mindContainerComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the Mind's UserId, Session, and updates the player's PlayerData. This should have no direct effect on the
|
/// Sets the Mind's UserId, Session, and updates the player's PlayerData. This should have no direct effect on the
|
||||||
/// entity that any mind is connected to, except as a side effect of the fact that it may change a player's
|
/// entity that any mind is connected to, except as a side effect of the fact that it may change a player's
|
||||||
|
|||||||
Reference in New Issue
Block a user