using Unity.Collections;
using Unity.Entities;
using Unity.NetCode.HostMigration;
using Unity.Networking.Transport;
using UnityEngine;
namespace Unity.NetCode.Samples.Common
{
public static class HostMigrationHelper
{
///
/// Optional helper method to assist with the operations needed during a host migration process.
/// can also be called directly with a server world which has been manually set up to resume hosting
/// with a given host migration data.
///
/// Takes the given host migration data and starts the host migration process. This starts loading
/// the entity scenes the host previously had loaded (if any). The and in the
/// new server world will be created appropriately, the driver constructor will need to be capable of
/// setting up the relay connection with the given constructor. The local client world will switch from relay to
/// local IPC connection to the server world.
///
/// The network driver constructor registered in the new server world and also in the client world.
/// The data blob containing host migration data, deployed to the new server world.
/// Returns false if there was any immediate failure when starting up the new server
public static bool MigrateDataToNewServerWorld(INetworkStreamDriverConstructor driverConstructor, ref NativeArray migrationData)
{
var oldConstructor = NetworkStreamReceiveSystem.DriverConstructor;
NetworkStreamReceiveSystem.DriverConstructor = driverConstructor;
var serverWorld = ClientServerBootstrap.CreateServerWorld("ServerWorld");
NetworkStreamReceiveSystem.DriverConstructor = oldConstructor;
if (migrationData.Length == 0)
Debug.LogWarning($"No host migration data given during host migration, no data will be deployed.");
else
HostMigrationData.Set(migrationData, serverWorld);
using var serverDriverQuery = serverWorld.EntityManager.CreateEntityQuery(ComponentType.ReadWrite());
var serverDriver = serverDriverQuery.GetSingletonRW();
if (!serverDriver.ValueRW.Listen(NetworkEndpoint.AnyIpv4))
{
Debug.LogError($"NetworkStreamDriver.Listen() failed");
return false;
}
var ipcPort = serverDriver.ValueRW.GetLocalEndPoint(serverDriver.ValueRW.DriverStore.FirstDriver).Port;
// The client driver needs to be recreated, and then directly connected to new server world via IPC
return ConfigureClientAndConnect(ClientServerBootstrap.ClientWorld, driverConstructor, NetworkEndpoint.LoopbackIpv4.WithPort(ipcPort));
}
///
/// Optional helper method to create the client driver with the given driver constructor and connect to the endpoint.
/// The NetworkDriverStore will be recreated as the client can be switching from a local IPC connection to relay
/// connection or reversed, the relay data can be set at driver creation time.
///
/// The client world which needs to be configured.
/// The network driver constructor used for creating a new network driver in the client world.
/// The network endpoint the client will connect to after configuring the network driver.
/// Returns true if the connect call succeeds
public static bool ConfigureClientAndConnect(World clientWorld, INetworkStreamDriverConstructor driverConstructor, NetworkEndpoint serverEndpoint)
{
if (clientWorld == null || !clientWorld.IsCreated)
{
Debug.LogError("HostMigration.ConfigureClientAndConnect: Invalid client world provided");
return false;
}
using var clientNetDebugQuery = clientWorld.EntityManager.CreateEntityQuery(ComponentType.ReadOnly());
var clientNetDebug = clientNetDebugQuery.GetSingleton();
var clientDriverStore = new NetworkDriverStore();
driverConstructor.CreateClientDriver(clientWorld, ref clientDriverStore, clientNetDebug);
using var clientDriverQuery = clientWorld.EntityManager.CreateEntityQuery(ComponentType.ReadWrite());
var clientDriver = clientDriverQuery.GetSingleton();
clientDriver.ResetDriverStore(clientWorld.Unmanaged, ref clientDriverStore);
var connectionEntity = clientDriver.Connect(clientWorld.EntityManager, serverEndpoint);
if (connectionEntity == Entity.Null)
return false;
return true;
}
}
}