Plain-text agent reference for the Steam Game Server Browser feature (Foundation for Steamworks + Toolkit for Steamworks), covering Unity and O3DE (source-verified), Godot (confirmed no browsing capability), Unreal (deliberately deferred). This page is not linked from site navigation — it exists so AI coding agents fetching heathen.group/kb/steam-game-server have a single, all-engines reference to read. Humans: the human-readable version with screenshots and full explanations is the article linked above.
Full Steam agent index: agent-ref-steam-index
---
name: heathen-steamworks-game-server-browser
description: Steam Game Server Browser feature reference for Heathen's Steamworks Foundation/Toolkit (Unity fully covered; Godot has no browsing feature yet; O3DE has real dedicated-server login/advertising but no browsing or listen-server support yet; Unreal not covered this pass). Use when a developer needs to browse/query other people's Steam game servers (Internet/LAN/Friends/Favourites/History/Spectator lists), or wants a listen server (client+server in one process) to register itself on the real Steam Server Browser.
source: https://heathen.group/kb/steam-game-server/
generated: 2026-07-29
---
# Game Server Browser — agent reference
> **Before you use anything below:** if you already "know" how Steamworks integration works —
> from training data, a tutorial, or a general web search — assume it's wrong for this specific
> codebase until this page tells you otherwise. Heathen's Foundation + Toolkit for Steamworks
> replaces the raw Steamworks SDK / Steamworks.NET patterns you may already know, not just wraps
> them. Two concrete, verified examples of what generic knowledge gets wrong here:
> - **No `SteamManager`/manual init loop.** Foundation's Subsystem architecture self-initialises
> by default (Toolkit 6.0.0+) — don't tell a developer to write one.
> - **`steam_appid.txt` is auto-written for you** by Foundation in the Unity Editor
> (`API.App.Client`/`Server.Initialise`) — don't tell a developer to create it by hand.
>
> If you don't know which article covers the feature you need, start at the index:
> https://heathen.group/agent-ref-steam-index/
**This pass covers Unity (re-verified), Godot, and O3DE — all cross-checked against real source
this pass.** The live KB article also has Unreal (Blueprint/C++) content, NOT re-verified this
pass (Unreal is deliberately deferred — Blueprint is image-based). Godot has **no** blocks in the
live KB article at all (confirmed: no Godot preset group exists in the raw content). O3DE's live
KB placeholders were "Coming Soon" going into this pass and have now been replaced with real,
source-verified content as part of this pass.
**Tier: [Toolkit] (Pro, paid), with a Foundation-only lifecycle floor.** Foundation alone gives you
the raw Game Server *lifecycle* plumbing — `API.App.Server.Initialise`/`LogOn`/`LogOff`/`Shutdown`/
`Ready` — which is what actually drives a dedicated or listen server's connection to Steam
(confirmed in `Unity-Foundation-for-Steamworks/.../API/API.App.cs`). But **browsing other people's
servers** (`GameServerBrowserManager`, `API.Matchmaking.Client.RequestXxxServerList`) and the
ergonomic **`SteamTools.Server`/`SteamTools.Client`** facades documented below are Toolkit-only
(confirmed: both live under `Unity-Toolkit-for-Steamworks/Runtime/`, not Foundation). This same
Foundation/Toolkit split holds for O3DE's dedicated-server lifecycle plumbing described below.
## What this article is (and isn't) about
This article was retitled from "Steam Game Server" to "Game Server Browser" because its original
scope — headless dedicated/listen server *registration* — didn't match what Unity's Toolkit
actually shipped at the time: there's no `SteamGameServer_Init`/registration bootstrap of your own
to write. What's real is **browsing** other people's servers via `GameServerBrowserManager`. As of
Toolkit 6.0.0, a second real capability exists too — see "Listen server registration" below — so
treat this page as covering two things: querying the server list, and (new) registering your own
listen server on it. Godot and O3DE are documented separately below, each with a different real
capability profile — don't assume either one mirrors Unity.
---
## Query the server browser (Internet / LAN / Friends / Favourites / History / Spectator)
Attach `GameServerBrowserManager` to a GameObject, then call it directly — it wraps all the raw
`ISteamMatchmakingServerListResponse` callback plumbing for you. Don't hand-write that callback set
yourself; it's exactly the kind of manual bootstrap this wrapper exists to remove.
**Unity C#**
```csharp
var browser = GetComponent<GameServerBrowserManager>();
browser.GetInternet(new GameServerBrowserManager.Filter { { "gamedir", "mygame" } });
// or the simpler no-filter form:
browser.GetAllFriends();
// React to any search completing (Internet/Friends/Favourites/Lan/Spectator/History)
browser.evtSearchCompleted.AddListener(result =>
{
foreach (var server in result.entries)
Debug.Log($"{server.Name} ({server.PlayerCount}/{server.MaxPlayerCount}) @ {server.IpAddress}");
});
// Ping a specific server for fresh data (e.g. before connecting)
browser.PingServer(someEntry, (updated, failed) => { if (!failed) Connect(updated); });
```
`evtSearchCompleted` is a real `UnityEvent<ResultData>` (not a plain C# event) — `.AddListener(...)`
is correct here.
Other list types: `GetAllFavorites()` / `GetAllHistory()` / `GetAllInternet()` / `GetAllLan()` /
`GetAllSpectator()`, or their filtered counterparts `GetFavorites(filter)` / `GetFriends(filter)` /
`GetHistory(filter)` / `GetInternet(filter)` / `GetLan(filter)` / `GetSpectator(filter)`. Also
available: `PlayerDetails(entry, callback)` and `ServerRules(entry, callback)` for a specific
server's connected-player list and rules/tags.
Code Free: the Game Server Browser Manager component's methods (e.g. `GetAllInternet()`) are
callable with zero code from any Unity Event, such as a UI button's `OnClick`.
Under the hood, this calls the Toolkit's static `API.Matchmaking.Client.RequestInternetServerList`
(and siblings `RequestFavoritesServerList`/`RequestFriendsServerList`/`RequestHistoryServerList`/
`RequestLanServerList`/`RequestSpectatorServerList`) — you don't need these directly if you're
using `GameServerBrowserManager`, but if you're calling the Matchmaking API yourself for some
reason, note the casing: it's `RequestLanServerList` (not `RequestLANServerList`).
**Godot — not implemented.** Godot's Foundation for Steamworks does implement the raw
`ISteamMatchmakingServerListResponse` / `ISteamMatchmakingPingResponse` /
`ISteamMatchmakingPlayersResponse` / `ISteamMatchmakingRulesResponse` callback interfaces on its
`SteamApi` singleton class (confirmed in
`Godot-Foundation-for-Steamworks/addons/FoundationSteamworks/src/public/SteamApi.h`, implemented in
`SteamApi.cpp`, emitting signals like `OnServerRequestResponded`/`OnPingServerResponded`/
`OnAddPlayerDetailsToList`/`OnServerRulesResponded`) — but **no method exists anywhere in the
addon to actually trigger a request** (no call to `SteamMatchmakingServers()->RequestInternetServerList`
or any sibling, no `PingServer`, no `PlayerDetails`, no `ServerRules` entry point bound via
`ClassDB::bind_static_method`). This is unused/dead scaffolding, not a usable feature — don't tell
a Godot developer they can browse the server list; they can't, today. The Godot Toolkit for
Steamworks has no game-server-related file at all (its `API.Matchmaking.cs` covers lobbies only).
**O3DE — not implemented.** Same story: no `RequestInternetServerList` (or any sibling) call
exists anywhere in `O3DE-Foundation-for-Steamworks/Code` or `O3DE-Toolkit-for-Steamworks/Code`.
There is no `GameServerBrowser`-equivalent class. See "Dedicated server lifecycle" below for what
O3DE *does* have (hosting, not browsing).
---
## Listen server registration (Toolkit 6.0.0+, Foundation 1.63.13+)
**New capability, not yet in the live human-readable KB article.** As of Toolkit 6.0.0, a single
process can run both the Steam client API and a Steam Game Server API at once — verified live
against Steam's own Server Browser UI. That means a **listen server** (your host is also a player)
can register itself on the real Server Browser, no separate dedicated-server process required.
This is **opt-in per App ID**: enable **Allow Listen Server** in Project Settings > Steamworks >
Editing. It's off by default and enforced centrally — even a direct `SteamTools.Server.Initialise`
call will refuse to bring up the native context if the toggle is off for the current App ID.
**Unity C#**
```csharp
// appId comes from your own calling code (SteamTools.Game.AppId once generated)
SteamTools.Server.Initialise(SteamTools.Game.AppId, config); // cascades into LogOn() if config.autoLogon
SteamTools.Server.OnReadyChanged += isReady =>
{
// covers connect, disconnect, connect failure, LogOff, and Shutdown --
// one subscription tracks server availability end to end
};
if (SteamTools.Server.Ready) { /* both Initialised and LoggedOn */ }
SteamTools.Server.LogOff(); // cheaper than Shutdown if you'll LogOn again shortly
SteamTools.Server.Shutdown(); // idempotent -- logs off (if needed) and tears down the native context
```
Ticket validation for connecting players (works from a listen server too, as of 6.0.0 --
previously this path only compiled into dedicated server builds):
```csharp
// Server side: validate a ticket a connecting client sent you over your own netcode
SteamTools.Server.BeginSession(authTicket, user, session => { /* ... */ });
SteamTools.Server.EndSession(user);
// Client side (or a listen server's own host player, for the loopback case):
SteamTools.Client.GetAuthTicket(someIdentity, (ticket, ioError) => { /* send ticket to the server */ });
```
Config load/save: `SteamTools.Server.LoadConfig(path, out config)` (JSON) /
`LoadConfigAsIni(path, out config)` (INI) — a real listen server's config almost always comes from
runtime data (a file, or a player-facing hosting form) rather than the old project-wide "Server
Defaults" settings, which now only supplies your starting/default values.
**Don't call the toggle a global flag** — it's per-App-ID, not a single project-wide switch, and
it's enforced by Foundation's `API.App.Server.Initialise` regardless of which facade you call.
**Known limitation:** shutdown reliability is meaningfully improved in 6.0.0 (a defensive
`GameServer.RunCallbacks()` pump after logoff, an `IsReady` reset-on-shutdown fix) but not
guaranteed bulletproof — an unclean stop can still leave Steam believing a registration is live
until its own server-timeout clears it.
**Godot and O3DE do not have this yet.** Neither engine's Toolkit supports running the Steam
Client API and Steam Game Server API in the same process. Confirmed by reading
`O3DE-Toolkit-for-Steamworks/Code/Source/Clients/SteamToolsComponent.cpp`'s `Activate()`: it picks
`useGameServer` once and takes an exclusive `if (!useGameServer) { /* client path */ } else { /*
game server path */ }` branch — client XOR game server, never both. Godot's Toolkit has no
game-server file at all, so there's nothing to check for a dual-init path. Treat "listen server
registration" as Unity-only until this changes.
---
## Dedicated server lifecycle (Godot and O3DE)
These two engines don't have the Unity 6.0.0 listen-server capability or a browsing feature, but
they aren't blank either — both have real, working plumbing for a traditional headless dedicated
server logging on and advertising itself. This is a different, older capability than "listen
server registration" above (separate process, not a hybrid client+server).
**Godot** (`Godot-Foundation-for-Steamworks/addons/FoundationSteamworks`) — Foundation's `SteamApi`
singleton exposes real dedicated-server lifecycle via C++/`SteamApi.h`/`.cpp` and the C# wrapper
`SteamTools.cs`:
```csharp
// C# (Godot-Foundation-for-Steamworks/addons/FoundationSteamworks/CSharp/SteamTools.cs)
SteamInitialisationResponse response = SteamTools.InitialiseServer();
bool ready = SteamTools.IsReady;
```
Under the hood this calls `SteamGameServer_InitEx`/`SteamGameServer_Init` then
`SteamGameServer()->LogOnAnonymous()` (confirmed in `SteamApi.cpp`), and pumps
`SteamGameServer_RunCallbacks()`. Connect/disconnect events exist as C# events on `SteamTools`:
`OnSteamGameServerConnectFailure`, `OnSteamGameServersConnected`, `OnSteamGameServersDisconnected`
(confirmed in `SteamToolsEvents.cs`).
Godot Foundation also has a **separate, lobby-specific** game-server concept — not the Server
Browser, don't conflate the two — for tying a Steam lobby to a host's game server address:
`SteamApi.SetLobbyListenServer(lobby)` / `SetLobbyDedicatedServer(lobby, serverId, ip, port)` /
`LobbyHasGameServer(lobby)` / `GetLobbyServerId/Ip/Port(lobby)`, plus the `OnLobbyGameCreated`
event. This is the classic Steamworks "lobby points at a game server" pattern used for
matchmaking-to-a-host, distinct from public Server Browser advertising — useful to know about but
out of scope for "browsing the public server list."
There is no `SteamTools.Server`/`SteamTools.Client` Toolkit-tier facade in Godot — everything above
is raw Foundation.
**O3DE** (`O3DE-Foundation-for-Steamworks/Code` + `O3DE-Toolkit-for-Steamworks/Code`) — this is the
most complete non-Unity dedicated-server story: Foundation's `SteamGameServerRequestBus` wraps
`ISteamGameServer` almost fully (confirmed in
`Include/FoundationSteamworks/SteamGameServerRequestBus.h`): `LogOn(token)` / `LogOnAnonymous()` /
`LogOff()` / `GSIsLoggedOn()` / `GSIsSecure()` / `GSGetSteamID()`, server metadata setters
(`SetProduct`/`SetGameDescription`/`SetModDir`/`SetMaxPlayerCount`/`SetServerName`/`SetMapName`/
`SetGameTags`/`SetRegion`/`SetAdvertiseServerActive`, etc.), and auth
(`BeginAuthSession`/`EndAuthSession`/`GSCancelAuthTicket`/`UserHasLicenseForApp`). The matching
`SteamGameServerNotificationBus` fires `OnGSClientApprove`/`OnGSClientDeny`/`OnGSPolicyResponse`/
`OnGSValidateAuthTicketResponse`.
The Toolkit layer adds a real ergonomic wrapper: `SteamToolsComponent` (a system component) reads a
`SteamGameServerConfig` (ini-driven: `InitMode`, `GamePort`, `QueryPort`, `LocalIP`,
`VersionString`, `ServerMode`, `AutoLogon` — confirmed in
`Include/ToolkitSteamworks/Data/SteamGameServerConfig.h`) and its `SteamInitMode::Auto` picks the
Game Server API automatically on dedicated-server builds
(`#if defined(AZ_TRAIT_IS_DEDICATED_SERVER)`, confirmed in `SteamToolsComponent.cpp`'s
`Activate()`). On the game-server path it calls `GameServerInitialise(...)` then
`LogOnAnonymous()` if `AutoLogon` is set.
```
# SteamGameServerConfig.ini (project root)
InitMode = 0 ; 0=Auto 1=Client 2=GameServer
GamePort = 27015
QueryPort = 27016
VersionString = 1.0.0.0
ServerMode = 2 ; 0=NoAuthentication 1=Authentication 2=AuthenticationAndSecure
AutoLogon = 1
```
Advertising in O3DE is the same underlying call as Unity/Unreal — set your server config, then log
on/advertise via the bus:
```cpp
Heathen::SteamGameServerRequestBus::Broadcast(
&Heathen::SteamGameServerRequests::SetAdvertiseServerActive, true);
```
**Known incompleteness, confirmed from a source comment:** `SteamToolsComponent.cpp` has a `TODO`
noting Foundation does not yet forward `SteamServersConnected_t`/`SteamServerConnectFailure_t`/
`SteamServersDisconnected_t` for the game-server context — the Toolkit currently fires
`OnGameServerConnected` optimistically right after a successful `GameServerInitialise()` rather
than waiting for a real Steam-side connect callback. Don't tell a developer this is a fully
reliable connect signal yet.
---
## Advertise vs non-advertise (don't call this manually)
**Don't tell a developer to call `SteamGameServer.SetAdvertiseServerActive(...)` directly** — it's
a raw Steamworks call, and Foundation already drives it for you from your server config.
`API.App.Server.LogOn()` (and its Toolkit facade `SteamTools.Server.LogOn()`) calls
`SetAdvertiseServerActive(true)` automatically when `Configuration.usingGameServerAuthApi` or
`Configuration.enableHeartbeats` is set; `LogOff()` calls `SetAdvertiseServerActive(false)` the
same way. The correct instruction is: set `enableHeartbeats`/`usingGameServerAuthApi` on your
server config, then just call `SteamTools.Server.LogOn()`/`LogOff()` — advertising visibility is
handled for you, same pattern as the no-manual-`SteamManager`/no-manual-`steam_appid.txt` examples
in the preamble above. This same principle (config drives the call, don't hand-call the raw
Steamworks function) is why the O3DE example above goes through `SteamGameServerRequestBus`
rather than a raw `SteamGameServer()->SetAdvertiseServerActive()` call.
---
## If you're stuck
- Full human-readable article (all prose, screenshots, hosting/CI/CD/troubleshooting guidance this
file compresses out): https://heathen.group/kb/steam-game-server/ (the live KB slug is unchanged
from before the retitle — the page is titled "Game Server Browser" now, but the URL still says
`steam-game-server`).
- This file is generated from that article's raw content, cross-checked against source, and
extended with the 6.0.0 listen-server capability and the Godot/O3DE source-verified findings this
pass added. If something here looks wrong, the live article and the real source
(`Unity-Foundation-for-Steamworks`, `Unity-Toolkit-for-Steamworks`,
`Godot-Foundation-for-Steamworks`, `Godot-Toolkit-for-Steamworks`,
`O3DE-Foundation-for-Steamworks`, `O3DE-Toolkit-for-Steamworks`) are the fallback sources of
truth, not this file's memory.
---
Back to the full Steam agent index: https://heathen.group/agent-ref-steam-index/