/// <summary> /// Starts given peer instance. /// </summary> /// <param name="peer">Peer to start.</param> /// <param name="configuration">Configuration of peer.</param> /// <param name="onRegisterHandlers"></param> private static T InternalStartPeer <T>([NotNull] T peer, [NotNull] QNetConfiguration configuration, Action onRegisterHandlers) where T : QNetPeer { if (peer == null) { throw new ArgumentNullException(nameof(peer)); } if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } // first register peer handlers peer.RegisterPeerHandlers(); // then register peer local handlers onRegisterHandlers.Invoke(); // then start peer peer.Start(configuration); // and at the end, add new running peer InternalRunningPeers.Add(peer); // rebuild array RunningPeers = InternalRunningPeers.ToArray(); // clear network time QNetTime.Frame = 0; QNetSimulation.EstimatedServerFrame = 0; return(peer); }
/// <summary> /// Starts local host based on given configuration. /// </summary> public static void StartHost([NotNull] QNetConfiguration configuration) { //if (!Nickname.IsSet) // throw new InvalidOperationException("QNet is unable to start host while nickname is not set."); if (IsHostActive) { throw new InvalidOperationException( "QNet is unable to start host while there is already active instance of host."); } if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } JEMLogger.Log("QNet is starting local host."); IsHostActive = true; StartServer(configuration); StartClient(configuration.IpAddress, configuration.Port, string.Empty); Server.SetUpHostServer(Client.OriginalConnection); Client.OnDisconnection += (lostConnection, reason) => { reason = $"(Client) {reason}"; JEMLogger.Log("QNet is stopping host because of client disconnection."); // stop all peers StopCurrentConnection(reason); }; }
/// <summary> /// Runs host. /// This method starts all server then client loading processes and at the end runs QNetServer and QNetClient peers. /// </summary> public static void RunHost(QNetConfiguration configuration) { if (GameInitialized) { throw new InvalidOperationException( "QNetUnity can't run game initialization process while game is already initialized."); } if (GameIsInitializing) { throw new InvalidOperationException( "QNetUnity can't run game initialization process while other process is already running."); } if (GameIsDeInitializing) { throw new InvalidOperationException( "QNetUnity can't run game initialization while de-initialization process is running."); } JEMLogger.Log("QNetUnity is starting host."); GameIsInitializing = true; Instance.StartCoroutine(InternalRunHost(configuration)); }
private static IEnumerator InternalRunServer(QNetConfiguration configuration) { var sw = Stopwatch.StartNew(); var targetLevel = ServerNextMapName; ServerIsInitializing = true; // load world fist LastMapState = QNetMapState.Loading; OnMapStateChanged?.Invoke(QNetMapState.Loading); var isLevelLoading = true; QNetLevelLoader.Load(targetLevel, () => { isLevelLoading = false; }); while (isLevelLoading) { yield return(new WaitForEndOfFrame()); } OnLevelLoaded?.Invoke(targetLevel); yield return(new WaitForEndOfFrame()); // then load serialized object in memory // TODO: Write objects from save in to memory var time = DateTime.Now; var isWorldSerializing = true; var worldSerializingLastAction = "not defined"; QNetWorldSerializer.DeSerializeObjectsInMemory(() => { isWorldSerializing = false; }, action => { time = DateTime.Now; worldSerializingLastAction = action; }); while (isWorldSerializing) { yield return(new WaitForEndOfFrame()); } if ((DateTime.Now - time).Seconds >= DeserializingTimeout) { ShutdownInitializing(worldSerializingLastAction); yield break; } LastMapState = QNetMapState.Loaded; OnMapStateChanged?.Invoke(QNetMapState.Loaded); // the initialize server QNetManager.StartServer(configuration); GameIsInitializing = false; ServerIsInitializing = false; GameInitialized = true; bool isWorldReady = false; OnWorldAndNetworkReady?.Invoke(() => { isWorldReady = true; }); while (!isWorldReady) { yield return(new WaitForEndOfFrame()); } // we need to call OnNetworkActive event for (var index = 0; index < QNetObjectBehaviour.SpawnedBehaviours.Length; index++) { var obj = QNetObjectBehaviour.SpawnedBehaviours[index]; obj.OnNetworkActive(); } for (var index = 0; index < QNetObjectBehaviour.PredefinedBehaviours.Length; index++) { var obj = QNetObjectBehaviour.PredefinedBehaviours[index]; obj.OnInternalSpawned(); obj.OnNetworkActive(); } JEMLogger.Log($"QNetUnity ServerRun main work took {sw.Elapsed.Milliseconds:0.00}ms."); }
private static IEnumerator InternalRunHost(QNetConfiguration configuration) { // load world first var targetLevel = ServerNextMapName; // activate loading screen OnClientLoadingInfoUpdated?.Invoke(true, "LOADING LEVEL", $"Loading {targetLevel}"); OnClientLoadingStart?.Invoke(); yield return(new WaitForSeconds(0.6f)); // wait some time, lol var sw = Stopwatch.StartNew(); ServerIsInitializing = true; HostIsInitializing = true; LastMapState = QNetMapState.Loading; OnMapStateChanged?.Invoke(LastMapState); var isLevelLoading = true; QNetLevelLoader.Load(targetLevel, () => { isLevelLoading = false; }); while (isLevelLoading) { yield return(new WaitForEndOfFrame()); } OnLevelLoaded?.Invoke(targetLevel); yield return(new WaitForEndOfFrame()); // TODO: Write objects from save in to memory // TODO: Remove block of code below (DeSerializingObjects) // update lading screen OnClientLoadingInfoUpdated?.Invoke(true, "LOADING WORLD", $"DeSerializing {QNetWorldSerializer.SerializedObjectsInMemory} world objects."); var isWorldSerializing = true; var time = DateTime.Now; var worldSerializingLastAction = "not defined"; QNetWorldSerializer.DeSerializeObjectsInMemory(() => { isWorldSerializing = false; }, action => { time = DateTime.Now; worldSerializingLastAction = action; }); while (isWorldSerializing) { yield return(new WaitForEndOfFrame()); } if ((DateTime.Now - time).Seconds >= DeserializingTimeout) { ShutdownInitializing(worldSerializingLastAction); yield break; } LastMapState = QNetMapState.Loaded; OnMapStateChanged?.Invoke(LastMapState); // update loading screen OnClientLoadingInfoUpdated?.Invoke(true, "READY", "Setting up player."); // then initialize host QNetManager.StartHost(configuration); GameIsInitializing = false; ServerIsInitializing = false; HostIsInitializing = false; GameInitialized = true; OnClientLoadingEnd?.Invoke(); // the initialize client OnLoadClientSideContent?.Invoke(); bool isWorldReady = false; OnWorldAndNetworkReady?.Invoke(() => { isWorldReady = true; }); while (!isWorldReady) { yield return(new WaitForEndOfFrame()); } // we need to call OnNetworkActive event for (var index = 0; index < QNetObjectBehaviour.SpawnedBehaviours.Length; index++) { var obj = QNetObjectBehaviour.SpawnedBehaviours[index]; obj.OnNetworkActive(); } for (var index = 0; index < QNetObjectBehaviour.PredefinedBehaviours.Length; index++) { var obj = QNetObjectBehaviour.PredefinedBehaviours[index]; obj.OnInternalSpawned(); obj.OnNetworkActive(); } JEMLogger.Log($"QNetUnity RunHost main work took {sw.Elapsed.Milliseconds:0.00}ms."); }
/// <summary> /// Starts client connection. /// </summary> public static void StartClient([NotNull] string ipAddress, ushort port, string password) { if (IsClientActive) { throw new InvalidOperationException( "QNet is unable to start client while there is already active instance of client."); } if (ipAddress == null) { throw new ArgumentNullException(nameof(ipAddress)); } var configuration = new QNetConfiguration { IpAddress = ipAddress, Port = port, MaxConnections = 2 }; if (OnClientPrepare == null) { throw new NullReferenceException("QNet is unable to start client. OnClientPrepare event is not set."); } Client = new QNetClient(); Client = InternalStartPeer(Client, configuration, () => { QNetHandlerStack.RegisterClientHandlers(Client); OnClientRegisterHeaders?.Invoke(); }); OnClientPrepare.Invoke(out var nickname, out var token); Client.OnMessagePoll += reader => { // as the server always send the server frame // we need to read that right here QNetSimulation.ReceivedServerFrame = reader.ReadUInt32(); QNetSimulation.AdjustServerFrames = QNetSimulation.ReceivedServerFrame > QNetTime.ServerFrame; }; Client.OnConnectionReady += (reader, writer) => { var tickRate = reader.ReadInt32(); var frameNumber = reader.ReadUInt32(); // set TickRate QNetTime.TickRate = tickRate; // initialize server frame count QNetSimulation.ReceivedServerFrame = frameNumber; QNetSimulation.EstimatedServerFrame = frameNumber; // write player data writer.WriteString(nickname); writer.WriteUInt32(token); writer.WriteString(JEMBuild.BuildVersion); OnClientReady?.Invoke(reader, writer); }; Client.OnDisconnection += (lostConnection, reason) => { // stop peer if (InternalRunningPeers.Contains(Client)) { InternalStopPeer(Client, reason); } // update active state IsClientActive = false; // and de-initialize game if (!IsHostActive && QNetGameInitializer.GameInitialized && !QNetGameInitializer.GameIsDeInitializing ) // ignore if host, server will de-initialize it anyway { QNetGameInitializer.DeInitialize(() => { OnClientDisconnected?.Invoke(IsHostActive, lostConnection, reason); }); } else { if (!QNetGameInitializer.GameInitialized && !QNetGameInitializer.GameIsInitializing) { OnClientDisconnected?.Invoke(IsHostActive, lostConnection, reason); } } }; IsClientActive = true; }
/// <summary> /// Starts local server based on given or local configuration. /// </summary> public static void StartServer(QNetConfiguration configuration = null) { if (IsServerActive) { throw new InvalidOperationException( "QNet is unable to start server while there is already active instance of server."); } if (configuration == null) { configuration = new QNetConfiguration(); } var hostIsActive = IsHostActive; Server = new QNetServer(); Server = InternalStartPeer(Server, configuration, () => { QNetHandlerStack.RegisterServerHandlers(Server); OnServerRegisterHeaders?.Invoke(); }); OnServerPrepare?.Invoke(); Server.OnBeforeMessage += message => { // server should always send the server frame to the client! message.Write(QNetTime.ServerFrame); }; Server.OnConnectionAuthorizing += (QNetConnection connection, QNetMessageWriter writer, ref bool refuse) => { writer.WriteInt32(QNetTime.TickRate); writer.WriteUInt32(QNetTime.ServerFrame); OnServerAuthorizePlayer?.Invoke(connection, writer, ref refuse); }; Server.OnConnectionReady += reader => { // read player nickname and create its QNetPlayer instance var nickname = reader.ReadString(); var token = reader.ReadUInt32(); var version = reader.ReadString(); if (JEMBuild.BuildVersion != version) { JEMLogger.LogError( $"Newly received connection don't have right version of the game -> {version} ( is {JEMBuild.BuildVersion} )"); Server.CloseConnection(reader.Connection, "InvalidBuildVersion"); } else { if (IsHostActive && (reader.Connection.ConnectionIdentity == 0 || reader.Connection.ConnectionIdentity == Client.ConnectionIdentity)) { HostClientConnection = reader.Connection; JEMLogger.Log("QNetUnity received host client connection."); } if (QNetPlayer.GetQNetPlayerByToken(token) != null) { JEMLogger.LogError("Newly received connection is using already used token. Disconnecting!"); Server.CloseConnection(reader.Connection, "TokenAlreadyInUse"); return; } var qNetPlayer = QNetPlayer.CreateQNetPlayer(reader.Connection.ConnectionIdentity, nickname, token); if (qNetPlayer == null) { JEMLogger.LogError( "Newly received connection don't have his QNetPlayer instance. Disconnecting!"); Server.CloseConnection(reader.Connection, "InternalQNetPlayerError"); return; } OnServerNewPlayer?.Invoke(qNetPlayer, reader); QNetServerConnectionInit.PrepareNewConnection(reader.Connection); } }; Server.OnConnectionLost += (connection, reason) => { var qNetPlayer = QNetPlayer.GetQNetPlayer(connection); if (qNetPlayer != null) // if QNetPlayer of this connection not exists, just ignore { OnServerPlayerLost?.Invoke(qNetPlayer, reason); // the only thing to do here is to tag player as not ready (if ready) if (qNetPlayer.Ready) { qNetPlayer.TagAsNotReady(); } // and remove QNetPlayer from local machine QNetPlayer.DestroyQNetPlayer(qNetPlayer); } }; Server.OnServerStop += reason => { // server has been stopped, try to de-initialize game if (!ShuttingDownByApplicationQuit) { QNetGameInitializer.DeInitialize(() => { OnServerShutdown?.Invoke(hostIsActive); }); } else { OnServerShutdown?.Invoke(hostIsActive); } IsServerActive = false; IsHostActive = false; }; IsServerActive = true; OnServerStarted?.Invoke(); }