diff --git a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs index 89ac0c1485b..64d954331c0 100644 --- a/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs +++ b/Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs @@ -93,6 +93,10 @@ public BwoinkControl() var ach = AHelpHelper.EnsurePanel(a.SessionId); var bch = AHelpHelper.EnsurePanel(b.SessionId); + // Pinned players first + if (a.IsPinned != b.IsPinned) + return a.IsPinned ? -1 : 1; + // First, sort by unread. Any chat with unread messages appears first. We just sort based on unread // status, not number of unread messages, so that more recent unread messages take priority. var aUnread = ach.Unread > 0; @@ -104,15 +108,31 @@ public BwoinkControl() if (a.Connected != b.Connected) return a.Connected ? -1 : 1; - // Next, group by whether or not the players have participated in this round. - // The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom. - if (a.ActiveThisRound != b.ActiveThisRound) - return a.ActiveThisRound ? -1 : 1; + // Sort connected players 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)); + + if (aNewPlayer != bNewPlayer) + return aNewPlayer ? -1 : 1; + + if (a.Antag != b.Antag) + return a.Antag ? -1 : 1; + } + + // Sort disconnected players by participation in the round + if (!a.Connected && !b.Connected) + { + if (a.ActiveThisRound != b.ActiveThisRound) + return a.ActiveThisRound ? -1 : 1; + } // Finally, sort by the most recent message. return bch.LastMessage.CompareTo(ach.LastMessage); }; + Bans.OnPressed += _ => { if (_currentPlayer is not null) @@ -258,7 +278,20 @@ private void SwitchToChannel(NetUserId? ch) public void PopulateList() { + // Maintain existing pin statuses + var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId); + ChannelSelector.PopulateList(); + + // Restore pin statuses + foreach (var player in ChannelSelector.PlayerInfo) + { + if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer)) + { + player.IsPinned = pinnedPlayer.IsPinned; + } + } + UpdateButtons(); } } diff --git a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs index b09cd727ef8..c7fbf6c2dc0 100644 --- a/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs +++ b/Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs @@ -21,11 +21,11 @@ public sealed partial class PlayerListControl : BoxContainer private readonly IEntityManager _entManager; private readonly IUserInterfaceManager _uiManager; - + private PlayerInfo? _selectedPlayer; private List _playerList = new(); - private readonly List _sortedPlayerList = new(); + private List _sortedPlayerList = new(); public Comparison? Comparison; public Func? OverrideText; @@ -110,19 +110,30 @@ private void FilterList() if (Comparison != null) _sortedPlayerList.Sort((a, b) => Comparison(a, b)); - // Ensure pinned players are always at the top - _sortedPlayerList.Sort((a, b) => a.IsPinned != b.IsPinned && a.IsPinned ? -1 : 1); - PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList()); if (_selectedPlayer != null) PlayerListContainer.Select(new PlayerListData(_selectedPlayer)); } + public void PopulateList(IReadOnlyList? players = null) { + // Maintain existing pin statuses + var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId); + players ??= _adminSystem.PlayerList; _playerList = players.ToList(); + + // Restore pin statuses + foreach (var player in _playerList) + { + if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer)) + { + player.IsPinned = pinnedPlayer.IsPinned; + } + } + if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer)) _selectedPlayer = null; diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs index ccc39d50ff1..0a62ff23619 100644 --- a/Content.Client/Lobby/LobbyUIController.cs +++ b/Content.Client/Lobby/LobbyUIController.cs @@ -275,6 +275,7 @@ private void OpenSavePanel() _logManager, _playerManager, _prototypeManager, + _resourceCache, _requirements, _markings); diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml index 2aa87a1fd77..7ff0c2e4dc4 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml @@ -38,6 +38,8 @@