CatStation/Content.Server/NewPlayer/NewPlayerSystem.cs
SlamBamActionman 19bc31aef7
Add "new player label" over the head of players joining the game for the first time (#44709)
* Initial commit

* RMC? I 'ardly know 'er

* Sprout

* Rework it to use job icons

* Revert the one change

* Always the one yaml

* Easy review changes

* We do a little salami

* Review changes

* NIGHTMARE NIGHTMARE NIGHTMARE

* Update to use StatusIconPrototype only

* Remove async

* Aaaand remove the comment, bah

* Review fixes

* Update master

* Add the ability for ghosts to see

* Byte backing

* Review fix + comment

* Update to 90 min

* Add todo message
2026-09-24 15:34:37 +00:00

82 lines
3.2 KiB
C#

using Content.Server.Database;
using Content.Server.Players.PlayTimeTracking;
using Content.Server.Players.Whitelist;
using Content.Shared.CCVar;
using Content.Shared.NewPlayer;
using Content.Shared.GameTicking;
using Content.Shared.Ghost.Components;
using Robust.Shared.Configuration;
using Robust.Shared.GameStates;
using Robust.Shared.Player;
namespace Content.Server.NewPlayer;
/// <summary>
/// Used to set "new player" icons, and to manage the components to only have them visible to players with <see cref="ShowNewPlayerIconComponent"/>.
/// </summary>
public sealed partial class NewPlayerSystem : EntitySystem
{
[Dependency] private IConfigurationManager _config = default!;
[Dependency] private PlayTimeTrackingManager _playtimeManager = default!;
[Dependency] private WhitelistManager _whitelistManager = default!;
[Dependency] private EntityQuery<ShowNewPlayerIconComponent> _showPlayerIconQuery = default!;
private TimeSpan _newPlayerTimeTotal;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
Subs.CVar(_config, CCVars.NewPlayerTimeTotalMinutes, v => _newPlayerTimeTotal = TimeSpan.FromMinutes(v), true);
}
[SubscribeLocalEvent]
private void OnNewPlayerGetStateAttempt(Entity<NewPlayerIconComponent> entity, ref ComponentGetStateAttemptEvent args)
{
args.Cancelled = !(args.Player?.AttachedEntity is not { } uid || _showPlayerIconQuery.HasComp(uid));
}
/// <summary>
/// This is a bit of a "hack" to ensure that the component states are properly updated in the event a component
/// is removed out of when the client isn't here to receive it. It's taken from how SharedRevolutionarySystem does it.
/// TODO: It's not ideal, and preferably we'd have a more solid way to handle specific session component handling.
/// </summary>
[SubscribeLocalEvent]
private void OnComponentStartup(Entity<ShowNewPlayerIconComponent> entity, ref ComponentStartup args)
{
var newPlayerQuery = AllEntityQuery<NewPlayerIconComponent>();
while (newPlayerQuery.MoveNext(out var uid, out var comp))
{
Dirty(uid, comp);
}
}
[SubscribeLocalEvent]
private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent ev)
{
// Give to players who should see
if (_whitelistManager.IsConnectedWhitelisted(ev.Player)) // TODO: This should use permissions from a Mentor group or similar, as this only checks manual whitelisting.
EnsureComp<ShowNewPlayerIconComponent>(ev.Mob);
// Give the players who should have it
try
{
var totalTime = _playtimeManager.GetOverallPlaytime(ev.Player);
if (totalTime < _newPlayerTimeTotal)
EnsureComp<NewPlayerIconComponent>(ev.Mob);
}
catch (Exception e)
{
Log.Error($"Error getting new player playtime:\n{e}");
}
}
[SubscribeLocalEvent]
private void OnPlayerAttachedEvent(Entity<GhostComponent> ent, ref PlayerAttachedEvent ev)
{
if (_whitelistManager.IsConnectedWhitelisted(ev.Player))
EnsureComp<ShowNewPlayerIconComponent>(ev.Entity);
}
}