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; } } }