Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "RobustToolbox"]
path = RobustToolbox
url = https://github.com/space-wizards/RobustToolbox.git
url = https://github.com/Simple-Station/SupermatterEngine.git
branch = master
[submodule "StyleSheetify"]
path = StyleSheetify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Input;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Utility;

namespace Content.Client.Administration.UI.CustomControls;
Expand All @@ -21,6 +24,10 @@ public sealed partial class PlayerListControl : BoxContainer

private readonly IEntityManager _entManager;
private readonly IUserInterfaceManager _uiManager;
// WD EDIT START
private readonly ISharedPlayerManager _playerManager;
private readonly IConfigurationManager _config;
// WD EDIT END

private PlayerInfo? _selectedPlayer;

Expand All @@ -34,6 +41,10 @@ public PlayerListControl()
{
_entManager = IoCManager.Resolve<IEntityManager>();
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
// WD EDIT START
_playerManager = IoCManager.Resolve<ISharedPlayerManager>();
_config = IoCManager.Resolve<IConfigurationManager>();
// WD EDIT END
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
Expand Down Expand Up @@ -98,7 +109,10 @@ private void FilterList()
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
// WD EDIT START
var displayName = $"{info.CharacterName} ({info.Username}@{AuthServer.GetServerFromCVarListByUrl(_config,
_playerManager.GetSessionById(info.SessionId).Channel.UserData.AuthServer)?.Id})";
// WD EDIT END
Comment on lines +112 to +115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Потенциальное NullReferenceException при получении сессии (аналогично PlayerListEntry).

Та же проблема, что и в PlayerListEntry.xaml.cs — цепочка вызовов _playerManager.GetSessionById(...).Channel.UserData.AuthServer может выбросить исключение.

🛠️ Предлагаемое исправление
-            var displayName = $"{info.CharacterName} ({info.Username}@{AuthServer.GetServerFromCVarListByUrl(_config,
-                _playerManager.GetSessionById(info.SessionId).Channel.UserData.AuthServer)?.Id})";
+            var serverId = "Unknown";
+            if (_playerManager.TryGetSessionById(info.SessionId, out var session))
+            {
+                serverId = AuthServer.GetServerFromCVarListByUrl(_config, session.Channel.UserData.AuthServer)?.Id ?? "Unknown";
+            }
+            var displayName = $"{info.CharacterName} ({info.Username}@{serverId})";
🤖 Prompt for AI Agents
In @Content.Client/Administration/UI/CustomControls/PlayerListControl.xaml.cs
around lines 112 - 115, The displayName construction can throw a
NullReferenceException because
_playerManager.GetSessionById(info.SessionId).Channel.UserData.AuthServer may be
null; fix by retrieving the session into a local (e.g., var session =
_playerManager.GetSessionById(info.SessionId)), then null-check session,
session.Channel, session.Channel.UserData and
session.Channel.UserData.AuthServer before accessing Id (use conditional access
or explicit checks) and fall back to a safe string like "unknown" when any piece
is null; update the displayName expression in PlayerListControl.xaml.cs to use
that guarded value (mirror the null-safe pattern used in
PlayerListEntry.xaml.cs).

if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Utility;

namespace Content.Client.Administration.UI.CustomControls;

[GenerateTypedNameReferences]
public sealed partial class PlayerListEntry : BoxContainer
{
// WD EDIT START
private readonly ISharedPlayerManager _playerManager;
private readonly IConfigurationManager _config;
// WD EDIT END
public PlayerListEntry()
{
RobustXamlLoader.Load(this);
// WD EDIT START
_playerManager = IoCManager.Resolve<IPlayerManager>();
_config = IoCManager.Resolve<IConfigurationManager>();
// WD EDIT END
}

public event Action<PlayerInfo>? OnPinStatusChanged;
Expand All @@ -36,8 +48,12 @@ public void Setup(PlayerInfo info, Func<PlayerInfo, string, string>? overrideTex

private void Update(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
$"{info.CharacterName} ({info.Username})";
// WD EDIT START
PlayerEntryLabel.Text = overrideText?.Invoke(
info,
$"{info.CharacterName} ({info.Username}@{AuthServer.GetServerFromCVarListByUrl(_config,
_playerManager.GetSessionById(info.SessionId).Channel.UserData.AuthServer)?.Id})") ?? $"{info.CharacterName} ({info.Username})";
// WD EDIT END
Comment on lines +51 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Потенциальное NullReferenceException при получении сессии.

Цепочка вызовов _playerManager.GetSessionById(info.SessionId).Channel.UserData.AuthServer может выбросить исключение, если сессия не найдена или отсутствует.

Рассмотрите добавление null-проверок или использование TryGetSessionById:

🛠️ Предлагаемое исправление
-        PlayerEntryLabel.Text = overrideText?.Invoke(
-                info,
-                $"{info.CharacterName} ({info.Username}@{AuthServer.GetServerFromCVarListByUrl(_config,
-                    _playerManager.GetSessionById(info.SessionId).Channel.UserData.AuthServer)?.Id})") ?? $"{info.CharacterName} ({info.Username})";
+        var serverId = "Unknown";
+        if (_playerManager.TryGetSessionById(info.SessionId, out var session))
+        {
+            serverId = AuthServer.GetServerFromCVarListByUrl(_config, session.Channel.UserData.AuthServer)?.Id ?? "Unknown";
+        }
+        var displayName = $"{info.CharacterName} ({info.Username}@{serverId})";
+        PlayerEntryLabel.Text = overrideText?.Invoke(info, displayName) ?? $"{info.CharacterName} ({info.Username})";


UpdatePinButtonTexture(info.IsPinned);
}
Expand Down
5 changes: 3 additions & 2 deletions Content.Server/Administration/PlayerLocator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
Expand Down Expand Up @@ -101,7 +102,7 @@ public PlayerLocator()
return ReturnForPlayerRecord(record);

// If all else fails, ask the auth server.
var authServer = _configurationManager.GetCVar(CVars.AuthServer);
var authServer = AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers)).First().AuthUrl; // WD EDIT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Потенциальное исключение при пустом списке серверов аутентификации.

Вызов .First() на пустой последовательности выбросит InvalidOperationException. Если CVars.AuthServers пуст или не содержит валидных записей, это приведёт к сбою.

🛠️ Предлагаемое исправление
-            var authServer = AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers)).First().AuthUrl; // WD EDIT
+            var authServers = AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers));
+            var authServer = authServers.FirstOrDefault()?.AuthUrl
+                ?? throw new InvalidOperationException("No auth servers configured in CVars.AuthServers"); // WD EDIT
🤖 Prompt for AI Agents
In @Content.Server/Administration/PlayerLocator.cs at line 105,
AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers)).First()
can throw InvalidOperationException for an empty/invalid list; change the call
to safely handle empties (e.g., use FirstOrDefault/null-check or check the
returned collection count) and then handle the missing server case before
accessing AuthUrl — update the usage in the method that currently reads
AuthServer.FromStringList(...).First().AuthUrl to use a safe retrieval
(FirstOrDefault), verify the result is non-null (or collection non-empty) and
provide a fallback or clear error/log message if no auth server is available.

var requestUri = $"{authServer}api/query/name?name={WebUtility.UrlEncode(playerName)}";
using var resp = await _httpClient.GetAsync(requestUri, cancel);

Expand All @@ -120,7 +121,7 @@ public PlayerLocator()
return ReturnForPlayerRecord(record);

// If all else fails, ask the auth server.
var authServer = _configurationManager.GetCVar(CVars.AuthServer);
var authServer = AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers)).First().AuthUrl; // WD EDIT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Дублирование проблемы с .First() — то же самое исключение возможно здесь.

Аналогичная проблема с вызовом .First() без проверки на пустую последовательность. Рассмотрите вынос логики получения auth server URL в отдельный приватный метод для устранения дублирования.

♻️ Предлагаемый рефакторинг — вынести в отдельный метод
+    private string GetPrimaryAuthServerUrl()
+    {
+        var authServers = AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers));
+        return authServers.FirstOrDefault()?.AuthUrl
+            ?? throw new InvalidOperationException("No auth servers configured");
+    }

Затем использовать:

-            var authServer = AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers)).First().AuthUrl; // WD EDIT
+            var authServer = GetPrimaryAuthServerUrl(); // WD EDIT
🤖 Prompt for AI Agents
In @Content.Server/Administration/PlayerLocator.cs at line 124, The code calls
AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers)).First().AuthUrl
which can throw on an empty sequence and duplicates this logic; extract this
into a private helper (e.g. GetAuthServerUrl or ResolveAuthServerUrl) that calls
AuthServer.FromStringList, checks for empty/null (use
FirstOrDefault/SingleOrDefault), returns AuthUrl or handles the missing case
(log and throw a clear exception or return null/Optional) and replace all direct
.First().AuthUrl usages with this helper to remove duplication and avoid the
exception.

var requestUri = $"{authServer}api/query/userid?userid={WebUtility.UrlEncode(userId.UserId.ToString())}";
using var resp = await _httpClient.GetAsync(requestUri, cancel);

Expand Down
25 changes: 23 additions & 2 deletions Content.Server/Chat/Managers/ChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Content.Shared.Database;
using Content.Shared.Mind;
using Content.Shared.Players.RateLimiting;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
Expand Down Expand Up @@ -248,15 +249,35 @@ private void SendOOC(ICommonSession player, string message)
}

Color? colorOverride = null;
var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message)));

// WD EDDIT START
var authServer = player.AuthType == LoginType.LoggedIn
? AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers))
.FirstOrDefault(x => x.AuthUrl.ToString() == player.Channel.UserData.AuthServer)
?.Id ?? "Unknown"
: "Unknown";
var wrappedMessage = Loc.GetString(
"chat-manager-send-ooc-wrap-message",
("playerName", $"{player.Name}"),
("authServer", authServer),
("message", FormattedMessage.EscapeText(message)));
// WD EDIT END
Comment on lines +253 to +264
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Логика определения authServer корректна, но есть опечатка в комментарии.

Реализация с использованием FirstOrDefault и ?? "Unknown" обеспечивает правильную обработку случаев, когда сервер не найден. Однако, на строке 253 опечатка: WD EDDIT вместо WD EDIT.

🔧 Исправление опечатки
-        // WD EDDIT START
+        // WD EDIT START
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// WD EDDIT START
var authServer = player.AuthType == LoginType.LoggedIn
? AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers))
.FirstOrDefault(x => x.AuthUrl.ToString() == player.Channel.UserData.AuthServer)
?.Id ?? "Unknown"
: "Unknown";
var wrappedMessage = Loc.GetString(
"chat-manager-send-ooc-wrap-message",
("playerName", $"{player.Name}"),
("authServer", authServer),
("message", FormattedMessage.EscapeText(message)));
// WD EDIT END
// WD EDIT START
var authServer = player.AuthType == LoginType.LoggedIn
? AuthServer.FromStringList(_configurationManager.GetCVar(CVars.AuthServers))
.FirstOrDefault(x => x.AuthUrl.ToString() == player.Channel.UserData.AuthServer)
?.Id ?? "Unknown"
: "Unknown";
var wrappedMessage = Loc.GetString(
"chat-manager-send-ooc-wrap-message",
("playerName", $"{player.Name}"),
("authServer", authServer),
("message", FormattedMessage.EscapeText(message)));
// WD EDIT END
🤖 Prompt for AI Agents
In @Content.Server/Chat/Managers/ChatManager.cs around lines 253 - 264, The
inline block comments around the authServer logic contain a typo: change the
opening marker "WD EDDIT START" to "WD EDIT START" (matching the closing "WD
EDIT END") so comment markers are consistent; update only the comment text
surrounding the AuthServer resolution/Loc.GetString call (the lines containing
"WD EDDIT START" and "WD EDIT END").


if (_adminManager.HasAdminFlag(player, AdminFlags.Admin))
{
var prefs = _preferencesManager.GetPreferences(player.UserId);
colorOverride = prefs.AdminOOCColor;
}
if ( _netConfigManager.GetClientCVar(player.Channel, CCVars.ShowOocPatronColor) && player.Channel.UserData.PatronTier is { } patron && PatronOocColors.TryGetValue(patron, out var patronColor))
{
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
// WD EDIT START
wrappedMessage = Loc.GetString(
"chat-manager-send-ooc-patron-wrap-message",
("patronColor", patronColor),
("playerName", player.Name),
("authServer", authServer),
("message", FormattedMessage.EscapeText(message)));
// WD EDIT END
}

//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
Expand Down
10 changes: 8 additions & 2 deletions Resources/Locale/en-US/chat/managers/chat-manager.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,14 @@ chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) ->
}

chat-manager-entity-looc-wrap-message = LOOC: {$entityName}: {$message}
chat-manager-send-ooc-wrap-message = OOC: {$playerName}: {$message}
chat-manager-send-ooc-patron-wrap-message = OOC: [color={$patronColor}]{$playerName}[/color]: {$message}
chat-manager-send-ooc-wrap-message = OOC: {$playerName}{$authServer ->
[Unknown] {""}
*[other] @{$authServer}
}: {$message}
chat-manager-send-ooc-patron-wrap-message = OOC: [color={$patronColor}]{$playerName}{$authServer ->
[Unknown] {""}
*[other] @{$authServer}
}[/color]: {$message}

chat-manager-send-dead-chat-wrap-message = {$deadChannelName}: [BubbleHeader]{$playerName}[/BubbleHeader]: [BubbleContent]{$message}[/BubbleContent]
chat-manager-send-admin-dead-chat-wrap-message = {$adminChannelName}: ([BubbleHeader]{$userName}[/BubbleHeader]): [BubbleContent]{$message}[/BubbleContent]
Expand Down
10 changes: 8 additions & 2 deletions Resources/Locale/ru-RU/chat/managers/chat-manager.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@ chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][Bubble
chat-manager-entity-me-wrap-message = { $entityName } { $message }

chat-manager-entity-looc-wrap-message = LOOC: {$entityName}: [BubbleContent]{$message}[/BubbleContent]
chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}:[/bold] {$message}
chat-manager-send-ooc-patron-wrap-message = OOC: [color={$patronColor}]{$playerName}[/color]: {$message}
chat-manager-send-ooc-wrap-message = OOC: {$playerName}{$authServer ->
[Unknown] {""}
*[other] @{$authServer}
}: {$message}
chat-manager-send-ooc-patron-wrap-message = OOC: [color={$patronColor}]{$playerName}{$authServer ->
[Unknown] {""}
*[other] @{$authServer}
}[/color]: {$message}

chat-manager-send-dead-chat-wrap-message = {$deadChannelName}: [bold][BubbleHeader]{$playerName}[/BubbleHeader]:[/bold] [BubbleContent]{$message}[/BubbleContent]
chat-manager-send-admin-dead-chat-wrap-message = {$adminChannelName}: [bold]([BubbleHeader]{$userName}[/BubbleHeader]):[/bold] [BubbleContent]{$message}[/BubbleContent]
Expand Down
Loading