ECSify IdCardConsole (#13878)

This commit is contained in:
Visne
2023-02-05 21:02:28 +01:00
committed by GitHub
parent e7f3e75230
commit 174482bb45
2 changed files with 188 additions and 207 deletions

View File

@@ -1,200 +1,11 @@
using System.Linq;
using Content.Server.Access.Systems;
using Content.Server.Station.Systems;
using Content.Server.StationRecords;
using Content.Server.UserInterface;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.StationRecords;
using Content.Server.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Roles;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server.Access.Components
namespace Content.Server.Access.Components;
[RegisterComponent]
[ComponentReference(typeof(SharedIdCardConsoleComponent))]
[Access(typeof(IdCardConsoleSystem))]
public sealed class IdCardConsoleComponent : SharedIdCardConsoleComponent
{
[RegisterComponent]
[ComponentReference(typeof(SharedIdCardConsoleComponent))]
public sealed class IdCardConsoleComponent : SharedIdCardConsoleComponent
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(IdCardConsoleUiKey.Key);
private StationRecordsSystem? _recordSystem;
private StationSystem? _stationSystem;
protected override void Initialize()
{
base.Initialize();
Owner.EnsureComponentWarn<AccessReaderComponent>();
Owner.EnsureComponentWarn<ServerUserInterfaceComponent>();
_stationSystem = _entities.EntitySysManager.GetEntitySystem<StationSystem>();
_recordSystem = _entities.EntitySysManager.GetEntitySystem<StationRecordsSystem>();
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
}
}
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
{
if (obj.Session.AttachedEntity is not {Valid: true} player)
{
return;
}
switch (obj.Message)
{
case WriteToTargetIdMessage msg:
TryWriteToTargetId(msg.FullName, msg.JobTitle, msg.AccessList, msg.JobPrototype, player);
UpdateUserInterface();
break;
}
}
/// <summary>
/// Returns true if there is an ID in <see cref="PrivilegedIdSlot"/> and said ID satisfies the requirements of <see cref="AccessReaderComponent"/>.
/// </summary>
private bool PrivilegedIdIsAuthorized()
{
if (!_entities.TryGetComponent(Owner, out AccessReaderComponent? reader))
{
return true;
}
var privilegedIdEntity = PrivilegedIdSlot.Item;
var accessSystem = _entities.EntitySysManager.GetEntitySystem<AccessReaderSystem>();
return privilegedIdEntity != null && accessSystem.IsAllowed(privilegedIdEntity.Value, reader);
}
/// <summary>
/// Called whenever an access button is pressed, adding or removing that access from the target ID card.
/// Writes data passed from the UI into the ID stored in <see cref="TargetIdSlot"/>, if present.
/// </summary>
private void TryWriteToTargetId(string newFullName, string newJobTitle, List<string> newAccessList, string newJobProto, EntityUid player)
{
if (TargetIdSlot.Item is not {Valid: true} targetIdEntity || !PrivilegedIdIsAuthorized())
return;
var cardSystem = _entities.EntitySysManager.GetEntitySystem<IdCardSystem>();
cardSystem.TryChangeFullName(targetIdEntity, newFullName, player: player);
cardSystem.TryChangeJobTitle(targetIdEntity, newJobTitle, player: player);
if (!newAccessList.TrueForAll(x => AccessLevels.Contains(x)))
{
Logger.Warning("Tried to write unknown access tag.");
return;
}
var accessSystem = _entities.EntitySysManager.GetEntitySystem<AccessSystem>();
var oldTags = accessSystem.TryGetTags(targetIdEntity) ?? new List<string>();
oldTags = oldTags.ToList();
if (oldTags.SequenceEqual(newAccessList))
return;
var addedTags = newAccessList.Except(oldTags).Select(tag => "+" + tag).ToList();
var removedTags = oldTags.Except(newAccessList).Select(tag => "-" + tag).ToList();
accessSystem.TrySetTags(targetIdEntity, newAccessList);
/*TODO: ECS IdCardConsoleComponent and then log on card ejection, together with the save.
This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{_entities.ToPrettyString(player):player} has modified {_entities.ToPrettyString(targetIdEntity):entity} with the following accesses: [{String.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
UpdateStationRecord(targetIdEntity, newFullName, newJobTitle, newJobProto);
}
private void UpdateStationRecord(EntityUid idCard, string newFullName, string newJobTitle, string newJobProto)
{
var station = _stationSystem?.GetOwningStation(Owner);
if (station == null
|| _recordSystem == null
|| !_entities.TryGetComponent(idCard, out StationRecordKeyStorageComponent? keyStorage)
|| keyStorage.Key == null
|| !_recordSystem.TryGetRecord(station.Value, keyStorage.Key.Value, out GeneralStationRecord? record))
{
return;
}
record.Name = newFullName;
record.JobTitle = newJobTitle;
if (_prototypeManager.TryIndex(newJobProto, out JobPrototype? job))
{
record.JobPrototype = newJobProto;
record.JobIcon = job.Icon;
}
_recordSystem.Synchronize(station.Value);
}
public void UpdateUserInterface()
{
if (!Initialized)
return;
IdCardConsoleBoundUserInterfaceState newState;
// this could be prettier
if (TargetIdSlot.Item is not {Valid: true} targetIdEntity)
{
var privilegedIdName = string.Empty;
if (PrivilegedIdSlot.Item is {Valid: true} item)
{
privilegedIdName = _entities.GetComponent<MetaDataComponent>(item).EntityName;
}
newState = new IdCardConsoleBoundUserInterfaceState(
PrivilegedIdSlot.HasItem,
PrivilegedIdIsAuthorized(),
false,
null,
null,
null,
string.Empty,
privilegedIdName,
string.Empty);
}
else
{
var targetIdComponent = _entities.GetComponent<IdCardComponent>(targetIdEntity);
var targetAccessComponent = _entities.GetComponent<AccessComponent>(targetIdEntity);
var name = string.Empty;
if (PrivilegedIdSlot.Item is {Valid: true} item)
name = _entities.GetComponent<MetaDataComponent>(item).EntityName;
var station = _stationSystem?.GetOwningStation(Owner);
var jobProto = string.Empty;
if (_recordSystem != null
&& station != null
&& _entities.TryGetComponent(targetIdEntity, out StationRecordKeyStorageComponent? keyStorage)
&& keyStorage.Key != null
&& _recordSystem.TryGetRecord(station.Value, keyStorage.Key.Value,
out GeneralStationRecord? record))
{
jobProto = record.JobPrototype;
}
newState = new IdCardConsoleBoundUserInterfaceState(
PrivilegedIdSlot.HasItem,
PrivilegedIdIsAuthorized(),
true,
targetIdComponent.FullName,
targetIdComponent.JobTitle,
targetAccessComponent.Tags.ToArray(),
jobProto,
name,
_entities.GetComponent<MetaDataComponent>(targetIdEntity).EntityName);
}
UserInterface?.SetState(newState);
}
}
}

View File

@@ -1,21 +1,191 @@
using Content.Server.Access.Components;
using System.Linq;
using Content.Server.Station.Systems;
using Content.Server.StationRecords;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Roles;
using Content.Shared.StationRecords;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.SharedIdCardConsoleComponent;
namespace Content.Server.Access.Systems
namespace Content.Server.Access.Systems;
[UsedImplicitly]
public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
{
[UsedImplicitly]
public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
{
public override void Initialize()
{
base.Initialize();
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly StationRecordsSystem _record = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
[Dependency] private readonly AccessSystem _access = default!;
[Dependency] private readonly IdCardSystem _idCard = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
// one day, maybe bound user interfaces can be shared too.
SubscribeLocalEvent<IdCardConsoleComponent, ComponentStartup>((_, comp, _) => comp.UpdateUserInterface());
SubscribeLocalEvent<IdCardConsoleComponent, EntInsertedIntoContainerMessage>((_, comp, _) => comp.UpdateUserInterface());
SubscribeLocalEvent<IdCardConsoleComponent, EntRemovedFromContainerMessage>((_, comp, _) => comp.UpdateUserInterface());
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedIdCardConsoleComponent, WriteToTargetIdMessage>(OnWriteToTargetIdMessage);
// one day, maybe bound user interfaces can be shared too.
SubscribeLocalEvent<SharedIdCardConsoleComponent, ComponentStartup>(UpdateUserInterface);
SubscribeLocalEvent<SharedIdCardConsoleComponent, EntInsertedIntoContainerMessage>(UpdateUserInterface);
SubscribeLocalEvent<SharedIdCardConsoleComponent, EntRemovedFromContainerMessage>(UpdateUserInterface);
}
private void OnWriteToTargetIdMessage(EntityUid uid, SharedIdCardConsoleComponent component, WriteToTargetIdMessage args)
{
if (args.Session.AttachedEntity is not { Valid: true } player)
return;
TryWriteToTargetId(uid, args.FullName, args.JobTitle, args.AccessList, args.JobPrototype, player, component);
UpdateUserInterface(uid, component, args);
}
private void UpdateUserInterface(EntityUid uid, SharedIdCardConsoleComponent component, EntityEventArgs args)
{
if (!component.Initialized)
return;
IdCardConsoleBoundUserInterfaceState newState;
// this could be prettier
if (component.TargetIdSlot.Item is not { Valid: true } targetId)
{
var privilegedIdName = string.Empty;
if (component.PrivilegedIdSlot.Item is { Valid: true } item)
{
privilegedIdName = EntityManager.GetComponent<MetaDataComponent>(item).EntityName;
}
newState = new IdCardConsoleBoundUserInterfaceState(
component.PrivilegedIdSlot.HasItem,
PrivilegedIdIsAuthorized(uid, component),
false,
null,
null,
null,
string.Empty,
privilegedIdName,
string.Empty);
}
else
{
var targetIdComponent = EntityManager.GetComponent<IdCardComponent>(targetId);
var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId);
var name = string.Empty;
if (component.PrivilegedIdSlot.Item is { Valid: true } item)
name = EntityManager.GetComponent<MetaDataComponent>(item).EntityName;
var jobProto = string.Empty;
if (_station.GetOwningStation(uid) is { } station
&& EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
&& keyStorage.Key != null
&& _record.TryGetRecord<GeneralStationRecord>(station, keyStorage.Key.Value, out var record))
{
jobProto = record.JobPrototype;
}
newState = new IdCardConsoleBoundUserInterfaceState(
component.PrivilegedIdSlot.HasItem,
PrivilegedIdIsAuthorized(uid, component),
true,
targetIdComponent.FullName,
targetIdComponent.JobTitle,
targetAccessComponent.Tags.ToArray(),
jobProto,
name,
EntityManager.GetComponent<MetaDataComponent>(targetId).EntityName);
}
_userInterface.TrySetUiState(uid, IdCardConsoleUiKey.Key, newState);
}
/// <summary>
/// Called whenever an access button is pressed, adding or removing that access from the target ID card.
/// Writes data passed from the UI into the ID stored in <see cref="SharedIdCardConsoleComponent.TargetIdSlot"/>, if present.
/// </summary>
private void TryWriteToTargetId(EntityUid uid,
string newFullName,
string newJobTitle,
List<string> newAccessList,
string newJobProto,
EntityUid player,
SharedIdCardConsoleComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (component.TargetIdSlot.Item is not { Valid: true } targetId || !PrivilegedIdIsAuthorized(uid, component))
return;
_idCard.TryChangeFullName(targetId, newFullName, player: player);
_idCard.TryChangeJobTitle(targetId, newJobTitle, player: player);
if (!newAccessList.TrueForAll(x => component.AccessLevels.Contains(x)))
{
Logger.Warning("Tried to write unknown access tag.");
return;
}
var oldTags = _access.TryGetTags(targetId) ?? new List<string>();
oldTags = oldTags.ToList();
if (oldTags.SequenceEqual(newAccessList))
return;
var addedTags = newAccessList.Except(oldTags).Select(tag => "+" + tag).ToList();
var removedTags = oldTags.Except(newAccessList).Select(tag => "-" + tag).ToList();
_access.TrySetTags(targetId, newAccessList);
/*TODO: ECS SharedIdCardConsoleComponent and then log on card ejection, together with the save.
This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
UpdateStationRecord(uid, targetId, newFullName, newJobTitle, newJobProto);
}
/// <summary>
/// Returns true if there is an ID in <see cref="SharedIdCardConsoleComponent.PrivilegedIdSlot"/> and said ID satisfies the requirements of <see cref="AccessReaderComponent"/>.
/// </summary>
private bool PrivilegedIdIsAuthorized(EntityUid uid, SharedIdCardConsoleComponent? component = null)
{
if (!Resolve(uid, ref component))
return true;
if (!EntityManager.TryGetComponent<AccessReaderComponent>(uid, out var reader))
return true;
var privilegedId = component.PrivilegedIdSlot.Item;
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, reader);
}
private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, string newJobProto)
{
if (_station.GetOwningStation(uid) is not { } station
|| !EntityManager.TryGetComponent<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|| keyStorage.Key is not { } key
|| !_record.TryGetRecord<GeneralStationRecord>(station, key, out var record))
{
return;
}
record.Name = newFullName;
record.JobTitle = newJobTitle;
if (_prototype.TryIndex<JobPrototype>(newJobProto, out var job))
{
record.JobPrototype = newJobProto;
record.JobIcon = job.Icon;
}
_record.Synchronize(station);
}
}