Exemplo n.º 1
0
 public void SendBroadcastIfNeeded()
 {
     if (!IsStatisticsNeeded)
     {
         return;
     }
     using (instance.GetRequestors(out var requestors))
     {
         if (requestors.Count > 0)
         {
             //Export and prepare update packet
             StatisticUpdateDataPacket updatePacket;
             using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
             {
                 ExportCurrentTickData(writer.BinaryWriter);
                 updatePacket = new StatisticUpdateDataPacket(writer.CloseAndGetBytes());
             }
             //Broadcast the update packet to the people with opened statistic window
             foreach (var player in requestors)
             {
                 player.Value.SendPacket(updatePacket);
             }
             ClearCapturedData();
         }
     }
 }
        public override void ProcessPacket(GlobalGameDataRequest packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }

            //Export GameHistoryData, TrashSystem, MilestoneSystem
            //PlanetFactory, Dysonsphere, GalacticTransport will be handle else where

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                GameMain.history.Export(writer.BinaryWriter);
                conn.SendPacket(new GameHistoryDataResponse(writer.CloseAndGetBytes()));
            }

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                GameMain.data.trashSystem.Export(writer.BinaryWriter);
                conn.SendPacket(new TrashSystemResponseDataPacket(writer.CloseAndGetBytes()));
            }

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                GameMain.data.milestoneSystem.Export(writer.BinaryWriter);
                conn.SendPacket(new MilestoneDataResponse(writer.CloseAndGetBytes()));
            }
        }
        public override void ProcessPacket(FactoryLoadRequest packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }

            PlanetData    planet  = GameMain.galaxy.PlanetById(packet.PlanetID);
            PlanetFactory factory = GameMain.data.GetOrCreateFactory(planet);

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                factory.Export(writer.BinaryWriter);
                byte[] data = writer.CloseAndGetBytes();
                Log.Info($"Sent {data.Length} bytes of data for PlanetFactory {planet.name} (ID: {planet.id})");
                conn.SendPacket(new FactoryData(packet.PlanetID, data, planet.data.modData));
            }

            // Add requesting client to connected player, so he can receive following update
            IPlayerManager playerManager = Multiplayer.Session.Network.PlayerManager;
            INebulaPlayer  player        = playerManager.GetSyncingPlayer(conn);

            if (player != null)
            {
                player.Data.LocalPlanetId = packet.PlanetID;
                player.Data.LocalStarId   = GameMain.galaxy.PlanetById(packet.PlanetID).star.id;
                using (playerManager.GetConnectedPlayers(out System.Collections.Generic.Dictionary <INebulaConnection, INebulaPlayer> connectedPlayers))
                {
                    connectedPlayers.Add(player.Connection, player);
                }
            }
        }
Exemplo n.º 4
0
        public override void ProcessPacket(StatisticsRequestEvent packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }

            INebulaPlayer player = playerManager.GetPlayer(conn);

            if (player != null)
            {
                if (packet.Event == StatisticEvent.WindowOpened)
                {
                    Multiplayer.Session.Statistics.RegisterPlayer(conn, player.Id);

                    using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                    {
                        Multiplayer.Session.Statistics.ExportAllData(writer.BinaryWriter);
                        conn.SendPacket(new StatisticsDataPacket(writer.CloseAndGetBytes()));
                    }
                }
                else if (packet.Event == StatisticEvent.WindowClosed)
                {
                    Multiplayer.Session.Statistics.UnRegisterPlayer(player.Id);
                }
            }
        }
Exemplo n.º 5
0
 public void ProcessPacket(TrashSystemRequestDataPacket packet, NebulaConnection conn)
 {
     using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
     {
         GameMain.data.trashSystem.Export(writer.BinaryWriter);
         conn.SendPacket(new TrashSystemResponseDataPacket(writer.CloseAndGetBytes()));
     }
 }
Exemplo n.º 6
0
 public void ProcessPacket(GameHistoryDataRequest packet, NebulaConnection conn)
 {
     using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
     {
         GameMain.history.Export(writer.BinaryWriter);
         conn.SendPacket(new GameHistoryDataResponse(writer.CloseAndGetBytes()));
     }
 }
        public void ProcessPacket(DysonSphereLoadRequest packet, NebulaConnection conn)
        {
            DysonSphere dysonSphere = GameMain.data.CreateDysonSphere(packet.StarIndex);

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                dysonSphere.Export(writer.BinaryWriter);
                conn.SendPacket(new DysonSphereData(packet.StarIndex, writer.CloseAndGetBytes()));
            }
        }
Exemplo n.º 8
0
        public AddEntityPreviewRequest(int planetId, PrebuildData prebuild)
        {
            PlanetId = planetId;

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                prebuild.Export(writer.BinaryWriter);
                PrebuildDataRaw = writer.CloseAndGetBytes();
            }
        }
Exemplo n.º 9
0
 public static void OnClose_Postfix()
 {
     if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsClient)
     {
         using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
         {
             GameMain.mainPlayer.mecha.diyAppearance.Export(writer.BinaryWriter);
             Multiplayer.Session.Network.SendPacket(new PlayerMechaDIYArmor(writer.CloseAndGetBytes(), GameMain.mainPlayer.mecha.diyItems.items.Keys.ToArray(), GameMain.mainPlayer.mecha.diyItems.items.Values.ToArray()));
         }
     }
 }
Exemplo n.º 10
0
 public static void ApplyMechaAppearance_Postfix()
 {
     if (Multiplayer.IsActive)
     {
         using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
         {
             GameMain.mainPlayer.mecha.appearance.Export(writer.BinaryWriter);
             Multiplayer.Session.Network.SendPacket(new PlayerMechaArmor(Multiplayer.Session.LocalPlayer.Id, writer.CloseAndGetBytes()));
         }
     }
 }
Exemplo n.º 11
0
        public void ProcessPacket(FactoryLoadRequest packet, NebulaConnection conn)
        {
            PlanetData    planet  = GameMain.galaxy.PlanetById(packet.PlanetID);
            PlanetFactory factory = GameMain.data.GetOrCreateFactory(planet);

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                factory.Export(writer.BinaryWriter);
                conn.SendPacket(new FactoryData(packet.PlanetID, writer.CloseAndGetBytes()));
            }
            conn.SendPacket(StatisticsManager.instance.GetFactoryPlanetIds());
        }
Exemplo n.º 12
0
        public static byte[] GS2GetSettings()
        {
            byte[] compressedSettings = null;

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                writer.BinaryWriter.Write((String)GS2_GSSettings.GetMethod("Serialize").Invoke(GS2_GSSettings.GetProperty("Instance"), null));
                compressedSettings = writer.CloseAndGetBytes();
            }

            return(compressedSettings);
        }
Exemplo n.º 13
0
        public override void ProcessPacket(MilestoneDataRequest packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                GameMain.data.milestoneSystem.Export(writer.BinaryWriter);
                conn.SendPacket(new MilestoneDataResponse(writer.CloseAndGetBytes()));
            }
        }
Exemplo n.º 14
0
        public override void ProcessPacket(PlanetDataRequest packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }

            Dictionary <int, byte[]> planetDataToReturn = new Dictionary <int, byte[]>();

            foreach (int planetId in packet.PlanetIDs)
            {
                PlanetData planet = GameMain.galaxy.PlanetById(planetId);
                Log.Info($"Returning terrain for {planet.name}");

                // NOTE: The following has been picked-n-mixed from "PlanetModelingManager.PlanetComputeThreadMain()"
                // This method is **costly** - do not run it more than is required!
                // It generates the planet on the host and then sends it to the client

                PlanetAlgorithm planetAlgorithm = PlanetModelingManager.Algorithm(planet);

                if (planet.data == null)
                {
                    planet.data    = new PlanetRawData(planet.precision);
                    planet.modData = planet.data.InitModData(planet.modData);
                    planet.data.CalcVerts();
                    planet.aux = new PlanetAuxData(planet);
                    planetAlgorithm.GenerateTerrain(planet.mod_x, planet.mod_y);
                    planetAlgorithm.CalcWaterPercent();

                    //Load planet meshes and register callback to unload unneccessary stuff
                    planet.wanted    = true;
                    planet.onLoaded += OnActivePlanetLoaded;
                    PlanetModelingManager.modPlanetReqList.Enqueue(planet);

                    if (planet.type != EPlanetType.Gas)
                    {
                        planetAlgorithm.GenerateVegetables();
                        planetAlgorithm.GenerateVeins(false);
                    }
                }

                using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                {
                    planet.ExportRuntime(writer.BinaryWriter);
                    planetDataToReturn.Add(planetId, writer.CloseAndGetBytes());
                }
            }

            conn.SendPacket(new PlanetDataResponse(planetDataToReturn));
        }
Exemplo n.º 15
0
 public CreatePrebuildsRequest(int planetId, List <BuildPreview> buildPreviews, Pose pose)
 {
     PlanetId     = planetId;
     PosePosition = new Float3(pose.position);
     PoseRotation = new Float4(pose.rotation);
     using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
     {
         writer.BinaryWriter.Write(buildPreviews.Count);
         for (int i = 0; i < buildPreviews.Count; i++)
         {
             SerializeBuildPreview(buildPreviews[i], buildPreviews, writer.BinaryWriter);
         }
         BuildPreviewData = writer.CloseAndGetBytes();
     }
 }
Exemplo n.º 16
0
 public void ProcessPacket(StorageSyncRequestPacket packet, NebulaConnection conn)
 {
     if (GameMain.galaxy.PlanetById(packet.PlanetId) != null)
     {
         StorageComponent storageComponent = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory?.factoryStorage?.storagePool[packet.StorageId];
         if (storageComponent != null)
         {
             using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
             {
                 storageComponent.Export(writer.BinaryWriter);
                 conn.SendPacket(new StorageSyncResponsePacket(packet.PlanetId, packet.StorageId, writer.CloseAndGetBytes()));
             }
         }
     }
 }
Exemplo n.º 17
0
 public CreatePrebuildsRequest(int planetId, List <BuildPreview> buildPreviews, int playerId, string buildToolType)
 {
     AuthorId      = playerId;
     PlanetId      = planetId;
     BuildToolType = buildToolType;
     using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
     {
         writer.BinaryWriter.Write(buildPreviews.Count);
         for (int i = 0; i < buildPreviews.Count; i++)
         {
             SerializeBuildPreview(buildPreviews[i], buildPreviews, writer.BinaryWriter);
         }
         BuildPreviewData = writer.CloseAndGetBytes();
     }
 }
Exemplo n.º 18
0
        public override void ProcessPacket(DysonSphereLoadRequest packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }
            switch (packet.Event)
            {
            case DysonSphereRequestEvent.List:
                using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                {
                    List <int> list = new List <int>();
                    for (int i = 0; i < GameMain.data.dysonSpheres.Length; ++i)
                    {
                        if (GameMain.data.dysonSpheres[i] != null)
                        {
                            list.Add(i);
                        }
                    }
                    writer.BinaryWriter.Write(list.Count);
                    foreach (int starIndex in list)
                    {
                        writer.BinaryWriter.Write(starIndex);
                    }
                    conn.SendPacket(new DysonSphereData(packet.StarIndex, writer.CloseAndGetBytes(), DysonSphereRespondEvent.List));
                }
                break;

            case DysonSphereRequestEvent.Load:
                DysonSphere dysonSphere = GameMain.data.CreateDysonSphere(packet.StarIndex);
                using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                {
                    dysonSphere.Export(writer.BinaryWriter);
                    byte[] data = writer.CloseAndGetBytes();
                    Log.Info($"Sent {data.Length} bytes of data for DysonSphereData (INDEX: {packet.StarIndex})");
                    conn.SendPacket(new DysonSphereData(packet.StarIndex, data, DysonSphereRespondEvent.Load));
                    Multiplayer.Session.DysonSpheres.RegisterPlayer(conn, packet.StarIndex);
                }
                break;

            case DysonSphereRequestEvent.Unload:
                Multiplayer.Session.DysonSpheres.UnRegisterPlayer(conn, packet.StarIndex);
                break;
            }
        }
 public TrashSystemNewTrashCreatedPacket(int trashId, TrashObject trashObj, TrashData trashData, ushort playerId, int localPlanetId)
 {
     TrashId = trashId;
     using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
     {
         trashObj.Export(writer.BinaryWriter);
         TrashObjectByte = writer.CloseAndGetBytes();
     }
     using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
     {
         trashData.Export(writer.BinaryWriter);
         TrashDataByte = writer.CloseAndGetBytes();
     }
     // Fix overflow in TrashObj.Export() for item.count
     Count         = trashObj.count;
     LocalPlanetId = localPlanetId;
     PlayerId      = playerId;
 }
Exemplo n.º 20
0
        private static byte[] PlanetCompute(int planetId) 
        {
            PlanetData planet = GameMain.galaxy.PlanetById(planetId);
            HighStopwatch highStopwatch = new HighStopwatch();
            highStopwatch.Begin();

            // NOTE: The following has been picked-n-mixed from "PlanetModelingManager.PlanetComputeThreadMain()"
            // This method is **costly** - do not run it more than is required!
            // It generates the planet on the host and then sends it to the client

            PlanetAlgorithm planetAlgorithm = PlanetModelingManager.Algorithm(planet);

            if (planet.data == null)
            {
                planet.data = new PlanetRawData(planet.precision);
                planet.modData = planet.data.InitModData(planet.modData);
                planet.data.CalcVerts();
                planet.aux = new PlanetAuxData(planet);
                planetAlgorithm.GenerateTerrain(planet.mod_x, planet.mod_y);
                planetAlgorithm.CalcWaterPercent();

                //Load planet meshes and register callback to unload unneccessary stuff
                planet.wanted = true;
                planet.onLoaded += OnActivePlanetLoaded;
                PlanetModelingManager.modPlanetReqList.Enqueue(planet);

                if (planet.type != EPlanetType.Gas)
                {
                    planetAlgorithm.GenerateVegetables();
                    planetAlgorithm.GenerateVeins(false);
                }
            }

            byte[] data;
            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                planet.ExportRuntime(writer.BinaryWriter);
                data = writer.CloseAndGetBytes();
            }
            Log.Info($"Returning terrain for {planet.name} (id:{planet.id} time:{highStopwatch.duration:F4}s)");
            return data;
        }
Exemplo n.º 21
0
        public void ProcessPacket(StatisticsRequestEvent packet, NebulaConnection conn)
        {
            Player player = playerManager.GetPlayer(conn);

            if (player != null)
            {
                if (packet.Event == StatisticEvent.WindowOpened)
                {
                    StatisticsManager.instance.RegisterPlayer(conn, player.Id);

                    using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                    {
                        StatisticsManager.ExportAllData(writer.BinaryWriter);
                        conn.SendPacket(new StatisticsDataPacket(writer.CloseAndGetBytes()));
                    }
                }
                else if (packet.Event == StatisticEvent.WindowClosed)
                {
                    StatisticsManager.instance.UnRegisterPlayer(player.Id);
                }
            }
        }
Exemplo n.º 22
0
        public LobbyRequest(byte[] clientCert, string username, Float4[] mechaColors)
        {
            Username    = username;
            MechaColors = mechaColors;

            using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
            {
                int count = 0;
                foreach (System.Collections.Generic.KeyValuePair <string, BepInEx.PluginInfo> pluginInfo in BepInEx.Bootstrap.Chainloader.PluginInfos)
                {
                    if (pluginInfo.Value.Instance is IMultiplayerMod mod)
                    {
                        writer.BinaryWriter.Write(pluginInfo.Key);
                        writer.BinaryWriter.Write(mod.Version);
                        count++;
                    }
                    else
                    {
                        foreach (BepInEx.BepInDependency dependency in pluginInfo.Value.Dependencies)
                        {
                            if (dependency.DependencyGUID == NebulaModAPI.API_GUID)
                            {
                                writer.BinaryWriter.Write(pluginInfo.Key);
                                writer.BinaryWriter.Write(pluginInfo.Value.Metadata.Version.ToString());
                                count++;
                            }
                        }
                    }
                }

                ModsVersion = writer.CloseAndGetBytes();
                ModsCount   = count;
            }

            GameVersionSig = GameConfig.gameVersion.sig;
            ClientCert     = clientCert;
        }
Exemplo n.º 23
0
        public override void ProcessPacket(SyncComplete packet, NebulaConnection conn)
        {
            if (IsHost)
            {
                INebulaPlayer player = playerManager.GetSyncingPlayer(conn);
                if (player == null)
                {
                    Log.Warn("Received a SyncComplete packet, but no player is joining.");
                    Multiplayer.Session.World.OnAllPlayersSyncCompleted();
                    return;
                }

                // store the player now, not when he enters the lobby. that would cause weird teleportations when clients reenter the lobby without ever having loaded into the game
                string clientCertHash = CryptoUtils.Hash(packet.ClientCert);
                using (playerManager.GetSavedPlayerData(out Dictionary <string, IPlayerData> savedPlayerData))
                {
                    if (!savedPlayerData.TryGetValue(clientCertHash, out IPlayerData value))
                    {
                        savedPlayerData.Add(clientCertHash, player.Data);
                    }
                }

                // Should these be locked together?

                int syncingCount;
                using (playerManager.GetSyncingPlayers(out System.Collections.Generic.Dictionary <INebulaConnection, INebulaPlayer> syncingPlayers))
                {
                    bool removed = syncingPlayers.Remove(player.Connection);
                    syncingCount = syncingPlayers.Count;
                }

                using (playerManager.GetConnectedPlayers(out System.Collections.Generic.Dictionary <INebulaConnection, INebulaPlayer> connectedPlayers))
                {
                    if (!connectedPlayers.ContainsKey(player.Connection))
                    {
                        connectedPlayers.Add(player.Connection, player);
                    }
                }

                // Load overriden Planet and Star names
                player.SendPacket(new NameInputPacket(GameMain.galaxy, Multiplayer.Session.LocalPlayer.Id));

                // Since the player is now connected, we can safely spawn his player model
                Multiplayer.Session.World.SpawnRemotePlayerModel(player.Data);

                if (syncingCount == 0)
                {
                    IPlayerData[] inGamePlayersDatas = playerManager.GetAllPlayerDataIncludingHost();
                    playerManager.SendPacketToAllPlayers(new SyncComplete(inGamePlayersDatas));

                    // Since the host is always in the game he could already have changed his mecha armor, so send it to the new player.
                    using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                    {
                        GameMain.mainPlayer.mecha.appearance.Export(writer.BinaryWriter);
                        player.SendPacket(new PlayerMechaArmor(Multiplayer.Session.LocalPlayer.Id, writer.CloseAndGetBytes()));
                    }

                    // if the client had used a custom armor we should have saved a copy of it, so send it back
                    if (player.Data.Appearance != null)
                    {
                        using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                        {
                            player.Data.Appearance.Export(writer.BinaryWriter);
                            playerManager.SendPacketToAllPlayers(new PlayerMechaArmor(player.Id, writer.CloseAndGetBytes()));
                        }

                        // and load custom appearance on host side too
                        // this is the code from PlayerMechaArmonrProcessor
                        using (Multiplayer.Session.World.GetRemotePlayersModels(out Dictionary <ushort, RemotePlayerModel> remotePlayersModels))
                        {
                            if (remotePlayersModels.TryGetValue(player.Id, out RemotePlayerModel playerModel))
                            {
                                if (playerModel.MechaInstance.appearance == null)
                                {
                                    playerModel.MechaInstance.appearance = new MechaAppearance();
                                    playerModel.MechaInstance.appearance.Init();
                                }
                                player.Data.Appearance.CopyTo(playerModel.MechaInstance.appearance);
                                playerModel.PlayerInstance.mechaArmorModel.RefreshAllPartObjects();
                                playerModel.PlayerInstance.mechaArmorModel.RefreshAllBoneObjects();
                                playerModel.MechaInstance.appearance.NotifyAllEvents();
                                playerModel.PlayerInstance.mechaArmorModel._Init(playerModel.PlayerInstance);
                                playerModel.PlayerInstance.mechaArmorModel._OnOpen();
                            }
                        }
                    }

                    // if the client has some changes made in his mecha editor send them back for them to load
                    if (player.Data.DIYAppearance != null)
                    {
                        using (BinaryUtils.Writer writer = new BinaryUtils.Writer())
                        {
                            player.Data.DIYAppearance.Export(writer.BinaryWriter);
                            player.SendPacket(new PlayerMechaDIYArmor(writer.CloseAndGetBytes(), player.Data.DIYItemId, player.Data.DIYItemValue));
                        }
                    }

                    // add together player sand count and tell others if we are syncing soil
                    if (Config.Options.SyncSoil)
                    {
                        GameMain.mainPlayer.sandCount += player.Data.Mecha.SandCount;
                        Multiplayer.Session.Network.SendPacket(new PlayerSandCount(GameMain.mainPlayer.sandCount));
                    }

                    Multiplayer.Session.World.OnAllPlayersSyncCompleted();
                }
            }
            else // IsClient
            {
                // Everyone is now connected, we can safely spawn the player model of all the other players that are currently connected
                foreach (NebulaModel.DataStructures.PlayerData playerData in packet.AllPlayers)
                {
                    if (playerData.PlayerId != Multiplayer.Session.LocalPlayer.Id)
                    {
                        Multiplayer.Session.World.SpawnRemotePlayerModel(playerData);
                    }
                }

                Multiplayer.Session.World.OnAllPlayersSyncCompleted();
            }
        }
Exemplo n.º 24
0
        public override void ProcessPacket(LobbyRequest packet, NebulaConnection conn)
        {
            if (IsClient)
            {
                return;
            }

            INebulaPlayer player;

            using (playerManager.GetPendingPlayers(out Dictionary <INebulaConnection, INebulaPlayer> pendingPlayers))
            {
                if (!pendingPlayers.TryGetValue(conn, out player))
                {
                    conn.Disconnect(DisconnectionReason.InvalidData);
                    Log.Warn("WARNING: Player tried to enter lobby without being in the pending list");
                    return;
                }

                if (GameMain.isFullscreenPaused)
                {
                    conn.Disconnect(DisconnectionReason.HostStillLoading);
                    pendingPlayers.Remove(conn);

                    return;
                }

                Dictionary <string, string> clientMods = new Dictionary <string, string>();

                using (BinaryUtils.Reader reader = new BinaryUtils.Reader(packet.ModsVersion))
                {
                    for (int i = 0; i < packet.ModsCount; i++)
                    {
                        string guid    = reader.BinaryReader.ReadString();
                        string version = reader.BinaryReader.ReadString();

                        if (!BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey(guid))
                        {
                            conn.Disconnect(DisconnectionReason.ModIsMissingOnServer, guid);
                            pendingPlayers.Remove(conn);

                            return;
                        }

                        clientMods.Add(guid, version);
                    }
                }

                foreach (KeyValuePair <string, BepInEx.PluginInfo> pluginInfo in BepInEx.Bootstrap.Chainloader.PluginInfos)
                {
                    if (pluginInfo.Value.Instance is IMultiplayerMod mod)
                    {
                        if (!clientMods.ContainsKey(pluginInfo.Key))
                        {
                            conn.Disconnect(DisconnectionReason.ModIsMissing, pluginInfo.Key);
                            pendingPlayers.Remove(conn);

                            return;
                        }

                        string version = clientMods[pluginInfo.Key];

                        if (mod.CheckVersion(mod.Version, version))
                        {
                            continue;
                        }

                        conn.Disconnect(DisconnectionReason.ModVersionMismatch, $"{pluginInfo.Key};{version};{mod.Version}");
                        pendingPlayers.Remove(conn);

                        return;
                    }
                    else
                    {
                        foreach (BepInEx.BepInDependency dependency in pluginInfo.Value.Dependencies)
                        {
                            if (dependency.DependencyGUID == NebulaModAPI.API_GUID)
                            {
                                string hostVersion = pluginInfo.Value.Metadata.Version.ToString();
                                if (!clientMods.ContainsKey(pluginInfo.Key))
                                {
                                    conn.Disconnect(DisconnectionReason.ModIsMissing, pluginInfo.Key);
                                    pendingPlayers.Remove(conn);
                                    return;
                                }
                                if (clientMods[pluginInfo.Key] != hostVersion)
                                {
                                    conn.Disconnect(DisconnectionReason.ModVersionMismatch, $"{pluginInfo.Key};{clientMods[pluginInfo.Key]};{hostVersion}");
                                    pendingPlayers.Remove(conn);
                                    return;
                                }
                            }
                        }
                    }
                }

                if (packet.GameVersionSig != GameConfig.gameVersion.sig)
                {
                    conn.Disconnect(DisconnectionReason.GameVersionMismatch, $"{ packet.GameVersionSig };{ GameConfig.gameVersion.sig }");
                    pendingPlayers.Remove(conn);

                    return;
                }
            }

            bool isNewUser = false;

            //TODO: some validation of client cert / generating auth challenge for the client
            // Load old data of the client
            string clientCertHash = CryptoUtils.Hash(packet.ClientCert);

            using (playerManager.GetSavedPlayerData(out Dictionary <string, IPlayerData> savedPlayerData))
            {
                if (savedPlayerData.TryGetValue(clientCertHash, out IPlayerData value))
                {
                    using (playerManager.GetConnectedPlayers(out Dictionary <INebulaConnection, INebulaPlayer> connectedPlayers))
                    {
                        IPlayerData playerData = value;
                        foreach (INebulaPlayer connectedPlayer in connectedPlayers.Values)
                        {
                            if (connectedPlayer.Data == playerData)
                            {
                                playerData = value.CreateCopyWithoutMechaData();
                                Log.Warn($"Copy playerData for duplicated player{playerData.PlayerId} {playerData.Username}");
                            }
                        }
                        player.LoadUserData(playerData);
                    }
                }
                else
                {
                    // store player data once he fully loaded into the game (SyncCompleteProcessor)
                    isNewUser = true;
                }
            }

            // Add the username to the player data
            player.Data.Username = !string.IsNullOrWhiteSpace(packet.Username) ? packet.Username : $"Player {player.Id}";

            // Add the Mecha Color to the player data
            player.Data.MechaColors = packet.MechaColors;

            Multiplayer.Session.NumPlayers += 1;
            DiscordManager.UpdateRichPresence();

            // if user is known and host is ingame dont put him into lobby but let him join the game
            if (!isNewUser && Multiplayer.Session.IsGameLoaded)
            {
                // Remove the new player from pending list
                using (playerManager.GetPendingPlayers(out Dictionary <INebulaConnection, INebulaPlayer> pendingPlayers))
                {
                    pendingPlayers.Remove(conn);
                }

                // Add the new player to the list
                using (playerManager.GetSyncingPlayers(out Dictionary <INebulaConnection, INebulaPlayer> syncingPlayers))
                {
                    syncingPlayers.Add(conn, player);
                }

                Multiplayer.Session.World.OnPlayerJoining(player.Data.Username);
                NebulaModAPI.OnPlayerJoinedGame?.Invoke(player.Data);

                // Make sure that each player that is currently in the game receives that a new player as join so they can create its RemotePlayerCharacter
                PlayerJoining pdata = new PlayerJoining((PlayerData)player.Data.CreateCopyWithoutMechaData(), Multiplayer.Session.NumPlayers); // Remove inventory from mecha data
                using (playerManager.GetConnectedPlayers(out Dictionary <INebulaConnection, INebulaPlayer> connectedPlayers))
                {
                    foreach (KeyValuePair <INebulaConnection, INebulaPlayer> kvp in connectedPlayers)
                    {
                        kvp.Value.SendPacket(pdata);
                    }
                }

                //Add current tech bonuses to the connecting player based on the Host's mecha
                ((MechaData)player.Data.Mecha).TechBonuses = new PlayerTechBonuses(GameMain.mainPlayer.mecha);

                using (BinaryUtils.Writer p = new BinaryUtils.Writer())
                {
                    int count = 0;
                    foreach (KeyValuePair <string, BepInEx.PluginInfo> pluginInfo in BepInEx.Bootstrap.Chainloader.PluginInfos)
                    {
                        if (pluginInfo.Value.Instance is IMultiplayerModWithSettings mod)
                        {
                            p.BinaryWriter.Write(pluginInfo.Key);
                            mod.Export(p.BinaryWriter);
                            count++;
                        }
                    }

                    GameDesc gameDesc = GameMain.data.gameDesc;
                    player.SendPacket(new HandshakeResponse(gameDesc.galaxyAlgo, gameDesc.galaxySeed, gameDesc.starCount, gameDesc.resourceMultiplier, gameDesc.savedThemeIds, isNewUser, (PlayerData)player.Data, p.CloseAndGetBytes(), count, Config.Options.SyncSoil, Multiplayer.Session.NumPlayers, DiscordManager.GetPartyId()));
                }
            }
            else
            {
                GameDesc gameDesc = Multiplayer.Session.IsGameLoaded ? GameMain.data.gameDesc : UIRoot.instance.galaxySelect.gameDesc;

                using (BinaryUtils.Writer p = new BinaryUtils.Writer())
                {
                    int count = 0;
                    foreach (KeyValuePair <string, BepInEx.PluginInfo> pluginInfo in BepInEx.Bootstrap.Chainloader.PluginInfos)
                    {
                        if (pluginInfo.Value.Instance is IMultiplayerModWithSettings mod)
                        {
                            p.BinaryWriter.Write(pluginInfo.Key);
                            mod.Export(p.BinaryWriter);
                            count++;
                        }
                    }

                    player.SendPacket(new LobbyResponse(gameDesc.galaxyAlgo, gameDesc.galaxySeed, gameDesc.starCount, gameDesc.resourceMultiplier, gameDesc.savedThemeIds, p.CloseAndGetBytes(), count, Multiplayer.Session.NumPlayers, DiscordManager.GetPartyId()));
                }

                // Send overriden Planet and Star names
                player.SendPacket(new NameInputPacket(GameMain.galaxy, Multiplayer.Session.LocalPlayer.Id));
            }
        }