Reference · Updated 2552.10.12.21.10

Lobby and map select — spec

Replaces the "host travels straight into the match" shortcut that MainMenuScreen.md §7 deferred. Decided 2026-10-06: the host picks the map inside the lobby, Halo custom-games styl

Replaces the "host travels straight into the match" shortcut that MainMenuScreen.md §7 deferred. Decided 2026-10-06: the host picks the map inside the lobby, Halo custom-games style, not before hosting. This is the LAN shape of the party lobby that MenusAndOnline.md §4.4 sketches for EOS, so Phase 6 mostly swaps the transport rather than the screens.


Main Menu ─ Host Game ─▶ Lobby map (listen server)  ◀─ Join Game (SL-10) lands here

                            │  host: Change Map ─▶ Map Select ─▶ back to Lobby

                            └─ host: Start ─▶ ServerTravel to the chosen map, clients follow


#1. Lobby level

  • Lobby map under /Game/SystemLink/Levels/Lobby/ — a plain room; nobody plays in it.
  • ASLLobbyGameMode — no pawns to fight with, no respawn loop. Owns StartMatch(): host-only,
  • ServerTravel to the selected map's level.

  • ASLLobbyGameState — replicates the selected map. The player list is the base PlayerArray,
  • which already replicates; do not build a parallel one.

  • USLMainMenuWidget::HostMap points at the Lobby map instead of Level1.

⚠ Hard travel, still. MultiplayerPlan.md §4.4 keeps hard travel for Phase 1. Connected clients follow a hard ServerTravel by reconnecting, which is fine on a LAN. Seamless travel (and its transition map) is a later decision, and the loading screen (§4) is what hides either kind.

⚠ Menu input across travel — the main menu → lobby → match chain crosses two hard travels. The fresh- controller restore in ASLPlayerController::BeginPlay (Footguns → Input) is what keeps each arrival live.

#2. Lobby screen — WBP_SL_Lobby (USLLobbyWidget)

  • Player list (one row per PlayerState), the selected map's name and thumbnail.
  • Host only: Change Map, Start. Clients see the same screen without them.
  • Back / B on the host: leave = LeaveMatch, which ends the lobby for everyone — confirm with a modal.
  • Back / B on a client: leave = LeaveMatch, back to the main menu.
  • Testable before Join exists: PIE, Net Mode = Play As Listen Server, 2 players, starting on the Lobby map.

#3. Map select — WBP_SL_MapSelect

  • USLMapDefinition primary data asset per map: display name, level (TSoftObjectPtr<UWorld>),
  • description, thumbnail, max players. The screen lists every one the Asset Manager finds, so adding a map never edits a screen.

  • Choosing a map sets it on the lobby game state (host's server — the host is the server, so no RPC).
  • Level1 is the first entry.

As built (SL-34):

  • USLMapDefinition (Public/Game/) adds SortOrder. Lowest lists first, and a new lobby opens on the
  • first one, so there is no separate default-map setting. Primary asset type SLMap, registered in DefaultGame.ini to scan /Game/SystemLink/Levels — a definition saved elsewhere is not found.

  • ASLLobbyGameState replicates the definition's FPrimaryAssetId, not the asset. An id always
  • arrives; an asset reference arrives null on a client that has not loaded it.

  • A map whose MaxPlayers is below the lobby's current player count is disabled in the list.
  • USLMapSelectWidget fills a plain panel with USLMapSelectEntry buttons, built once. A list view is the
  • rule for variable-length lists, but this is a handful of entries and buttons take pad focus without the list view's workarounds.

  • Picking a map sets it and pops the screen; Back pops it unchanged. The lobby reactivates and refreshes.

#4. Loading screen (after Join)

Hard travel blocks the game thread, so a UMG widget cannot animate during it — the load shows a frozen frame. Spec and decision (Epic's CommonLoadingScreen plugin): LoadingScreen.md (SL-35).


#5. Editor setup for SL-33 — in dependency order

The C++ (ASLLobbyGameMode, ASLLobbyGameState, USLLobbyWidget, USLLobbyPlayerRow, and ASLPlayerHUD::StartupScreenClass) is built. Each step below only references assets made in an earlier one.

  1. WBP_SL_LobbyPlayerRow — parent SLLobbyPlayerRow. One CommonTextBlock named exactly
  2. PlayerNameText.

  1. WBP_SL_Lobby — parent SLLobbyWidget:
    • VerticalBox PlayerListBox
    • CommonTextBlock MapNameText
    • StartButton — a WBP_SL_ButtonMainMenu, labelled Start
    • WBP_SL_CommandBar
    • Get Desired Focus Target override returning StartButton (the screen base will not compile
    • without it)

    • Class Defaults: Player Row Class = WBP_SL_LobbyPlayerRow, Leave Confirm Modal Class =
    • WBP_SL_Modal

  1. BP_SL_LobbyHUD — right-click BP_SL_MenuHUD → Create Child Blueprint Class. Set **Startup
  2. Screen Class** = WBP_SL_Lobby. No graph.

  1. BP_SL_LobbyGameMode in /Game/SystemLink/Levels/Lobby/ — parent SLLobbyGameMode:
    • Player Controller Class = BP_SL_PlayerController (it carries the input contexts)
    • HUD Class = BP_SL_LobbyHUD
  1. Lobby map — File → New Level → Basic, saved as /Game/SystemLink/Levels/Lobby/Lobby (the path
  2. USLMainMenuWidget::HostMap expects). Add one Camera Actor aimed at something; every player views the lobby through it. ⚠ Set its Auto Activate for Player to Player 0. Without it a client drops the camera the server gave it and views from its own pawnless controller (APlayerController::AutoManageActiveCameraTarget).

  1. Lobby map World Settings — GameMode Override = BP_SL_LobbyGameMode.

#6. Testing SL-33

  • Solo: MainMenu map → Host → the Lobby lists you and shows Level1 → Start → Level1, and Jump works.
  • From the lobby, B → confirm → back on the main menu.

  • Two players, no Join screen needed: open the Lobby map, PIE with Net Mode Play As Listen Server
  • and 2 Players. Both windows list both players; the client has no Start; the host's Start takes both to Level1.

#7. Editor setup for SL-34 — in dependency order

The C++ (USLMapDefinition, USLMapSelectEntry, USLMapSelectWidget, and the lobby's map image and Change Map button) is built. Each step only references assets made in an earlier one.

  1. Thumbnails — import one 16:9 image per map (e.g. 1280×720) as T_SL_Map_<Name>. In each texture:
  2. Texture Group = UI, Mip Gen Settings = NoMipmaps.

  1. Second level — the temporary map, saved anywhere under /Game/SystemLink/Levels/.
  1. Map definitions — Content Browser → right-click → Miscellaneous → Data Asset → SLMapDefinition.
  2. One per map, saved under /Game/SystemLink/Levels/ (that folder is what the Asset Manager scans):

    • DA_SL_Map_Level1 — Display Name, Description (a line or two), Level = Level1, Thumbnail, Max Players 8, Sort Order 0
    • DA_SL_Map_<Second> — the same for the temporary level, Sort Order 10
  1. WBP_SL_MapSelectEntry — User Widget, parent SLMapSelectEntry:
    • Style = the same style WBP_SL_ButtonMainMenu uses
    • VerticalBox → SizeBox (320×180) → Image named exactly MapImage; under it a CommonTextBlock named
    • exactly ButtonText

  1. WBP_SL_MapSelect — User Widget, parent SLMapSelectWidget:
    • A WrapBox or HorizontalBox named exactly MapListBox
    • WBP_SL_CommandBar, for the Back prompt
    • Get Desired Focus Target override returning Get Current Map Entry
    • Class Defaults: Entry Class = WBP_SL_MapSelectEntry
  1. WBP_SL_Lobby — add:
    • an Image named exactly MapImage (in a 16:9 SizeBox, e.g. 480×270)
    • a WBP_SL_ButtonMainMenu named exactly ChangeMapButton, labelled Change Map
    • a CommonTextBlock named exactly MapDescriptionText, under the map name, with Auto Wrap Text on
    • Class Defaults: Map Select Screen Class = WBP_SL_MapSelect

⚠ Steps 4–6 will not compile until their named widgets exist — that is the BindWidget check doing its job.

#8. Testing SL-34

  • Solo: Host → the lobby shows Level1's name, description and thumbnail → Change Map → the screen opens with Level1
  • focused → pick the second map → back in the lobby showing it → Start → the second map loads.

  • Back on the map select returns to the lobby with the map unchanged.
  • Pad only: the whole path above, with no mouse.
  • Two players (PIE, Play As Listen Server, 2 players, starting on the Lobby map): the client has neither
  • Change Map nor Start; when the host changes the map, the client's name and thumbnail follow.

  • Max players: set the second map's Max Players to 1, host with two players → its entry is disabled.
  • Set it back afterwards.