AnsonBot/AnsonBot.Core/PalworldPlayer.cs
2026-07-30 20:47:39 +01:00

63 lines
2.1 KiB
C#

namespace AnsonBot.Core;
/// <summary>A player as reported by Palworld's ShowPlayers command.</summary>
public record PalworldPlayer(string Name, string PlayerUid, string SteamId)
{
/// <summary>
/// Stable key for tracking. Prefers SteamId, falls back to PlayerUid, then
/// name. Palworld sometimes reports "00000000" for a uid mid-connection.
/// </summary>
public string Id =>
IsUsable(SteamId) ? SteamId :
IsUsable(PlayerUid) ? PlayerUid :
Name;
private static bool IsUsable(string value) =>
!string.IsNullOrWhiteSpace(value) && value.Trim('0').Length > 0;
}
public static class ShowPlayersParser
{
/// <summary>
/// Parses ShowPlayers output, which looks like:
/// name,playeruid,steamid
/// Sam,1234567890,76561198000000000
/// Malformed or incomplete rows are skipped rather than throwing, because a
/// single bad row shouldn't take down the polling loop.
/// </summary>
public static IReadOnlyList<PalworldPlayer> Parse(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
return Array.Empty<PalworldPlayer>();
var players = new List<PalworldPlayer>();
var lines = raw.Split('\n', StringSplitOptions.RemoveEmptyEntries)
.Select(l => l.Trim('\r', ' '))
.Where(l => l.Length > 0);
foreach (var line in lines)
{
// Skip the header wherever it appears; rcon-cli sometimes echoes it.
if (line.StartsWith("name,", StringComparison.OrdinalIgnoreCase))
continue;
// Names may contain commas, so split from the right: the last two
// fields are uid and steamid, everything before is the name.
var parts = line.Split(',');
if (parts.Length < 3)
continue;
var steamId = parts[^1].Trim();
var uid = parts[^2].Trim();
var name = string.Join(',', parts[..^2]).Trim();
if (string.IsNullOrWhiteSpace(name))
continue;
players.Add(new PalworldPlayer(name, uid, steamId));
}
return players;
}
}