Unique name identifiers (#6697)
This commit is contained in:
@@ -19,6 +19,7 @@ namespace Content.Client.Entry
|
|||||||
"WarpPoint",
|
"WarpPoint",
|
||||||
"EmitSoundOnUse",
|
"EmitSoundOnUse",
|
||||||
"EmitSoundOnLand",
|
"EmitSoundOnLand",
|
||||||
|
"NameIdentifier",
|
||||||
"EmitSoundOnActivate",
|
"EmitSoundOnActivate",
|
||||||
"FootstepModifier",
|
"FootstepModifier",
|
||||||
"HeatResistance",
|
"HeatResistance",
|
||||||
|
|||||||
11
Content.Server/NameIdentifier/NameIdentifierComponent.cs
Normal file
11
Content.Server/NameIdentifier/NameIdentifierComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Content.Shared.NameIdentifier;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
|
namespace Content.Server.NameIdentifier;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class NameIdentifierComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("group", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<NameIdentifierGroupPrototype>))]
|
||||||
|
public string Group = string.Empty;
|
||||||
|
}
|
||||||
118
Content.Server/NameIdentifier/NameIdentifierSystem.cs
Normal file
118
Content.Server/NameIdentifier/NameIdentifierSystem.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.NameIdentifier;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.NameIdentifier;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles unique name identifiers for entities e.g. `monkey (MK-912)`
|
||||||
|
/// </summary>
|
||||||
|
public sealed class NameIdentifierSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<NameIdentifierGroupPrototype, HashSet<int>> CurrentIds = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<NameIdentifierComponent, ComponentInit>(OnComponentInit);
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(CleanupIds);
|
||||||
|
|
||||||
|
InitialSetupPrototypes();
|
||||||
|
_prototypeManager.PrototypesReloaded += OnReloadPrototypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
base.Shutdown();
|
||||||
|
|
||||||
|
_prototypeManager.PrototypesReloaded -= OnReloadPrototypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/>
|
||||||
|
/// but does not set the entity's name.
|
||||||
|
/// </summary>
|
||||||
|
public string GenerateUniqueName(EntityUid uid, NameIdentifierGroupPrototype proto)
|
||||||
|
{
|
||||||
|
var entityName = Name(uid);
|
||||||
|
if (!CurrentIds.TryGetValue(proto, out var set))
|
||||||
|
return entityName;
|
||||||
|
|
||||||
|
if (set.Count == (proto.MaxValue - proto.MinValue) + 1)
|
||||||
|
{
|
||||||
|
// Oh jeez. We're outta numbers.
|
||||||
|
return entityName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is kind of inefficient with very large amounts of entities but its better than any other method
|
||||||
|
// I could come up with.
|
||||||
|
|
||||||
|
var randomVal = _robustRandom.Next(proto.MinValue, proto.MaxValue);
|
||||||
|
while (set.Contains(randomVal))
|
||||||
|
{
|
||||||
|
randomVal = _robustRandom.Next(proto.MinValue, proto.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
set.Add(randomVal);
|
||||||
|
|
||||||
|
return proto.Prefix is not null
|
||||||
|
? $"{proto.Prefix}-{randomVal}"
|
||||||
|
: $"{randomVal}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnComponentInit(EntityUid uid, NameIdentifierComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex<NameIdentifierGroupPrototype>(component.Group, out var group))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Generate a new name.
|
||||||
|
var meta = MetaData(uid);
|
||||||
|
var uniqueName = GenerateUniqueName(uid, group);
|
||||||
|
|
||||||
|
// "DR-1234" as opposed to "drone (DR-1234)"
|
||||||
|
meta.EntityName = group.FullName
|
||||||
|
? uniqueName
|
||||||
|
: $"{meta.EntityName} ({uniqueName})";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitialSetupPrototypes()
|
||||||
|
{
|
||||||
|
foreach (var proto in _prototypeManager.EnumeratePrototypes<NameIdentifierGroupPrototype>())
|
||||||
|
{
|
||||||
|
CurrentIds.Add(proto, new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReloadPrototypes(PrototypesReloadedEventArgs ev)
|
||||||
|
{
|
||||||
|
if (!ev.ByType.TryGetValue(typeof(NameIdentifierGroupPrototype), out var set))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var (_, proto) in set.Modified)
|
||||||
|
{
|
||||||
|
if (proto is not NameIdentifierGroupPrototype group)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Only bother adding new ones.
|
||||||
|
if (CurrentIds.ContainsKey(group))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CurrentIds.Add(group, new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupIds(RoundRestartCleanupEvent ev)
|
||||||
|
{
|
||||||
|
foreach (var (_, set) in CurrentIds)
|
||||||
|
{
|
||||||
|
set.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.NameIdentifier;
|
||||||
|
|
||||||
|
[Prototype("nameIdentifierGroup")]
|
||||||
|
public sealed class NameIdentifierGroupPrototype : IPrototype
|
||||||
|
{
|
||||||
|
[DataField("id", required: true)]
|
||||||
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the identifier become the full name, or just append?
|
||||||
|
/// </summary>
|
||||||
|
[DataField("fullName")]
|
||||||
|
public bool FullName = false;
|
||||||
|
|
||||||
|
[DataField("prefix")]
|
||||||
|
public string? Prefix;
|
||||||
|
|
||||||
|
[DataField("maxValue")]
|
||||||
|
public int MaxValue = 999;
|
||||||
|
|
||||||
|
[DataField("minValue")]
|
||||||
|
public int MinValue = 0;
|
||||||
|
}
|
||||||
@@ -551,6 +551,8 @@
|
|||||||
parent: SimpleMobBase
|
parent: SimpleMobBase
|
||||||
description: New church of neo-darwinists actually believe that EVERY animal evolved from a monkey. Tastes like pork, and killing them is both fun and relaxing.
|
description: New church of neo-darwinists actually believe that EVERY animal evolved from a monkey. Tastes like pork, and killing them is both fun and relaxing.
|
||||||
components:
|
components:
|
||||||
|
- type: NameIdentifier
|
||||||
|
group: Monkey
|
||||||
- type: GhostTakeoverAvailable
|
- type: GhostTakeoverAvailable
|
||||||
makeSentient: true
|
makeSentient: true
|
||||||
name: monkey
|
name: monkey
|
||||||
|
|||||||
@@ -63,6 +63,8 @@
|
|||||||
- id: PowerDrill
|
- id: PowerDrill
|
||||||
- id: JawsOfLife
|
- id: JawsOfLife
|
||||||
- id: WelderExperimental
|
- id: WelderExperimental
|
||||||
|
- type: NameIdentifier
|
||||||
|
group: Drone
|
||||||
- type: GhostTakeoverAvailable
|
- type: GhostTakeoverAvailable
|
||||||
makeSentient: true
|
makeSentient: true
|
||||||
name: Maintenance Drone
|
name: Maintenance Drone
|
||||||
|
|||||||
11
Resources/Prototypes/name_identifier_groups.yml
Normal file
11
Resources/Prototypes/name_identifier_groups.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Non-fungible apes, anyone?
|
||||||
|
- type: nameIdentifierGroup
|
||||||
|
id: Monkey
|
||||||
|
prefix: MK
|
||||||
|
|
||||||
|
- type: nameIdentifierGroup
|
||||||
|
id: Drone
|
||||||
|
prefix: DR
|
||||||
|
fullName: true
|
||||||
|
minValue: 10000
|
||||||
|
maxValue: 99999
|
||||||
Reference in New Issue
Block a user