/// <summary>
        ///     Method run from QNeyHandlerWorldReceiver from message of header WORLD_SERIALIZATION.
        /// </summary>
        private static IEnumerator InternalRunLateClientWorldSerializer(Action onDone)
        {
            var sw = Stopwatch.StartNew();

            // update lading screen
            OnClientLoadingInfoUpdated?.Invoke(true, "LOADING WORLD",
                                               $"DeSerializing {QNetWorldSerializer.SerializedObjectsInMemory} world objects.");

            // load serialized object in memory
            var time = DateTime.Now;
            var isWorldSerializing         = true;
            var worldSerializingLastAction = "not defined";

            QNetWorldSerializer.DeSerializeObjectsInMemory(() => { isWorldSerializing = false; }, action =>
            {
                time = DateTime.Now;
                worldSerializingLastAction = action;
            });
            while (isWorldSerializing && (DateTime.Now - time).Seconds < DeserializingTimeout)
            {
                yield return(new WaitForEndOfFrame());
            }

            if ((DateTime.Now - time).Seconds >= DeserializingTimeout)
            {
                ShutdownInitializing(worldSerializingLastAction);
                yield break;
            }

            LastMapState = QNetMapState.Loaded;
            OnMapStateChanged?.Invoke(QNetMapState.Loaded);

            // update loading screen
            OnClientLoadingInfoUpdated?.Invoke(true, "READY", "Setting up player.");

            GameIsInitializing = false;
            GameInitialized    = true;
            OnClientLoadingEnd?.Invoke();

            // the initialize client
            OnLoadClientSideContent?.Invoke();

            bool isWorldReady = false;

            OnWorldAndNetworkReady?.Invoke(() => { isWorldReady = true; });
            while (!isWorldReady)
            {
                yield return(new WaitForEndOfFrame());
            }

            JEMLogger.Log($"QNetUnity ClientLateRun main work took {sw.Elapsed.Milliseconds:0.00}ms.");
            onDone?.Invoke();
        }
        private static IEnumerator InternalDeInitialize(Action onDone)
        {
            var sw = Stopwatch.StartNew();

            // the initialize client
            OnUnloadClientSideContent?.Invoke();

            // activate loading screen
            OnClientLoadingInfoUpdated?.Invoke(true, "UNLOADING WORLD", "Destroying players.");
            OnClientLoadingStart?.Invoke();

            LastMapState = QNetMapState.Unloading;
            OnMapStateChanged?.Invoke(LastMapState);

            // destroy players
            yield return(QNetPlayer.DestroyAllQNetPlayers());

            // activate loading screen
            OnClientLoadingInfoUpdated?.Invoke(true, "UNLOADING WORLD",
                                               $"Destroying {QNetWorldSerializer.SerializedAndInstancedObjects.Count} objects.");

            // destroy world objects
            var isDestroyingWorldObjects = true;

            QNetWorldSerializer.DestroySerializedObjects(() => { isDestroyingWorldObjects = false; });
            while (isDestroyingWorldObjects)
            {
                yield return(new WaitForEndOfFrame());
            }

            // try to destroy rest of QNet objects just for sure
            if (QNetObjectBehaviour.SpawnedBehaviours.Length != 0)
            {
                JEMLogger.Log(
                    $"QNetUnity find and will destroy {QNetObjectBehaviour.SpawnedBehaviours.Length} additional objects that has been created not by QNetWorldSerializer.");
            }

            while (QNetObjectBehaviour.SpawnedBehaviours.Length > 0)
            {
                QNetObjectBehaviour.InternalDestroy(QNetObjectBehaviour.SpawnedBehaviours[0]);
                yield return(new WaitForEndOfFrame());
            }

            // activate loading screen
            OnClientLoadingInfoUpdated?.Invoke(true, "UNLOADING LEVEL", "Unloading level.");

            // unload world
            var isLevelUnLoading = true;

            QNetLevelLoader.UnLoad(() => { isLevelUnLoading = false; });
            while (isLevelUnLoading)
            {
                yield return(new WaitForEndOfFrame());
            }

            // clear behaviours just for sure
            QNetObjectBehaviour.ClearBehaviours();

            LastMapState = QNetMapState.NotLoaded;
            OnMapStateChanged?.Invoke(LastMapState);

            GameIsDeInitializing = false;
            GameInitialized      = false;
            OnClientLoadingEnd?.Invoke();

            JEMLogger.Log($"QNetUnity DeInitialization main work took {sw.Elapsed.Milliseconds:0.00}ms.");
            onDone?.Invoke();
        }
        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.");
        }