Host or Join a Multiplayer Session on LAN
Code and workflows for hosting and/or joining a multiplayer session on LAN, and gracefully handling hosts/clients leaving
What you'll develop on this page

We will add logic to our transition between NavigateScene and MainScene that configures whether or not MainScene loads up ServerWorld and what IP address ClientWorld connects to.
We will also gracefully handle hosts or clients leaving a game.
Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Hosting-Joining-and-Leaving-a-Game
Hosting and joining
First, some background
We have 3 views that could take us to MainScene:
HostGameScreen
JoinGameScreen
ManualConnectScreen

We will update the custom Visual Elements (cVE) of HostGameScreen and ManualConnectScreen views to initially populate their values with system data. "Player Name" and "Game Name" will default to the host name of the machine running the application. We will update JoinGameScreen in the next section, "Broadcasting and Joining on LAN," when we work on broadcasting.
We will also update our LocalGamesFinder script (which we use to populate the table) with two new public variables, "Broadcast Ip Address" and "Broadcast Port." These values will be used in the next section, but we will update our ServerLaunchObject with these values now in this section to avoid doubling back to this flow diagram (which would be annoying for us and not really teach us anything).
We have a single script called "ClientServerLauncher" that handles the callbacks for these 3 views mentioned above. We will update ClientServerLauncher so that it will create the ClientLaunchObject and ServerLaunchObject that currently exist in our MainScene.
We will also update our ClientLaunchObjectData and ServerLaunchObjectData with our new broadcast, game, and player fields. ClientServerConnectionHandler will then pass this data onto our ClientServerInfo object.

We will update ClientDataComponent and ServerDataComponent to hold this additional information. We will also make GameNameComponent to be used by the client to store the game name.
To pass the game name from the server to the client we will update our load game workflow.
Finally, we will create GameOverlayUpdater to update the Game UI on the client with the new game and player information.
Now let's implement
Let's update LocalGamesFinder used by LocalGamesDiscovery in NavigateScene to be the "source of truth" for which IP address and port our server will broadcast UDP packets on
This is similar to how the game port is stored in MainScene in the ClientServerInfo GameObject
Add these lines to LocalGamesFinder.cs. Put them before OnEnable()

Now let's update HostGameScreen to automatically populate data based on host name and IP address
To do this, we will first update the uxml so that the game's IP address is read-only
This way, the host is not able to configure which IP address their machine can bind on
Why didn't we just build it this way in the first place?!
We thought there would be more "oomph" to this tutorial if we point out that a machine cannot configure which IP address they can start a server on in Unity 😉
With the code snippet below, we are updating the HostGameScreen uxml by changing a TextField VisualElement to a Label VisualElement
Paste the code snippet below into HostGameScreen.uxml:

Now let's update the HostGameScreen custom VisualElement (cVE)
We will pull the host name data and place it in both our Game Name field and our Player Name field
We will pull the host IP address and place it in our IP Address label
Paste the code snippet below into HostGameScreen.cs (cVE):

We also need to update our ManualConnectScreen uxml to have default data of 127.0.0.1
Paste the code snippet below into ManualConnectScreen.uxml:

Previously ManualConnectScreen uxml had a "Value" of "HostIPValue" even though the text read "127.0.0.1"
This illustrates that there can be a difference between what TextField "shows" and what "value" is saved
Once you update the TextField the value updates to what text is entered (automatically)
Next, update our ManualConnectScreen cVE
Here we only set our Player Name
We will not automatically set the IP address using any information
We leave the local host IP address as the default to hopefully inform our user that if this address is not updated, the client will try and connect to itself without a running server, which will not work
Paste the code snippet below into ManualConnectScreen.cs (cVE):

With NavigationScene section, hit play
Navigate to the Host Game view and the Manual Connect view

Great, our system data populates in the appropriate fields
Now we need to update the data we will be passing through to MainScene in these scripts:
ClientLaunchObjectData
ServerLaunchObjectData
First start by pasting the code snippet below into ClientLaunchObjectData.cs:
Next, paste this code snippet into ServerLaunchObjectData.cs:

Now go to Main Scene
Drag our ClientLaunchObject and ServerLaunchObject from MainScene into our Scripts and Prefabs folder to make them prefabs
Then delete them from MainScene
We will now be able to reference them in our ClientServerLauncher script

Great, now let's update ClientServerLauncher to grab data from the views and populate them in our launch objects
Paste the code snippet below into ClientServerLauncher.cs:

Now let's drag our ClientLaunchObject and ServerLaunchObject prefabs from the Scripts and Prefabs folder into the appropriate fields in our ClientServerLauncher GameObject in NavigationScene
As a reminder you can find these fields in Inspector when ClientServerLauncher is selected in Hierarchy
Lastly, let's also drag our LocalGamesDiscovery GameObject (in Hierarchy) into the Game Broadcasting field

Let's hit play, navigate to Host Game, click Host, and check it out

Now we are able to create our launch objects and our proper worlds are created 👍
Now let's go to Manual Connect screen and join an IP address

We can see from the logs that we have attempted to connect to the proper IP address
We are now able to take configurations from our NavigationScene and use them to create launch objects that are interpreted by our MainScene
We updated LocalGamesFinder
We updated our HostGameScreen uxml
We updated HostGameScreen and ManualConnectScreen cVEs to populate with default system data
We updated ClientLaunchObjectData and ServerLaunchObject data to take in more configuration data
We turned ClientLaunchObjectData and ServerLaunchObject into prefabs, and removed them from MainScene
We updated ClientServerLauncher to pull data and put them into our launch objects
Updating our Game UI
Now let's update ClientServerInfo to be able to take in the additional information provided by the launch objects
Paste the code snippet below into ClientServerInfo.cs:

Let's also update our ServerDataComponent to take in additional information: game name
We want our server to send the game name data to our client
A client manually connects to an IP address; they must be sent the name
Paste the code snippet below into ServerDataComponent.cs:

Now we will update ClientDataComponent to take in additional information: player name
We will make use of this in the "Scorekeeping" section when the player name needs to be sent to the server in order to keep score
Paste the code snippet below into ClientDataComponent.cs:

We must also create a new component called GameNameComponent in Multiplayer Setup to store the game name on the client
Right-click in Multiplayer Setup > Create > C# Script > name it "GameNameComponent"
It may seem weird to create a component for game name because we already have that data set in ClientServerInfo when hosting a session. However, please remember that we are building both a host build and a client build, and the client will not immediately have this information if they manually connect to an IP address
OK, then why aren't we just putting GameName inside ClientDataComponent? Why are we making a new component? Don't we have enough of these components already?!
Let us explain ourselves: .GameName is a FixedString64Bytes string, which means that it faces a limitation if and when it's used in a component. The limitation of using FixedString in a component is that you cannot "tell" if the value of the FixedString has been set if it's in a component
FixedStringByte's default value is equal to an empty string
So to circumvent this limitation, we need to create an entirely separate component just to store the value of GameName. To see if its value has been set, we check for the existence of the entire component
This mouthful is just to explain and show you that you cannot check to see if a FixedStringByte field has been updated in a component
Paste the code snippet below into GameNameComponent.cs:

Now let's update our ClientServerConnectionHandler to pass through more data from the launch objects to ClientServerInfo as well as ClientDataComponent and ServerDataComponent
Paste the code snippet below into ClientServerConnectionHandler.cs:

Now let's hit play and join through Host Game and Manual Connect and check out the updates to ClientServerInfo
Play around! For example, go ahead and change up the input fields on the view to see the updates in ClientServerInfo (see gif below for ideas)

Now let's update our SendClientGameRpc to include the game name
Paste the code snippet below into SendClientGameRpc:

We need to include the updated information in ServerSendGameSystem
Paste the code snippet below into ServerSendGameSystem.cs:

Now let's update the ClientLoadGameSystem to update ClientDataComponent with the game name. This will update our Game UI with the game name
Paste the code snippet below into ClientLoadGameSystem.cs:

Right-click in the Assets/UI folder and Create a new C# Script named GameOverlayUpdater
GameOverlapUpdater will be responsible for updating our GameUI overlay and pulling the ClientDataComponent and setting ClientServerInfo GameName
It will update the game name and player name shown in the game UI
It will also be responsible for updating player scores
Paste the code snippet below into the newly created GameOverlayUpdater.cs:
We already covered most of the techniques used here (grabbing UI Document, querying for Visual Elements, setting them to values) in the "Creating a ListView" page in the "UI Builder and UI Toolkit" section of the gitbook
So, if what we are doing here in this section is blowing your mind and you don't feel comfortable moving forward, we implore you to visit our earlier section, "UI Builder and UI Toolkit," where we take you step-by-step through UI Builder and UI Toolkit

Let's add GameOverlayUpdater as a component to the GameUI GameObject in MainScene
Click Add Component in Inspector while GameUI is selected in Hierarchy and add GameOverlayUpdater
Now let's drag the GameUI GameObject and the ClientServerInfo GameObjects into the appropriate fields on the component and save the scene, then return to NavigationScene

Let's hit play, join a game, and see how our game UI gets updated

We now have our Game UI updated with player and game information
We updated ClientServerInfo to take in additional data
We updated ServerDataComponent and ClientDataComponent to take in additional data
We created GameNameComponent
We updated ClientServerConnectionHandler to pass through more data from the launch objects
We updated SendClientGameRpc to include the game name
Updated ServerSendGameSystem to add the game name to the RPC
We updated ClientLoadGameSystem to create GameNameComponent when receiving the RPC
We created GameOverLayUpdater to update the Game UI
Updating build configurations
Now that we have 2 scenes, we need to update our build configurations so that we can test how our game responds to clients/hosts leaving games.
Select BaseBuildConfiguration in the Assets/BuildSettings folder in your Project
Go the Inspector. Under "Scene List" click the drop down icon next to "Scene Infos" and click "+ Add Element"
Update Element 0 to be NavigationScene (drag "NavigationScene" from your Scenes folder into the Scene field)
Update Element 1 to be MainScene (drag "MainScene" from your Scenes folder into the Scene field)
Uncheck "Build Current Scene" at the top of Scene List if it isn't already unchecked
Uncheck "Auto Load" for Element 0 if it isn't already unchecked
Click "Apply" at the bottom
Go to File > Build Settings and click the "Player Settings..." button in the bottom left corner
When Player is selected on the left, go to the Resolution and Presentation section and set FullScreen Mode to "Windowed"
This will make it easier for testing
If a scroll bar appears in your window increase the Default Screen Height to 1000px
This sometimes happens on retina screens
Also make the screen resizeable
In your Project folder, go to the BuildSettings folder, click on the file with your development platform name (i.e. macOS) then hit "Build and Run" in Inspector


Open your Unity editor, hit player, and host a game
In your running build manually join the game
In your running build quit the game

Notice that our player has not disappeared from the game when the client disconnected
A floating corpse!
We have updated our build configurations
We updated BaseBuildConfiguration
We updated Player Settings
Leaving a game
These are two ways to handle leaving a game:
Hitting the "Quit Game" button at the top of the game UI
Timing out (through either quitting the app or losing network connectivity)
When a client or server times out, NetCode automatically adds a "NetworkStreamDisconnected" component to the NCE "on the other side" that is still in the game. So if a client times out, the server gets it on their NCE, if a server times out, the client gets it on their NCE.
Network connectionThe network connection uses the Unity Transport package and stores each connection as an entity. Each connection entity has a NetworkStreamConnection component with the
Transporthandle for the connection. The connection also has aNetworkStreamDisconnectedcomponent for one frame, after it disconnects and before the entity is destroyed.To request disconnect, add a
NetworkStreamRequestDisconnectcomponent to the entity. Direct disconnection through the driver is not supported. Your game can mark a connection as being in-game, with theNetworkStreamInGamecomponent. Your game must do this; it is never done automatically.
NetCode provides NetworkStreamDisconnect automatically if a client/server times out. We can also trigger a NetworkStreamDisconnect automatically by adding a NetworkStreamRequestDisconnect tag. If the client or host clicks the "Quit Game" button on-screen, we can add NetworkStreamRequestDisconnect to inform everyone of the departure. The host must tell all clients. The client only needs to tell the server.
If the client notices a server disconnect, they'll be taken back to the NavigationScene as if they clicked the "Quit Game" button.
We are going to "clean" up any disconnected players on the server if a client leaves by deleting their player entity. We will do this by checking for a "NetworkStreamDisconnected" component on any NCEs.
Leaving as a client
When a client leaves, it must tell the server "I am leaving, goodbye!" before it goes. (Irish exiting is a great method of leaving parties, but it's not great for keeping a clean server game 👋)
First, we will add NetworkStreamRequestDisconnect to our client NCE before we leave. This will happen in ClientServerConnectionHandler
This will allow the server to follow a clean-up workflow
We will also update ClientServerConnectionHandler to delete our launch objects
Paste the code snippet below into ClientServerConnectionHandler.cs:

Now we need to create DisconnectSystem in the Server/Systems folder to handle the clean-up of any player entities from disconnected clients
Right-click in the Server/Systems folder > Create > C# Script
Paste the code snippet below into DisconnectSystem.cs:

Find your development platform of-choice in the Assets/BuildSettings folder again, then click Build and Run in Inspector

Now let's host a game in our editor and join the game in the build
Create a player then quit the game on the running build

Great- now our session can handle clients disconnecting
Now host a game in the build and join it in the editor
Quit the game on the running build

We are "stuck" in the game. Pretty lame
Let's fix that
Leaving as a host
If the server disconnects, we want our clients to return to NavigationScene. The clients must be "informed" that the server disconnects similar to how that server was "informed" when a client disconnected.
In ClientServerConnectionHandler, we will put in a check on the client to see if its NCE has a "NetworkStreamDisconnected" tag on it.
Also in ClientServerConnectionHandler, we will add to the ClickedQuitGame() method so that if the host clicks a button, tell all the clients that we disconnected by adding the "NetworkStreamRequestDisconnect" tag.
Paste the code snippet below into ClientServerConnectionHandler.cs and save:
Now again: go back to BuildSettings folder, choose your development platform, then click Build and Run in Inspector

Now Host a Game on the build (the one in the "Sample" window) and the Join the game in the Editor (in Unity)
Click "Quit Game" in the build version

If you are running with a non-zero amount of Thin Clients (in PlayMode Tools) you will notice some harmless errors about sending RPCs. This is because we are not gracefully handling Thin Clients when hosts disconnect mid-game. The Thin Client is inputting "spacebar" as the entities/systems are being destroyed causing RPCs to be sent but with no connections.
We are now able to handle clients or hosts leaving the game
We updated ClientServerConnectionHandler
We created DisconnectSystem to be run by the server
Hitting the "Quit" button on the main menu
Although it may be hard to believe, people might want to quit playing this super basic game 😱so let's add functionality to the "Quit" button at the top of the Title Screen.
Select the TitleScreenUI GameObject in NavigationScene
Add a new script as a component called "QuitButtonHandler"
Move the new script to Assets/UI
Paste the code snippet below into QuitButtonHandler.cs:

Now let's drag the TitleScreenUI GameObject from the Hierarchy into the appropriate field in the QuitButtonHandler component
Let's navigate back to BuildSettings, choose the configuration of your development platform, and Build and Run our game

Hit the "Quit" button at the top right

We are now able to quit the application
We created QuitButtonHandler and added it to TitleScreenUI
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
git checkout 'Hosting-Joining-and-Leaving-a-Game'
Last updated