[UE5] Online Sessions 101: Host a Room on LAN and Join from Another PC

Created: 2026-07-23Last updated: 2026-09-07

Build a join menu playable from two PCs on the same LAN with UE5's Create, Find, and Join Session. Illustrates passing search results into buttons, recovering from zero results and failures, and the difference between Sessions and Replication, plus the roles Steam and EOS play when extending to the internet.

The multiplayer introduction got the same door opening on two screens. Next you want to join from another PC. That requires building the game's entry points: "host a room" and "find a room".

This article builds a menu where, on two PCs on the same LAN, one hosts a room that appears in the other's list to select and join. A LAN is a network limited to a home, classroom, or similar. Let's connect finding someone and playing together there first.

Another player joining a room the host prepared

What You'll Learn

  • The difference between Sessions and the mechanism syncing in-game state
  • The order in which you use Create, Find, and Join Session
  • How to pass search results into rows with a join button
  • Handling zero results, failures, and leaving so you can test again
  • What roles Steam and EOS play when extending to the internet

The exercise assumes a Blueprint Third Person project and basic UMG work. Steam and EOS setup is covered as an overview later; the hands-on stays on LAN.

Sponsored

Finding a room versus playing in the same world

A Session is the mechanism managing information about the room you play in together. It handles "who is the host", "how many can join", and "where to join". It is not a room placed in a level or the menu shown on screen.

In PIE (Play In Editor) from the Replication introduction, the editor prepared the connection destination. With separately launched games, you build that entry point yourself.

Thinking of room search, network connection, and in-game syncing separately
RoleWhat it handlesExample here
Room informationFind others and choose where to joinSession
Game connectionEstablish a path for communicatingConnecting to a LAN host
In-game syncingAlign character and door statesReplication and RPCs

Joining a Session does not automatically sync arbitrary doors. Conversely, having door syncing working still needs a separate menu for finding where to join.

Split three operations between host and joiner

The host registers the room with Create Session . After it succeeds, it opens the game level with the listen option and waits for other players to connect. That is a Listen Server setup where the host also plays.

The joiner searches with Find Sessions and passes the chosen result to Join Session . The host's Create and the joiner's Find and Join run on their respective PCs.

The host waiting with listen after creating, and the joiner selecting a search result to join

The wiring diagrams below excerpt the part each section explains. Connect surrounding logic and omitted inputs per the text and tables.

These are asynchronous nodes. The result is not out yet right after calling them. Wire result-dependent logic into On Success / On Failure . Do not mix up the exec output right after starting with the outputs on completion.

Null here, versus Steam and EOS

An Online Subsystem is the entry point for calling online features from UE. Search methods and available features change with the service used underneath. Even with identically named nodes, which service you use matters.

OptionIts position hereMain preparation
NullThe first experiment searching LAN roomsChoose Null in the project and align LAN settings
SteamExtending to rooms, invites, and connection features for Steam usersSteamworks, an app ID, UE-side plugin and networking settings
EOSConsidering online features spanning multiple storesProduct setup on Epic's side, authentication, the features you use and networking settings

Null works for experiments finding rooms on a LAN. Turning Use LAN off does not by itself let you search rooms on the internet. Also, "LAN search does not reach" and "whether a direct connection by IP address is possible" are different matters.

Trying Null's LAN search on the same LAN, kept separate from searching to another household

Even on the same Wi-Fi name, guest networks and the like can isolate PCs from each other. We test with two PCs on a LAN that can communicate mutually. Mixing wired and wireless is fine as long as that holds.

Preparation: separate the join menu from the game level

State the LAN settings explicitly

We proceed in a UE5 Blueprint Third Person project. For projects already configured for Steam or EOS, using a copy or a separate prototype project makes settings easier to follow.

Confirm Online Subsystem Null is enabled in "Edit → Plugins". Set the following in the project's Config/DefaultEngine.ini and restart the editor. If the same entry exists, change its value.

[OnlineSubsystem]
DefaultPlatformService=Null

We use the standard Online Subsystem Blueprint nodes here. Do not mix in setup steps for the similarly named "Online Services" or nodes from additional plugins.

Separating L_Menu for hosting rooms from L_Game for two-player play, with DefaultPlatformService set to Null for LAN

Prepare two levels

LevelPurposeSettings
L_MenuThe screen for hosting and findingA new empty level. The menu appears only here
L_GameWhere two people playA duplicate of the Third Person level. Place two Player Starts

L_Game uses the Third Person GameMode. If you have BP_SharedDoor (the door shared by everyone) from the Replication introduction, placing it here also lets you test syncing after joining. For L_Menu, create BP_MenuGameMode with GameMode Base as the parent and set Default Pawn Class to None. Specify it in GameMode Override in L_Menu's World Settings.

Set Game Default Map to L_Menu in Project Settings' "Maps & Modes". Also add L_Menu and L_Game to Packaging's List of maps to include in a packaged build . Levels opened by name with Open Level still need including in the distributed game. The packaging steps are also a reference.

Build the menu and the result row

We build two Widgets. The room list creates a small Widget per entry and stacks them vertically.

Gathering the create and find buttons and ResultsBox in ActionsBox, with results listed as row Widgets

Place the following in a Widget Blueprint WBP_SessionMenu . Turn "Is Variable" on for the parts used from the graph.

NamePartRole
ActionsBoxVertical BoxGroups the two buttons below and ResultsBox
HostButtonButton + Text"Host a room"
FindButtonButton + Text"Find rooms"
ResultsBoxVertical BoxHolds rows for found rooms. Empty at first
StatusTextText"Host or find a room." Placed outside ActionsBox

"Room A" and "Room B" in the diagram are display examples. Actual names come from the search results later.

Placing StatusText and ActionsBox under a Canvas Panel, with the two buttons and ResultsBox as ActionsBox's children, is enough.

In L_Menu's Level Blueprint, wire BeginPlay into Create Widget (Class = WBP_SessionMenu) → Add to Viewport . Pass Get Player Controller (Player Index = 0) into Create Widget's Owning Player and its Return Value into Add to Viewport's Target.

Then call Set Input Mode UI Only . Player Controller is the same Get Player Controller and In Widget to Focus is Create Widget's Return Value. Set that Controller's Show Mouse Cursor to true . Play L_Menu, and seeing the menu and mouse means you are ready.

WBP_SessionRow: one search result

Create a Widget Blueprint WBP_SessionRow and place a Text RoomText inside a Button JoinButton . Make both usable as variables and add these variables.

VariableTypeSettings
SessionResultBlueprint Session ResultInstance Editable and Expose on Spawn on
MenuRefWBP_SessionMenu Object ReferenceInstance Editable and Expose on Spawn on

A Blueprint Session Result is a struct bundling the room information from a search. Join takes this value. A display name alone cannot specify where to join.

Expose on Spawn lets you pass values when creating the Widget. After compiling, SessionResult and MenuRef inputs appear on the Create Widget you place later. MenuRef serves as a reference to "the menu that listed this row".

Each row holding a SessionResult and a MenuRef to the parent menu

In WBP_SessionRow's Event Construct, connect SessionResult's Get into Get Server Name 's Result. Convert the Return Value to Text, pass it into RoomText's Set Text , and wire the exec line from Construct into Set Text. That is the display name from the service, not a room name we authored.

Disable the menu while working

Create a function SetMenuBusy in WBP_SessionMenu with a Boolean input Busy . Call Set Is Enabled from the entry with Target ActionsBox and In Is Enabled taking Busy through a NOT Boolean .

Disabling controls while Busy is true and re-enabling selection when it returns to false

NOT Boolean flips true and false. Setting "working" to true makes "operable" false.

With Busy true, the buttons inside ActionsBox become unpressable. That prevents re-searching mid-search or choosing another room mid-join. StatusText sits outside it, so the explanation stays visible while working.

Create another function FinishMenuAction with a Text input Message . Go from the entry into Set Text with StatusText's Get as Target and Message as In Text. After that call SetMenuBusy (Busy = false, Target = Self). From here, this function bundles the result explanation and re-enabling controls.

Sponsored

Host: create a room and wait for connections

From here we work in WBP_SessionMenu's Event Graph. Wire HostButton's On Clicked into SetMenuBusy (true) → StatusText's Set Text ("Creating room…") → Create Session .

Create Session inputValue
Player ControllerThis Widget's Get Owning Player
Public Connections2 (capacity including the host)
Use LANOn
Use Lobbies if AvailableOff where present. We use LAN Sessions here

Get Owning Player returns the Controller of the player set on this Widget. Specifying Create Widget's Owning Player earlier was for this.

Wire Create Session's On Success into Open Level (by Name) . Level Name is L_Game , Absolute is on, and the expanded Options field takes listen . Do not type quotes or a leading ? .

Going from Create Session's success into a listen Open Level and from failure into restoring menu controls

Wire On Failure into FinishMenuAction (Message = "Could not create a room"). On failure, keep the menu and return it to an operable state.

Create Session alone does not start listening in the play level. Registering the room and then opening L_Game as a Listen Server is the host's full preparation.

In L_Game's Level Blueprint, use Get Player Controller (0) from BeginPlay to call Set Input Mode Game Only and set Show Mouse Cursor to false. That returns each PC's controls to the game. We do not create the menu Widget in L_Game.

Joining side: search for rooms and build the list

Wire FindButton's On Clicked into SetMenuBusy (true) → ResultsBox's Clear Children → StatusText's Set Text ("Searching…") → Find Sessions . Clear Children removes rows displayed previously.

Find Sessions inputValue
Player ControllerGet Owning Player
Max Results20
Use LANOn
Use LobbiesOff where present

A successful search can still return zero

Go from On Success into a Branch with the Condition being whether Results' Length is greater than 0 . Length is the array's count. Create Length from Results, feed its output into an integer > 's A, and set B to 0.

Comparing Results' Length against 0 to check for at least one entry

Connect the comparison's Return Value into the Branch's Condition.

Separate messages for zero results after a successful search and for a failed search

False goes to FinishMenuAction ("No rooms found"). Wire On Failure into a separate FinishMenuAction ("Search failed"). Separate not being able to search from searching and finding nothing.

Pass results into row Widgets one at a time

Wire the Branch's True into For Each Loop and pass Find Sessions' Results into Array. Call Create Widget (Class = WBP_SessionRow) from Loop Body and connect as follows.

Create Widget inputConnected from
Owning PlayerGet Owning Player
SessionResultFor Each Loop's Array Element
MenuRefSelf (WBP_SessionMenu itself)
Passing For Each Loop's result into Create Widget's SessionResult and setting Self and Owning Player too

Then call Add Child to Vertical Box . Target is ResultsBox and Content is Create Widget's Return Value.

Add Child to Vertical Box inputConnected from
Exec inputCreate Widget's exec output
Target (where to add)ResultsBox's Get
Content (what to add)Create Widget's Return Value

Array Element is the one room currently being pulled out. Saving it into the row establishes "pressing this row goes to this room".

Call FinishMenuAction ("Choose a room to join") from For Each Loop's Completed . Controls come back after every row is built. There is no need to call Join Session inside the loop.

Join the room from the selected row

Create a Custom Event JoinSelectedSession in WBP_SessionMenu's Event Graph with an input SelectedResult of type Blueprint Session Result. Because it calls the asynchronous Join Session, it is a Custom Event rather than a normal function. Compile it.

Back in WBP_SessionRow, call JoinSelectedSession from JoinButton's On Clicked. Target is MenuRef and SelectedResult is that row's SessionResult.

Passing SessionResult into MenuRef's JoinSelectedSession from the row's click

In WBP_SessionMenu's JoinSelectedSession, wire SetMenuBusy (true) → StatusText's Set Text ("Joining…") → Join Session . Pass Get Owning Player into Player Controller and the event's SelectedResult into Search Result.

An excerpt passing the event's SelectedResult into Join Session's Search Result

On Success, set StatusText to "Traveling to the host…". The standard Join Session node performs the travel too. Do not re-open L_Game with Open Level on the joining side. That would open a same-named level separately in your own environment instead of the host's.

On Failure, Clear Children on ResultsBox and call FinishMenuAction ("Could not join. Search again."). The host may quit after your search or the room may fill up, so we avoid reusing stale rows. If a Session lingers after a failure, the Destroy Session cleanup below is needed. For first-pass isolation, quitting and relaunching the app also works.

Confirm creation through joining on two PCs

Compile and save every Blueprint and package in a Development configuration. Copy the same output folder set to two PCs on the same LAN.

  1. Launch the game on both and confirm L_Menu's menu appears.
  2. Press "Host a room" on PC A. Moving to L_Game and controlling the character means the host is ready.
  3. Press "Find rooms" on PC B. Select the result row and confirm it travels to L_Game.
  4. Confirm two characters appear on both screens and you can see the other's movement. If you placed the shared door, try opening it from PC B and see both change.
  5. Quit both apps and start over. This time search before hosting and confirm the zero-results display lets you search again.
Separate PCs on a LAN joining the same world

PIE and multiple processes on one PC are useful during development. Those alone cannot confirm searching and networking conditions between separate PCs, though. This article's goal is joining the same room from separate PCs .

Change where you look based on which stage failed

SymptomWhere to check first
Buttons unpressable or no screenWidget creation in L_Menu, Owning Player, the UI input settings
Create or Find itself failsAn active Online Subsystem, the Controller input, errors in the Output Log
Zero search resultsThe host's creation success, Use LAN on both, whether the LAN allows mutual communication
No result rows appearThe Length branch, For Each Loop, Add Child into ResultsBox
Rows appear but joining failsWhether that row's SessionResult was passed, whether the host is listening, whether it is full
Level travel fails in the packaged buildWhether L_Menu and L_Game are in the package, whether Level Name is correct
Joined but only the door does not syncReplication and RPCs beyond Sessions

When another PC cannot find it, check whether the OS firewall permits this game's executable to communicate on the network in use. There is no need to disable it entirely. Also look at guest Wi-Fi client isolation and whether a VPN changed the route.

Sponsored

Leaving, and cleanup when the connection drops

Our minimal test quit the app and started over. When you add a "leave the room" button in game, pair returning the screen with cleaning up the Session .

Use Destroy Session . Called by the host it closes the room; called by a joiner it is the logic for that joiner to leave the Session. A joiner leaving does not delete the host's whole room.

Returning to L_Menu after Destroy Session succeeds and re-enabling controls on failure

From the leave button's On Clicked, disable the button, call Destroy Session, and pass that Widget's Get Owning Player into Player Controller. From On Success, Open Level L_Menu (Absolute on, Options empty). On Failure, display the failure and restore controls so it can be retried.

When the host quits, joiners lose the connection. A successful Join does not guarantee that later communication and travel keep succeeding. GameInstance's Event Network Error and Event Travel Error are entry points for handling disconnections and travel failures. Connect them to logic that displays the error, cleans up the Session, and returns to the menu.

As leaving and error handling grow, gathering them in the GameInstance makes it easier to continue cleanup across level travel. Confirm "you can join" first, then add "you can act again after a disconnect".

Bonus: what to add when extending to the internet

Connecting to a PC in another household means thinking about the network path in addition to room search.

NAT , common on home routers, maps addresses inside the home to an outside address. An unsolicited connection from outside has no mapping saying which PC to deliver to and may not arrive. The techniques for establishing a connection through that are called NAT traversal.

Relay is a method that communicates through an intermediary server. You can consider setups that leave direct connection or relaying to Steam's or EOS's networking features. Confirm separately, though, that rooms becoming searchable and the game's traffic using that path are different things.

The roles of room management via Steam or EOS and of network paths via direct connection or Relay

Steam: the entry point where Steam users gather

Online Subsystem Steam integrates with Steam's rooms, friends, invites, and similar features. You align an AppID identifying the app, settings on the Steamworks side, and UE's plugin and networking method settings.

Turning the plugin on does not complete the friend invite screen and join handling. Session and Lobby settings also differ. Based on the official setup material for your UE version, confirm with a separate account and connection.

EOS: features that can span stores

EOS (Epic Online Services) is a set of services providing room and player management, authentication, P2P networking, and more. Epic's terms allow free use, but that does not mean a full game-server hosting setup is provided free.

You configure a product on Epic's side and build authentication identifying players plus the features you need into the game. It is a candidate for setups spanning multiple stores, but also confirm each platform's terms and the corresponding UE-side integration.

A relay server and a game server are different

Relay forwards traffic. A Dedicated Server , meanwhile, does not play as a player and handles game progression and state computation. It is a setup to consider when you do not want the host's PC at the center of game progression.

Both are "servers" with different roles. Using a dedicated server means preparing its runtime environment and operation too. Choose to fit the join method, player count, and session length your project needs. For projects played on one screen, split screen is another option.

Summary

The host created a room with Create Session and opened L_Game with listen. The joiner passed Find Sessions' results into row Widgets and sent the chosen result to Join Session. Doors and characters after connecting are shared through Replication.

Start by confirming on two PCs on the same LAN that the other's room appears in the list and you can walk together. Being able to trace search, joining, and in-game syncing separately makes it clearer what to add when you later move to Steam or EOS.

Reference: Create Session, Find Sessions, Join Session, Online Session nodes and error handling, Steam networking features.

Unreal Engine Notes in this section98