CodeLogic.GameNetQuery 5.0.121

This package has a SemVer 2.0.0 package version: 5.0.121+4885b82.
dotnet add package CodeLogic.GameNetQuery --version 5.0.121
                    
NuGet\Install-Package CodeLogic.GameNetQuery -Version 5.0.121
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CodeLogic.GameNetQuery" Version="5.0.121" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CodeLogic.GameNetQuery" Version="5.0.121" />
                    
Directory.Packages.props
<PackageReference Include="CodeLogic.GameNetQuery" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CodeLogic.GameNetQuery --version 5.0.121
                    
#r "nuget: CodeLogic.GameNetQuery, 5.0.121"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CodeLogic.GameNetQuery@5.0.121
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CodeLogic.GameNetQuery&version=5.0.121
                    
Install as a Cake Addin
#tool nuget:?package=CodeLogic.GameNetQuery&version=5.0.121
                    
Install as a Cake Tool

CodeLogic.GameNetQuery

NuGet License: MIT

Query and administer game servers from CodeLogic 5 — Valve A2S over UDP, Source RCON, and Minecraft GameSpy4 / RCON.

Speaks the wire protocols used by Source-engine and Minecraft servers: A2S over UDP for live server info and player lists, the Source RCON TCP protocol for remote console commands (with high-level Counter-Strike 2 / Counter-Strike: Source admin wrappers), and Minecraft's GameSpy4 UDP query plus RCON. No external NuGet dependencies.

Install

dotnet add package CodeLogic.GameNetQuery

Quick start

The query clients are used directly by namespace — they don't need a library instance. Load the library only when you want its lifecycle and health-check integration.

using CL.GameNetQuery.Valve;
using CL.GameNetQuery.Models;
using CodeLogic.Core.Results;

// A2S UDP — info + players + timing as a CodeLogic Result (no library load required)
Result<ServerQuery> query = await ValveUdpQuery.QueryAsync("203.0.113.10", 27015);
Console.WriteLine(query switch
{
    ServerQuery q => $"{q.Info.Hostname}: {q.Players.Count} players in {q.DurationMs} ms",
    Error e       => $"{e.Code}: {e.Message}",   // e.g. gamenetquery.timeout
    null          => "No result"
});

// Or the simple helpers: null / empty list on failure
ServerInfo? info = await ValveUdpQuery.GetServerInfoAsync("203.0.113.10", 27015);
if (info is { IsOnline: true })
    Console.WriteLine($"{info.Hostname} — {info.Map} ({info.PlayerCount}/{info.MaxPlayers})");

List<PlayerInfo> players = await ValveUdpQuery.GetPlayerListAsync("203.0.113.10", 27015);
foreach (var p in players)
    Console.WriteLine($"{p.Name}  score {p.Score}  {p.Ping}ms");

// Source RCON — remote console command
using var rcon = new ValveRconClient("203.0.113.10", 27015, "rcon-password");
if (await rcon.ConnectAsync())
    Console.WriteLine(await rcon.SendCommandAsync("status"));

To wire the library into CodeLogic's lifecycle (so it participates in health checks):

using CL.GameNetQuery;

await Libraries.LoadAsync<GameNetQueryLibrary>();   // register before ConfigureAsync()
await CodeLogic.ConfigureAsync();
await CodeLogic.StartAsync();

Features

  • Valve A2S UDP query — ValveUdpQuery.QueryAsync returns Result<ServerQuery> (info, players, duration) with typed failures (TimeoutError, UnavailableError, ...); GetServerInfoAsync / GetPlayerListAsync return ServerInfo? and List<PlayerInfo>, with null / an empty list on failure.
  • Source RCON — ValveRconClient connects over TCP and runs console commands verbatim, returning the raw server response string. Never build a raw command from untrusted text.
  • Counter-Strike admin wrappers — CounterStrike2 and CounterStrikeSource expose named helpers (change map, kick/ban by user id or name, say, cvars, restart…) over RCON; CS2 adds cheats, friendly-fire, team-balance, and slay. Arguments are validated before sending, so a player name like x; rcon_password pwned is refused with a ValidationError (gamenetquery.invalid_rcon_argument) instead of running a second command.
  • status parsers — ValveStatusParser and ValveStatusParserCS2 turn raw status output into ServerInfo / PlayerInfo.
  • Minecraft — MinecraftQueryClient (GameSpy4 full-stat UDP query: hostname, map, numplayers, players, ... via GetStatusValue) and MinecraftRconClient (RCON).

Configuration

None. CL.GameNetQuery is a zero-config library — it writes no config file. Every connection parameter (IP, port, RCON password, timeout) is passed directly to the query clients at call time.

Documentation

Full guide: CL.GameNetQuery documentation

Requirements

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.0.121 34 11/10/2026
5.0.118-preview 33 11/8/2026
5.0.117-preview 33 11/7/2026
5.0.116-preview 41 11/1/2026
5.0.115-preview 38 11/1/2026
5.0.113-preview 44 10/31/2026
4.9.98 47 10/25/2026
4.8.93 73 9/23/2026
4.8.87 63 9/13/2026
4.8.85 57 9/12/2026
4.6.72 91 6/20/2026
4.6.69-preview 61 6/20/2026
4.5.2 147 5/24/2026
4.5.2-preview.68 91 6/20/2026
4.5.1 126 5/24/2026
4.5.1-preview.56 82 5/24/2026
4.4.2-preview.53 83 5/24/2026
4.4.1 132 5/24/2026
4.0.5 128 5/15/2026
4.0.4 136 5/9/2026
Loading failed

# CL.GameNetQuery — Changelog

All notable changes to **CodeLogic.GameNetQuery** are documented here. Versions follow
[Semantic Versioning](https://semver.org/).

## 5.0 (2026-11-10)

### Breaking

- **Library ID is `CL.GameNetQuery`** (was `cl.gamenetquery`), like every other library. Its folder is `Libraries/CL.GameNetQuery`; CodeLogic 5 renames an existing `CL.gamenetquery` folder on case-sensitive file systems.

- Targets **.NET 11** (`net11.0`) and requires **CodeLogic 5.0** (`[5.0.0, 6.0.0)`). On .NET 10, stay on
 4.9.x. CodeLogic 5 makes `Result<T>` / `Result` C# 15 unions and `Error` a closed record with
 kind subtypes (`NotFoundError`, `ValidationError`, ...).
- **`QueryResult` removed.** The class (`Success`, `Info?`, `Players`, `Error` string, `DurationMs`,
 `Ok`/`Fail`) is replaced by CodeLogic `Result<ServerQuery>`, where
 `ServerQuery(ServerInfo Info, IReadOnlyList<PlayerInfo> Players, long DurationMs)` is the success value
 and failures are `Error` kinds with `gamenetquery.*` codes.
- **Counter-Strike commands that take caller text return `Task<Result<string>>`** (was `Task<string>`), so an
 unsafe argument can be refused: `ChangeMapAsync`, `KickPlayerAsync`, `BanPlayerAsync`, `UnbanPlayerAsync`,
 `SetHostnameAsync`, `ExecConfigAsync`, `SetCvarAsync`, `GetCvarAsync`, `SayAsync` (both `CounterStrike2` and
 `CounterStrikeSource`) and `CounterStrike2.SlayPlayerAsync`. Read the response from `.Value` after checking
 `IsFailure`. Commands without a text argument (`RestartAsync`, `EnableCheatsAsync`, `SetFriendlyFireAsync(bool)`, ...) still return `Task<string>`.
- `BanPlayerAsync(string)` / `UnbanPlayerAsync(string)` take a user id or SteamID (`123`, `STEAM_1:0:123`,
 `[U:1:123]`, `7656119...`); `banid` / `removeid` never accepted names, and a name is now a validation error.
 The parameter is renamed `playerId`.
- `SetCvarAsync` quotes the value (`mp_roundtime "1.92"`), so values with spaces are sent as one argument.

### Security

- **RCON command injection in the Counter-Strike wrappers.** Caller values were spliced into the console line
 unchecked, so kicking a player named `x; rcon_password pwned` (names come straight from `GetStatusAsync`) ran a
 second command with full RCON rights. Every argument is now checked before anything is sent: free text
 (names, messages, hostnames, cvar values, kick reasons) may not contain `;`, `"`, CR, LF, NUL or any other
 control character; maps and configs must be relative paths of letters, digits and `_ - . /` without `..`;
 cvar names and ids must match their shape. A rejected value is a `ValidationError`
 `gamenetquery.invalid_rcon_argument`. `ValveRconClient.SendCommandAsync` / `MinecraftRconClient.SendCommandAsync`
 stay raw: never pass untrusted text to them.
- **A2S replies are bound to the queried server.** `ValveUdpQuery` connects its UDP socket to the server and
 discards datagrams from any other endpoint, so a third party can no longer answer a query in its place.
 `MinecraftQueryClient` also checks the reply's source and session id.

### Added

- `KickPlayerAsync(int userId, string? reason = null)` (`kickid`) and `BanPlayerAsync(int userId, int minutes = 0)`
 (`banid`) on both Counter-Strike wrappers: act on the user id, which hostile names cannot affect.
- `PlayerInfo.UserId` (`int?`): the user id from RCON `status` (`ValveStatusParser`, `ValveStatusParserCS2`,
 `GetStatusAsync`); `null` from A2S.
- `GameNetQueryErrorCodes`: every `gamenetquery.*` code in one place.

- `ValveUdpQuery.QueryAsync(ip, port, includePlayers = true, timeoutMs = 3000, cancellationToken)` →
 `Task<Result<ServerQuery>>`: A2S_INFO plus (optionally) A2S_PLAYER with the elapsed time. Failures:
 `ValidationError` `gamenetquery.invalid_address` / `gamenetquery.invalid_timeout`, `TimeoutError`
 `gamenetquery.timeout`, `UnavailableError` `gamenetquery.unreachable`, `InternalError`
 `gamenetquery.malformed_response` / `gamenetquery.query_failed`.

### Fixed

- A2S_INFO answers the server's `S2C_CHALLENGE` (sent by Source servers since 2020) instead of parsing
 the challenge packet as server info, and replies are checked for the expected A2S header. Applies to
 `GetServerInfoAsync` / `GetPlayerListAsync` too (a wrong packet now gives `null` / empty, not garbage).
 The UDP socket now matches the address family, so IPv6 addresses work.
- **`ValveRconClient.ConnectAsync` reported success for a wrong password.** It accepted the empty
 `SERVERDATA_RESPONSE_VALUE` that Source servers send before the real answer. It now waits for the
 `SERVERDATA_AUTH_RESPONSE`, requires the request id, and returns `false` for id `-1` (rejected password)
 or when no auth response arrives within `ResponseTimeout`.
- **RCON responses split across TCP reads were dropped.** Both `ValveRconClient` and `MinecraftRconClient`
 now frame packets exactly from a persistent buffer, so a packet split over several segments (or several
 packets in one segment) is reassembled, and a malformed size field closes the connection instead of
 desynchronising it.
- **Multi-packet responses are complete.** `ValveRconClient.SendCommandAsync` returned only the first
 response packet (about 4 KiB, which truncated `status`/`cvarlist` on busy servers). Both clients now send
 an end-of-response marker after each command (Valve: an empty `SERVERDATA_RESPONSE_VALUE`; Minecraft: a
 packet of unknown type 200, answered with "Unknown request") and return every body up to its reply,
 joined in order. Servers that don't answer the marker fall back to `MultiPacketIdleTimeout`.
- Each request uses a fresh id, so a late reply to a timed-out command is skipped instead of being
 returned as the answer to the next one. Calls on one client are serialised. Request bodies and responses
 are UTF-8 (Minecraft used ASCII).
- **`MinecraftQueryClient.QueryServer` never returned the documented keys.** It sent a basic-stat request
 (without the four padding bytes) but parsed the reply as a full stat, so `hostname`, `map`, `numplayers` and
 the rest never appeared. It now sends a full-stat request and parses the reply by its layout; the response
 also has a `players` line (names joined by `", "`), empty values are empty (were `(empty)`), text is decoded
 as UTF-8, and `QueryServer(string, ...)` returns an empty string for a non-IP address instead of throwing.
- `ServerInfo.Version` and `ServerInfo.Tags` are filled from A2S_INFO (version string and `sv_tags` keywords),
 and `CounterStrike2.GetStatusAsync` fills `Version`. `RawResponse` is the raw `status` text for RCON-parsed
 info and empty for A2S.
- A2S strings without a terminator, or starting past the end of the packet, are a
 `gamenetquery.malformed_response` instead of reading out of bounds; a short A2S_PLAYER challenge is too.

### Added

- `ValveRconClient` / `MinecraftRconClient`: `ResponseTimeout` (default 5 s, was a fixed poll of about
 5 s) and `MultiPacketIdleTimeout` (default 500 ms).

### Documentation

- The `QueryAsync` examples in the README and the guide switch on the result union
 (`ServerQuery q => …, TimeoutError => …, Error e => …`). The guide's `ValveRconClient` member table no longer
 breaks before `ResponseTimeout`, and `ValveStatusParserCS2.GetPlayerCount` shows its named tuple.

## 2026-10-24

### Changed

- Requires **CodeLogic 4.9+**. The official libraries move to the 4.9 line together with the framework,
 whose live config changes the stateful libraries now use. No API changes in this library.

## 2026-09-12

### Changed

- Unified the version line with the CodeLogic framework on **4.8.x**. Every official
 library and the framework now share one `major.minor`, so a given `4.8.<patch>`
 means the same generation across all packages.
- `version.txt` moved from `4.6` to `4.8`. The patch component remains the CI run
 number, composed at pack time; `AssemblyVersion` stays pinned at `Major.Minor.0.0`
 (now `4.8.0.0`) so every patch in the line loads interchangeably.

## 2026-06-20

### Documentation

- Full rewrite of the README and the `docs/libs/gamenetquery.md` deep-dive to the current
 CodeLogic house style.
- Clarified that the library is zero-config and that the query clients (`ValveUdpQuery`,
 `ValveRconClient`, `CounterStrike2`/`CounterStrikeSource`, the status parsers, and the
 Minecraft clients) are used directly by namespace — the `GameNetQueryLibrary` instance is
 only needed for lifecycle/health integration.
- Documented that the query clients return model types directly (`ServerInfo?`,
 `List<PlayerInfo>`, response strings) rather than the framework `Result`, including the
 no-throw failure contracts.
- Added field tables for `ServerInfo` and `PlayerInfo`, method tables for both Counter-Strike
 wrappers (shared set vs. CS2-only extras), and the `ValveStatusParser` /
 `ValveStatusParserCS2` parser surfaces.
- No functional code changes.

## [4.5.2] — 2026-06-20

### Documentation

- Documented the `CounterStrike2` / `CounterStrikeSource` admin wrappers, including the
 CS2-only helpers (`UnbanPlayerAsync`, `SetHostnameAsync`, `ExecConfigAsync`, `GetCvarAsync`,
 `EnableCheatsAsync`/`DisableCheatsAsync`, `SetFriendlyFireAsync`, `SetTeamBalanceAsync`,
 `SlayPlayerAsync`, `GetStatusRawAsync`) and the shared command subset.
- Documented the `status` parsers — `ValveStatusParser` (`GetServerAddress`, `GetVersion`,
 `GetTags`, `GetPlayerList`, `ParseStatus`/`ParseStatusWithPlayers`) and `ValveStatusParserCS2`
 (`GetPlayerList`, `GetSpawngroups`).
- Documented the `ValveRconClient(IPAddress, …)` overload, the `ushort` port and `timeoutMs`
 parameters, and the no-throw failure contracts of the UDP/RCON clients.
- Documented the full `ServerInfo`, `PlayerInfo`, and `QueryResult` shapes (with `Ok`/`Fail`).
- Corrected the docs to match the API: `ServerInfo.Hostname` (not `Name`); `QueryResult.DurationMs`
 (not `LatencyMs`) with non-null `Players`; and `MinecraftQueryClient.QueryServer` is synchronous
 and returns key/value text read via `GetStatusValue` (not an awaited `QueryResult`).
- No functional code changes.

## [4.5.0] — 2026-05-24

### Changed

- **Unified versioning.** All CodeLogic.Libs now share a single version line
 controlled by `version.txt` in the repo root. This is a version alignment
 release — no functional changes to this library.
## [4.0.4] — 2026-04-16

### Changed

- README + manifest refresh for the v4 baseline. No functional changes vs 4.0.3.
- `LibraryManifest.Version` now reads from assembly metadata.

## [4.0.2] — 2026-04-09

### Changed

- Aligned with the v4 baseline across all libraries.

## [4.0.0] — 2026-04-09

Major rewrite. Republished as v4.0.0 to reset the version line under the
unified v4 baseline. Valve A2S + Minecraft Server List Ping queries with
typed request/response models and full XML documentation.

### Notes

- Earlier history is retained in the
 [git log](https://github.com/zyntal-com/CodeLogic.Libs/commits/main/CL.GameNetQuery).