Id[entity] 2.0 (real) (#9612)

* starter API

* network ID cards

* Port more stuff from old identity

* Re-implement identity representation + name updating

* move

* proper name returning for `IdentityName`

* move everything important to server, give in to  temptation

* shared / server / client split sadly. move ensure to shared and spawn to server

* identity update queueing + identityblocker

* fixes

* and just like that it's usable for admins

* huge identity pass

* pass dos

* jesus christ

* figs :D

* fuck u

* fix bad merge.

Co-authored-by: Moony <moonheart08@users.noreply.github.com>
This commit is contained in:
Kara
2022-07-10 18:36:53 -07:00
committed by GitHub
parent fb6586cdc6
commit 2d5ec7f85c
68 changed files with 668 additions and 188 deletions

View File

@@ -0,0 +1,18 @@
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
namespace Content.Shared.IdentityManagement.Components;
[RegisterComponent, NetworkedComponent]
public sealed class IdentityBlockerComponent : Component
{
}
/// <summary>
/// Raised on an entity and relayed to inventory to determine if its identity should be knowable.
/// </summary>
public sealed class SeeIdentityAttemptEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
// i.e. masks or helmets.
public SlotFlags TargetSlots => SlotFlags.MASK | SlotFlags.HEAD;
}

View File

@@ -0,0 +1,76 @@
using Robust.Shared.Containers;
using Robust.Shared.Enums;
namespace Content.Shared.IdentityManagement.Components;
/// <summary>
/// Stores the identity entity (whose name is the users 'identity', etc)
/// for a given entity, and marks that it can have an identity at all.
/// </summary>
/// <remarks>
/// This is a <see cref="ContainerSlot"/> and not just a datum entity because we do sort of care that it gets deleted and sent with the user.
/// </remarks>
[RegisterComponent]
public sealed class IdentityComponent : Component
{
[ViewVariables]
public ContainerSlot IdentityEntitySlot = default!;
}
/// <summary>
/// A data structure representing the 'identity' of an entity as presented to
/// other players.
/// </summary>
public sealed class IdentityRepresentation
{
public string TrueName;
public int TrueAge;
public Gender TrueGender;
public string? PresumedName;
public string? PresumedJob;
public IdentityRepresentation(string trueName, int trueAge, Gender trueGender, string? presumedName=null, string? presumedJob=null)
{
TrueName = trueName;
TrueAge = trueAge;
TrueGender = trueGender;
PresumedJob = presumedJob;
PresumedName = presumedName;
}
public string ToStringKnown(bool trueName)
{
return trueName
? TrueName
: PresumedName ?? ToStringUnknown();
}
/// <summary>
/// Returns a string representing their identity where it is 'unknown' by a viewer.
/// Used for cases where the viewer is not necessarily able to accurately assess
/// the identity of the person being viewed.
/// </summary>
public string ToStringUnknown()
{
var ageString = TrueAge switch
{
<= 30 => Loc.GetString("identity-age-young"),
> 30 and <= 60 => Loc.GetString("identity-age-middle-aged"),
> 60 => Loc.GetString("identity-age-old")
};
var genderString = TrueGender switch
{
Gender.Female => Loc.GetString("identity-gender-feminine"),
Gender.Male => Loc.GetString("identity-gender-masculine"),
Gender.Epicene or Gender.Neuter or _ => Loc.GetString("identity-gender-person")
};
// i.e. 'young assistant man' or 'old cargo technician person' or 'middle-aged captain'
return PresumedJob is null
? $"{ageString} {genderString}"
: $"{ageString} {PresumedJob} {genderString}";
}
}

View File

@@ -0,0 +1,60 @@
using Content.Shared.Ghost;
using Content.Shared.IdentityManagement.Components;
namespace Content.Shared.IdentityManagement;
/// <summary>
/// Static content API for getting the identity entities/names for a given entity.
/// This should almost always be used in favor of metadata name, if the entity in question is a human player that
/// can have identity.
/// </summary>
public static class Identity
{
/// <summary>
/// Returns the name that should be used for this entity for identity purposes.
/// </summary>
public static string Name(EntityUid uid, IEntityManager ent, EntityUid? viewer=null)
{
var uidName = ent.GetComponent<MetaDataComponent>(uid).EntityName;
if (!ent.TryGetComponent<IdentityComponent>(uid, out var identity))
return uidName;
var ident = identity.IdentityEntitySlot.ContainedEntity;
if (ident is null)
return uidName;
var identName = ent.GetComponent<MetaDataComponent>(ident.Value).EntityName;
if (viewer == null || !CanSeeThroughIdentity(uid, viewer.Value, ent))
{
return identName;
}
if (uidName == identName)
{
return uidName;
}
return uidName + $" ({identName})";
}
/// <summary>
/// Returns the entity that should be used for identity purposes, for example to pass into localization.
/// This is an extension method because of its simplicity, and if it was any harder to call it might not
/// be used enough for loc.
/// </summary>
public static EntityUid Entity(EntityUid uid, IEntityManager ent)
{
if (!ent.TryGetComponent<IdentityComponent>(uid, out var identity))
return uid;
return identity.IdentityEntitySlot.ContainedEntity ?? uid;
}
public static bool CanSeeThroughIdentity(EntityUid uid, EntityUid viewer, IEntityManager ent)
{
// Would check for uid == viewer here but I think it's better for you to see yourself
// how everyone else will see you, otherwise people will probably get confused and think they aren't disguised
return ent.HasComponent<SharedGhostComponent>(viewer);
}
}

View File

@@ -0,0 +1,28 @@
using Content.Shared.IdentityManagement.Components;
using Robust.Shared.Containers;
namespace Content.Shared.IdentityManagement;
public abstract class SharedIdentitySystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
private static string SlotName = "identity";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
}
private void OnSeeIdentity(EntityUid uid, IdentityBlockerComponent component, SeeIdentityAttemptEvent args)
{
args.Cancel();
}
protected virtual void OnComponentInit(EntityUid uid, IdentityComponent component, ComponentInit args)
{
component.IdentityEntitySlot = _container.EnsureContainer<ContainerSlot>(uid, SlotName);
}
}