# Network connection management

> Understand different network connection types and configuration options available for multiplayer sessions.

Players can create and join sessions using different network connection types and configuration options.

## Netcode support

The Multiplayer Services SDK integrates with `Netcode for GameObjects` and `Netcode for Entities` out of the box. For custom networking solutions, provide an `INetworHandler` implementation and register it with the session system. Use the `WithNetworkHandler()` extension method to attach your handler to the `SessionOptions` passed to the session operations.
For a more detailed explanation, refer to the [example](#example:-using-a-custom-network-handler).

> **Important:**
>
> Starting with Multiplayer Services SDK version 1.2, the default network handler implementation for Netcode for Entities automatically creates client and server worlds if none are available when starting a network connection through sessions.
>
> Older versions of the MPS SDK require you to create your [Netcode for Entities client and server worlds](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/manual/client-server-worlds.html) before you can create or join a Multiplayer Services session. When you first add the Netcode for Entities package to your project, the [default bootstrap](https://docs.unity3d.com/Packages/com.unity.netcode@latest/index.html?subfolder=/manual/client-server-worlds.html#bootstrap) automatically creates the client and server worlds at startup.
>
> For more advanced use cases, use a [custom network handler](/mps-sdk/manage-session-network-connection.md.md#example:-using-a-custom-network-handler) to override the default integration with Netcode for Entities.

## Types of network

By default, the following network connections types are available with the sessions system:

* Direct network connection.
* Client-hosted solutions by Unity, using [Relay](/relay.md) or [Distributed Authority](https://unity.com/products/distributed-authority). This type is the recommended best practice for peer-to-peer games played over the internet.

Refer to [`Class SessionOptions > Methods`](https://docs.unity3d.com/Packages/com.unity.services.multiplayer@latest/index.html?subfolder=/api/Unity.Services.Multiplayer.SessionOptions.html#extensionmethods) for more information.

Advanced users can provide additional connection types via a custom `INetworkHandler` implementation. Refer to [Using a custom network handler](#example:-using-a-custom-network-handler) for an example.

## Configurable network options

You can override the default network options of your sessions, including both direct and relay-based networking.

If you don't specify network options when calling the `WithRelayNetwork()`,`WithDirectNetwork()`, or `WithDistributedAuthorityNetwork()` methods, the session system automatically applies its default settings to configure the requested network type. For details about default values and behaviors for these methods, refer to [Class SessionOptionsExtensions](https://docs.unity3d.com/Packages/com.unity.services.multiplayer@latest/index.html?subfolder=/api/Unity.Services.Multiplayer.SessionOptionsExtensions.html).

### DirectNetworkOptions

Configure direct network connections with `DirectNetworkOptions`. Use these options with `WithDirectNetwork()` when setting a `SessionOption` to override the default behavior.
The following settings are available:

* ListenIp: The IPv4 or IPv6 address that you monitor for incoming IP connections.
* PublishIp: The IPv4 or IPv6 address to publish for other clients to connect to.
* Port: The port number used for both listening and publishing endpoints.

> **Note:**
>
> When a client-hosted game uses direct networking, the IP addresses of all players are visible to the host. To protect player privacy in client-hosted games, the recommended best practice is to use Relay or Distributed Authority to handle NAT, firewalls, and player IP addresses.

> **Note:**
>
> Not all platforms support IPv6 connections. Only use IPv6 addresses after you verify that your target platforms support it.

### RelayNetworkOptions

Configure relay protocol-based network connections with `RelayNetworkOptions`. Use it with the `WithRelayNetwork()` or `WithDistributedAuthorityNetwork()` when setting `SessionOption` to override the default behavior.

The following settings are available:

* Protocol: Defines the network protocol that the Relay connection uses.
* Region: Forces a specific [Relay region](/relay/locations-and-regions.md) to be used, and skips autoselection from [QoS](/relay/qos.md) results.
* PreserveRegion: If enabled, saves the relay [region](/relay/locations-and-regions.md) to session properties, and reuses it in case of host migration.

The following Relay protocols are supported:

* User Datagram Protocol (UDP)
* Datagram Transport Layer Security (DTLS)
* Secure websocket (WSS)

> **Note:**
>
> The recommended `Default` protocol might vary between platforms.

## Session and Netcode lifecycle separation

Starting with Multiplayer Services SDK version 1.2, you have the option to start the network connection after a session is created. The decoupled network lifecycle gives you greater flexibility and control over your game flow. After the host initiates the network connection, clients are notified and use the connection information shared by the host to start their own network connections. The connection information is shared with clients via session properties.

The session's `Network` property exposes two different interfaces:

* [`IHostSessionNetwork`](https://docs.unity3d.com/Packages/com.unity.services.multiplayer@latest/index.html?subfolder=/api/Unity.Services.Multiplayer.IHostSessionNetwork.html): Lets hosts control the network connection directly - start, stop, and manage it as needed. Available only through `IHostSession`; non-host clients cannot access this interface. To obtain an `IHostSession`, call `session.AsHost()`. This may fail if the caller is not the host.
* [`IClientSessionNetwork`](https://docs.unity3d.com/Packages/com.unity.services.multiplayer@latest/index.html?subfolder=/api/Unity.Services.Multiplayer.IClientSessionNetwork.html): Allows access only to network state and associated events. Can be accessed through the `ISession` interface.

This separation has the following benefits:

* You can delay starting the network connection until certain conditions are met, for example:
  * The host manually starts the game.
  * The session reaches the maximum number of players.
  * All players mark themselves as ready.
* As a host, you can stop the network connection at any time while keeping the session open, and restart it if needed.

## Network connection management examples

The following examples demonstrate how to configure connection types, options, and lifecycle management for the networking solution tied to your session.

### Example: Creating a session with a client-hosted solution with overridden default relay options

The following example demonstrates how to create a session using a Unity client-hosted solution with overridden options provided to the relay allocation:

```csharp
// When the region is specified, it forces the use of a specific Relay region, and skips QoS auto-selection.
async Task StartSessionAsHost(string region = null)
{
    var options = new SessionOptions
    {
      MaxPlayers = 2
    }.WithRelayNetwork(new RelayNetworkOptions(RelayProtocol.Default, region, true)); // or WithDistributedAuthorityNetwork() to use Distributed Authority instead of Relay
    var session = await MultiplayerService.Instance.CreateSessionAsync(options);
    Debug.Log($"Session {session.Id} created! Join code: {session.Code}");
}
```

The points from [Example: Creating a session with a client-hosted solution](./create-session.md#example:-creating-a-session-with-a-client-hosted-solution) also apply to this example, along with the following information:

* The `RelayNetworkOptions` class configures the session to start the relay connection using the platform-suggested `Default` protocol with the provided `region` parameter. If `PreserveRegion` is set to true, the newly-elected host doesn't change the relay allocation region in the event of a host migration. If set to false, the region might change.
* If the region isn't passed as a parameter, the default `null` value triggers a [QoS](/relay/qos.md) query to determine the most optimal region for the relay allocation. For the list of available regions, refer to \[Relay locations and regions]\[RelayRegions].

### Example: Using a custom network handler

Advanced users can specify a custom network handler by implementing [INetworkHandler](https://docs.unity3d.com/Packages/com.unity.services.multiplayer@latest/index.html?subfolder=/api/Unity.Services.Multiplayer.INetworkHandler.html), which overrides the default integration with Netcode for GameObjects and Netcode for Entities. You can provide integration with any third-party networking solutions when implementing the `INetworkHandler` interface.

```csharp
public Task CreateOrJoinSession(string sessionId)
{
    // your own INetworkHandler implementation
    var networkHandler = new CustomEntitiesNetworkHandler();
    var options = new SessionOptions { MaxPlayers = 2 }
    .WithRelayNetwork()
    .WithNetworkHandler(networkHandler);
    
    return MultiplayerServices.Instance.CreateOrJoin(sessionId, options);
}
```

### Example: Starting Netcode at a later time

In some games, you might want to delay starting the network connection and gameplay until certain conditions are met, for example, until all players have joined and are ready.
The following example demonstrates how to delay the connection using the Session API.

```csharp
async Task StartSessionAsHost()
{
    var options = new SessionOptions { MaxPlayers = 4 }; // note the omitted `With<X>Network() call.

    var session = await MultiplayerService.Instance.CreateSessionAsync(options);
    session.PlayerJoined += OnPlayerJoined;
    session.Network.StartFailed += OnNetworkStartFailed;
    session.Network.StateChanged += OnNetworkStateChanged;

    Debug.Log($"Session {session.Id} created! Join code: {session.Code}");

    void OnPlayerJoined(string playerId)
    {
        // verify if we have reached the max player count
        if (session.PlayerCount == session.MaxPlayers)
        {
            var networkOptions
                = new DirectNetworkOptions(ListenIPAddress.AnyIpv4, PublishIPAddress.LoopbackIpv4, 7777);

            // start the network once max player count have been reached
            _ = session.Network.StartDirectNetworkAsync(networkOptions);
        }
    }

    void OnNetworkStateChanged(NetworkState state)
    {
        switch (state)
        {
            case NetworkState.Starting:
                Debug.Log("Starting network...");
                break;
            case NetworkState.Started:
                Debug.Log($"Network has started, listening on: {session.Network.NetworkInfo.ListenAddress}, " +
                          $"and publishing on: {session.Network.NetworkInfo.PublishAddress} created!");
                break;
            default:
                Debug.Log($"[{nameof(StartSessionAsHost)}] Network state has changed! New State:{state:G}");
                break;
        }
    }

    void OnNetworkStartFailed(SessionError error)
    {
        // handle the error
        Debug.LogError($"Network start failed: {error:G}");
    }
}

async Task JoinSessionAsClient(string sessionJoinCode)
{
    // Join the session
    var session = await MultiplayerService.Instance.JoinSessionByCodeAsync(sessionJoinCode);

    Debug.Log($"Network state at the time of connection was: {session.Network.State:G}.");

    session.Network.StateChanged += OnNetworkStateChanged;

    void OnNetworkStateChanged(NetworkState state)
    {
        Debug.Log($"[{nameof(JoinSessionAsClient)}] Network state has changed! New State:{state:G}");
    }
}

```

Consider the following details about how this example works:

* Host(`StartSessionAsHost`):
  * By omitting the call to `WithDirectNetwork()`, `WithRelayNetwork()`, or `WithDistributedAuthorityNetwork()`, the session is created without starting a netcode connection right away.
  * The line `var session = await MultiplayerService.Instance.CreateSessionAsync(options);` creates the session, and makes the player the host.
  * The next three lines subscribe to session lifecycle events: player joining, network state changes, and network start failures.
  * The `OnPlayerJoined` event handler checks if the maximum number of players is reached. In this example, the maximum number is four.
  * After the required number of players has joined the session, `session.Network.StartDirectNetworkAsync(networkOptions)` is called to start the network connection.
  * The `DirectNetworkOptions` in this example are configured to listen on any IPv4 addresses (0.0.0.0), publish on 127.0.0.1 and use port 7777.
  * The `OnNetworkStateChanged` event handler logs network state changes for debugging purposes.
  * The `OnNetworkStartFailed` event handler provides a place to respond to errors if starting the network fails.

* Client(`JoinSessionAsClient`):
  * The line `var session = await MultiplayerService.Instance.JoinSessionByCodeAsync(sessionJoinCode);` joins the session using the provided join code.
  * After joining, the current network state is logged.
  * The `OnNetworkStateChanged` event handler ensures that clients get notified via console logs whenever the underlying network state changes.
