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);
IoCManager.InjectDependencies(this);
var newPlayerThreshold = 0;
_cfg.OnValueChanged(CCVars.NewPlayerThreshold, (val) => { newPlayerThreshold = val; }, true);
var uiController = _ui.GetUIController<AHelpUIController>();
if (uiController.UIHelper is not AdminAHelpUIHandler helper)
return;
@@ -59,9 +62,9 @@ namespace Content.Client.Administration.UI.Bwoink
var sb = new StringBuilder();
if (info.Connected)
sb.Append('●');
sb.Append(info.ActiveThisRound ? '⚫' : '◐');
else
sb.Append(info.ActiveThisRound ? '' : '·');
sb.Append(info.ActiveThisRound ? '' : '·');
sb.Append(' ');
if (AHelpHelper.TryGetChannel(info.SessionId, out var panel) && panel.Unread > 0)
@@ -73,10 +76,12 @@ namespace Content.Client.Administration.UI.Bwoink
sb.Append(' ');
}
// Mark antagonists with symbol
if (info.Antag && info.ActiveThisRound)
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.AppendFormat("\"{0}\"", text);
@@ -84,6 +89,19 @@ namespace Content.Client.Administration.UI.Bwoink
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) =>
{
var ach = AHelpHelper.EnsurePanel(a.SessionId);
@@ -93,31 +111,37 @@ namespace Content.Client.Administration.UI.Bwoink
if (a.IsPinned != b.IsPinned)
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 bUnread = bch.Unread > 0;
if (aUnread != bUnread)
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 bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
if (aRecent != bRecent)
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)
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)
{
var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
var bNewPlayer = b.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
var aNewPlayer = IsNewPlayer(a);
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)
return aNewPlayer ? -1 : 1;
// Within all four previous groups, antagonists will be listed first.
if (a.Antag != b.Antag)
return a.Antag ? -1 : 1;
}

View File

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

View File

@@ -222,6 +222,7 @@ public sealed class AdminSystem : EntitySystem
var entityName = string.Empty;
var identityName = string.Empty;
// Visible (identity) name can be different from real name
if (session?.AttachedEntity != null)
{
entityName = EntityManager.GetComponent<MetaDataComponent>(session.AttachedEntity.Value).EntityName;
@@ -230,6 +231,7 @@ public sealed class AdminSystem : EntitySystem
var antag = false;
// Starting role, antagonist status and role type
RoleTypePrototype roleType = new();
var startingRole = string.Empty;
if (_minds.TryGetMind(session, out var mindId, out var mindComp))
@@ -243,8 +245,13 @@ public sealed class AdminSystem : EntitySystem
startingRole = _jobs.MindTryGetJobName(mindId);
}
// Connection status and playtime
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 &&
_playTime.TryGetTrackerTimes(session, out var playTimes) &&
playTimes.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out var playTime))