Skip to content

Commit

Permalink
Upstream 03.08.2024
Browse files Browse the repository at this point in the history
Merge pull request #41 from Space-Stories/upstream
  • Loading branch information
doublechest0 authored Aug 3, 2024
2 parents 7aa97ec + 329cef7 commit 1a13a1e
Show file tree
Hide file tree
Showing 666 changed files with 95,056 additions and 97,523 deletions.
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<!-- Если это изменение кода, кратко опишите на высоком уровне, как работает ваш новый код. Это облегчит рецензирование. -->

## Медиа
<!--
<!--
К PR, вносящим внутриигровые изменения (добавление одежды, предметов, новых возможностей и т.д.), необходимо прикладывать медиа, демонстрирующие изменения.
Небольшие исправления/рефакторы не рассматриваются.
Любые медиаматериалы могут быть использованы в отчетах о проделанной работе в SS14, с указанием четких заслуг.
Expand Down
4 changes: 4 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
- changed-files:
- any-glob-to-any-file: '**/*.xaml*'

"Changes: Shaders":
- changed-files:
- any-glob-to-any-file: '**/*.swsl'

"No C#":
- changed-files:
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
Expand Down
50 changes: 44 additions & 6 deletions Content.Client/Administration/UI/Bwoink/BwoinkControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,51 @@ public BwoinkControl()
var ach = AHelpHelper.EnsurePanel(a.SessionId);
var bch = AHelpHelper.EnsurePanel(b.SessionId);

// 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.
// Pinned players first
if (a.IsPinned != b.IsPinned)
return a.IsPinned ? -1 : 1;

// First, sort by unread. Any chat with unread messages appears first.
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.
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.
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)
Expand Down Expand Up @@ -253,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();
}
}
Expand Down
6 changes: 5 additions & 1 deletion Content.Client/Administration/UI/Bwoink/BwoinkWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ public BwoinkWindow()
}
};

OnOpen += () => Bwoink.PopulateList();
OnOpen += () =>
{
Bwoink.ChannelSelector.StopFiltering();
Bwoink.PopulateList();
};
}
}
}
236 changes: 124 additions & 112 deletions Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,154 +4,166 @@
using Content.Client.Verbs.UI;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Utility;

namespace Content.Client.Administration.UI.CustomControls
namespace Content.Client.Administration.UI.CustomControls;

[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
{
[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
{
private readonly AdminSystem _adminSystem;
private readonly AdminSystem _adminSystem;

private List<PlayerInfo> _playerList = new();
private readonly List<PlayerInfo> _sortedPlayerList = new();
private readonly IEntityManager _entManager;
private readonly IUserInterfaceManager _uiManager;

public event Action<PlayerInfo?>? OnSelectionChanged;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
private PlayerInfo? _selectedPlayer;

public Func<PlayerInfo, string, string>? OverrideText;
public Comparison<PlayerInfo>? Comparison;
private List<PlayerInfo> _playerList = new();
private List<PlayerInfo> _sortedPlayerList = new();

private IEntityManager _entManager;
private IUserInterfaceManager _uiManager;
public Comparison<PlayerInfo>? Comparison;
public Func<PlayerInfo, string, string>? OverrideText;

private PlayerInfo? _selectedPlayer;
public PlayerListControl()
{
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
}

public PlayerListControl()
{
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
}
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;

private void PlayerListNoItemSelected()
{
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
public event Action<PlayerInfo?>? OnSelectionChanged;

private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
return;
private void PlayerListNoItemSelected()
{
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}

if (selectedPlayer == _selectedPlayer)
return;
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;

if (args.Event.Function != EngineKeyFunctions.UIClick)
return;
if (selectedPlayer == _selectedPlayer)
return;

OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
if (args.Event.Function != EngineKeyFunctions.UIClick)
return;

// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;

private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}

if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;

_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;

_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}

public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}

public void StopFiltering()
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
FilterLineEdit.Text = string.Empty;
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}

private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));

if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}

PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}

public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
players ??= _adminSystem.PlayerList;
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
// Maintain existing pin statuses
var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);

_playerList = players.ToList();
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;
players ??= _adminSystem.PlayerList;

FilterList();
}
_playerList = players.ToList();

private string GetText(PlayerInfo info)
// Restore pin statuses
foreach (var player in _playerList)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
{
player.IsPinned = pinnedPlayer.IsPinned;
}
}

private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;

button.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new Label
{
ClipText = true,
Text = GetText(info)
}
}
});

button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
FilterList();
}


private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}

public record PlayerListData(PlayerInfo Info) : ListData;
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;

var entry = new PlayerListEntry();
entry.Setup(info, OverrideText);
entry.OnPinStatusChanged += _ =>
{
FilterList();
};

button.AddChild(entry);
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
}

public record PlayerListData(PlayerInfo Info) : ListData;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal" HorizontalExpand="true">
<Label Name="PlayerEntryLabel" Text="" ClipText="True" HorizontalExpand="True" />
<TextureButton Name="PlayerEntryPinButton"
HorizontalAlignment="Right" />
</BoxContainer>
Loading

0 comments on commit 1a13a1e

Please sign in to comment.