namespace AnsonBot.Core; /// A player as reported by Palworld's ShowPlayers command. public record PalworldPlayer(string Name, string PlayerUid, string SteamId) { /// /// Stable key for tracking. Prefers SteamId, falls back to PlayerUid, then /// name. Palworld sometimes reports "00000000" for a uid mid-connection. /// 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 { /// /// 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. /// public static IReadOnlyList Parse(string raw) { if (string.IsNullOrWhiteSpace(raw)) return Array.Empty(); var players = new List(); 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; } }