Skip to content

Standalone server: stream map commands only to relevant players#868

Merged
notfood merged 2 commits intorwmt:devfrom
MhaWay:map-catchup-standalone
Apr 7, 2026
Merged

Standalone server: stream map commands only to relevant players#868
notfood merged 2 commits intorwmt:devfrom
MhaWay:map-catchup-standalone

Conversation

@MhaWay
Copy link
Copy Markdown

@MhaWay MhaWay commented Apr 7, 2026

Scope

This change is intentionally limited to the standalone dedicated server path. Local hosting and non-standalone modes keep the previous broadcast behavior.

What changed

  • Added a standalone-only gate (MultiplayerServer.IsStandaloneServer), set only by Source/Server/Server.cs`n- Filtered map-scoped Server_Command packets to players currently on the relevant map
  • When a player reports a map switch via PlayerCount, the standalone server sends a Server_MapResponse resync for the destination map
  • Client reloads the destination map from the updated snapshot when Server_MapResponse arrives
  • Conservative fallback: if a map does not exist in the server snapshot data, the old broadcast path is preserved

Validation

  • dotnet test Source/Tests/Tests.csproj -c Release --no-restore
  • dotnet build Source/Client/Multiplayer.csproj -c Release
  • dotnet build Source/Server/Server.csproj -c Release

Introduce a conservative standalone-only map streaming mode.
Map-scoped commands are filtered to players currently on that map only when
running under the standalone server and when the map exists in the server
snapshot data. When a player reports a map switch, the standalone server sends
a map resync payload for the destination map so the client reloads the latest
snapshot for that map.

Non-standalone hosting keeps the previous broadcast behavior unchanged.
Maps without snapshot data also fall back to the old broadcast path to avoid
breaking maps created after the last joinpoint.
Copilot AI review requested due to automatic review settings April 7, 2026 11:48
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds standalone-dedicated-server-only support for “map streaming” by sending map-scoped Server_Command packets only to players currently on that map, and resyncing a player’s destination map snapshot on PlayerCount map switches.

Changes:

  • Introduce a standalone-only gate (MultiplayerServer.IsStandaloneServer) and helper APIs to enable map streaming + per-player map resync (SendMapResponse).
  • Filter map-scoped command broadcasting in CommandHandler.Send to only players on the relevant map (with a conservative fallback to broadcast).
  • Add tests + a test connection implementation to verify standalone filtering and map-switch resync behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Source/Tests/StandaloneMapStreamingTest.cs New tests covering standalone filtering, non-standalone broadcast behavior, and map-switch resync.
Source/Tests/Helper/RecordingConnection.cs New test connection that records sent packet IDs.
Source/Server/Server.cs Sets IsStandaloneServer = true for the standalone dedicated server entrypoint.
Source/Common/ServerPlayer.cs Adds hasReportedCurrentMap tracking for per-player map filtering behavior.
Source/Common/Networking/State/ServerPlayingState.cs Updates player map tracking on PlayerCount and triggers map resync when applicable.
Source/Common/MultiplayerServer.cs Adds IsStandaloneServer, CanUseStandaloneMapStreaming, and SendMapResponse.
Source/Common/CommandHandler.cs Implements map-scoped filtering for Server_Command in standalone mode.
Source/Client/Networking/State/ClientPlayingState.cs Reloads the destination map from the updated snapshot when Server_MapResponse arrives.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +153 to 158
OnMainThread.Enqueue(() =>
{
var mapsToLoad = Find.Maps.Select(m => m.uniqueID).Append(mapId).Distinct().ToList();
Loader.ReloadGame(mapsToLoad, false, Multiplayer.game?.gameComp.asyncTime ?? false);
});
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

Server_MapResponse is sent via SendFragmented, but the client handler is declared with [PacketHandler(Packets.Server_MapResponse)] (no allowFragmented: true). If the payload exceeds MaxFragmentPacketSize (likely for map snapshots), the receiver will throw Packet ... can't be fragmented. Mark the handler as allowing fragmented packets (or add an explicit fragmented handler) so standalone resync works for real map sizes.

Copilot uses AI. Check for mistakes.
When a standalone-server client is not inside a concrete map yet (world map
or no active map), map-scoped live commands should keep using the previous
broadcast-like behavior for that player. This avoids starving world-view users
of updates until they enter a specific map.

Players who are inside a concrete map still keep the per-map filtering.
@notfood notfood added enhancement New feature or request. 1.6 Fixes or bugs relating to 1.6 (Not Odyssey). standalone server Fix or bugs relating to the standalone server. labels Apr 7, 2026
@notfood notfood merged commit 218390f into rwmt:dev Apr 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.6 Fixes or bugs relating to 1.6 (Not Odyssey). enhancement New feature or request. standalone server Fix or bugs relating to the standalone server.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants