Players with unknown playtimes now are tagged as new players, take 2 (#35648)

* your commit? our commit.

* skreee

* show joined players before lobby players; comments

* comments

* playerinfo retains playtime data after disconnect

* new connection status symbols
This commit is contained in:
Errant
2025-03-05 17:25:42 +01:00
committed by GitHub
parent b57b534380
commit 6f23e10495
3 changed files with 43 additions and 15 deletions

View File

@@ -36,6 +36,9 @@ namespace Content.Client.Administration.UI.Bwoink
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
var newPlayerThreshold = 0;
_cfg.OnValueChanged(CCVars.NewPlayerThreshold, (val) => { newPlayerThreshold = val; }, true);
var uiController = _ui.GetUIController<AHelpUIController>(); var uiController = _ui.GetUIController<AHelpUIController>();
if (uiController.UIHelper is not AdminAHelpUIHandler helper) if (uiController.UIHelper is not AdminAHelpUIHandler helper)
return; return;
@@ -59,9 +62,9 @@ namespace Content.Client.Administration.UI.Bwoink
var sb = new StringBuilder(); var sb = new StringBuilder();
if (info.Connected) if (info.Connected)
sb.Append('●'); sb.Append(info.ActiveThisRound ? '⚫' : '◐');
else else
sb.Append(info.ActiveThisRound ? '' : '·'); sb.Append(info.ActiveThisRound ? '' : '·');
sb.Append(' '); sb.Append(' ');
if (AHelpHelper.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0) if (AHelpHelper.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0)
@@ -73,10 +76,12 @@ namespace Content.Client.Administration.UI.Bwoink
sb.Append(' '); sb.Append(' ');
} }
// Mark antagonists with symbol
if (info.Antag && info.ActiveThisRound) if (info.Antag && info.ActiveThisRound)
sb.Append(new Rune(0x1F5E1)); // 🗡 sb.Append(new Rune(0x1F5E1)); // 🗡
if (info.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold))) // Mark new players with symbol
if (IsNewPlayer(info))
sb.Append(new Rune(0x23F2)); // ⏲ sb.Append(new Rune(0x23F2)); // ⏲
sb.AppendFormat("\"{0}\"", text); sb.AppendFormat("\"{0}\"", text);
@@ -84,6 +89,19 @@ namespace Content.Client.Administration.UI.Bwoink
return sb.ToString(); return sb.ToString();
}; };
// <summary>
// Returns true if the player's overall playtime is under the set threshold
// </summary>
bool IsNewPlayer(PlayerInfo info)
{
// Don't show every disconnected player as new, don't show 0-minute players as new if threshold is
if (newPlayerThreshold <= 0 || info.OverallPlaytime is null && !info.Connected)
return false;
return (info.OverallPlaytime is null
|| info.OverallPlaytime < TimeSpan.FromMinutes(newPlayerThreshold));
}
ChannelSelector.Comparison = (a, b) => ChannelSelector.Comparison = (a, b) =>
{ {
var ach = AHelpHelper.EnsurePanel(a.SessionId); var ach = AHelpHelper.EnsurePanel(a.SessionId);
@@ -93,31 +111,37 @@ namespace Content.Client.Administration.UI.Bwoink
if (a.IsPinned != b.IsPinned) if (a.IsPinned != b.IsPinned)
return a.IsPinned ? -1 : 1; return a.IsPinned ? -1 : 1;
// First, sort by unread. Any chat with unread messages appears first. // Then, any chat with unread messages.
var aUnread = ach.Unread > 0; var aUnread = ach.Unread > 0;
var bUnread = bch.Unread > 0; var bUnread = bch.Unread > 0;
if (aUnread != bUnread) if (aUnread != bUnread)
return aUnread ? -1 : 1; return aUnread ? -1 : 1;
// Sort by recent messages during the current round. // Then, any chat with recent messages from the current round
var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue; var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue;
var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue; var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
if (aRecent != bRecent) if (aRecent != bRecent)
return aRecent ? -1 : 1; return aRecent ? -1 : 1;
// Next, sort by connection status. Any disconnected players are grouped towards the end. // Sort by connection status. Disconnected players will be last.
if (a.Connected != b.Connected) if (a.Connected != b.Connected)
return a.Connected ? -1 : 1; return a.Connected ? -1 : 1;
// Sort connected players by New Player status, then by Antag status // Sort connected players by whether they have joined the round, then by New Player status, then by Antag status
if (a.Connected && b.Connected) if (a.Connected && b.Connected)
{ {
var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)); var aNewPlayer = IsNewPlayer(a);
var bNewPlayer = b.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)); var bNewPlayer = IsNewPlayer(b);
// Players who have joined the round will be listed before players in the lobby
if (a.ActiveThisRound != b.ActiveThisRound)
return a.ActiveThisRound ? -1 : 1;
// Within both the joined group and lobby group, new players will be grouped and listed first
if (aNewPlayer != bNewPlayer) if (aNewPlayer != bNewPlayer)
return aNewPlayer ? -1 : 1; return aNewPlayer ? -1 : 1;
// Within all four previous groups, antagonists will be listed first.
if (a.Antag != b.Antag) if (a.Antag != b.Antag)
return a.Antag ? -1 : 1; return a.Antag ? -1 : 1;
} }

View File

@@ -22,12 +22,9 @@ namespace Content.Client.Administration.UI.Bwoink
return; return;
} }
Title = $"{sel.CharacterName} / {sel.Username}"; Title = $"{sel.CharacterName} / {sel.Username} | {Loc.GetString("generic-playtime-title")}: ";
if (sel.OverallPlaytime != null) Title += sel.OverallPlaytime != null ? sel.PlaytimeString : Loc.GetString("generic-unknown-title");
{
Title += $" | {Loc.GetString("generic-playtime-title")}: {sel.PlaytimeString}";
}
}; };
OnOpen += () => OnOpen += () =>

View File

@@ -222,6 +222,7 @@ public sealed class AdminSystem : EntitySystem
var entityName = string.Empty; var entityName = string.Empty;
var identityName = string.Empty; var identityName = string.Empty;
// Visible (identity) name can be different from real name
if (session?.AttachedEntity != null) if (session?.AttachedEntity != null)
{ {
entityName = EntityManager.GetComponent<MetaDataComponent>(session.AttachedEntity.Value).EntityName; entityName = EntityManager.GetComponent<MetaDataComponent>(session.AttachedEntity.Value).EntityName;
@@ -230,6 +231,7 @@ public sealed class AdminSystem : EntitySystem
var antag = false; var antag = false;
// Starting role, antagonist status and role type
RoleTypePrototype roleType = new(); RoleTypePrototype roleType = new();
var startingRole = string.Empty; var startingRole = string.Empty;
if (_minds.TryGetMind(session, out var mindId, out var mindComp)) if (_minds.TryGetMind(session, out var mindId, out var mindComp))
@@ -243,8 +245,13 @@ public sealed class AdminSystem : EntitySystem
startingRole = _jobs.MindTryGetJobName(mindId); startingRole = _jobs.MindTryGetJobName(mindId);
} }
// Connection status and playtime
var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame; var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame;
TimeSpan? overallPlaytime = null;
// Start with the last available playtime data
var cachedInfo = GetCachedPlayerInfo(data.UserId);
var overallPlaytime = cachedInfo?.OverallPlaytime;
// Overwrite with current playtime data, unless it's null (such as if the player just disconnected)
if (session != null && if (session != null &&
_playTime.TryGetTrackerTimes(session, out var playTimes) && _playTime.TryGetTrackerTimes(session, out var playTimes) &&
playTimes.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out var playTime)) playTimes.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out var playTime))