public void ClientReadCrew(IReadMessage msg)
        {
            ushort availableHireLength          = msg.ReadUInt16();
            List <CharacterInfo> availableHires = new List <CharacterInfo>();

            for (int i = 0; i < availableHireLength; i++)
            {
                CharacterInfo hire = CharacterInfo.ClientRead("human", msg);
                hire.Salary = msg.ReadInt32();
                availableHires.Add(hire);
            }

            ushort     pendingHireLength = msg.ReadUInt16();
            List <int> pendingHires      = new List <int>();

            for (int i = 0; i < pendingHireLength; i++)
            {
                pendingHires.Add(msg.ReadInt32());
            }

            bool validateHires = msg.ReadBoolean();

            bool fireCharacter = msg.ReadBoolean();

            int firedIdentifier = -1;

            if (fireCharacter)
            {
                firedIdentifier = msg.ReadInt32();
            }

            if (fireCharacter)
            {
                CharacterInfo firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
                // this one might and is allowed to be null since the character is already fired on the original sender's game
                if (firedCharacter != null)
                {
                    CrewManager.FireCharacter(firedCharacter);
                }
            }

            if (map?.CurrentLocation?.HireManager != null && CampaignUI?.CrewManagement != null)
            {
                CampaignUI?.CrewManagement?.SetHireables(map.CurrentLocation, availableHires);
                if (validateHires)
                {
                    CampaignUI?.CrewManagement.ValidatePendingHires();
                }
                CampaignUI?.CrewManagement?.SetPendingHires(pendingHires, map?.CurrentLocation);
                if (fireCharacter)
                {
                    CampaignUI?.CrewManagement.UpdateCrew();
                }
            }
        }
Beispiel #2
0
        public void ClientRead(IReadMessage inc)
        {
            bool shouldReset = inc.ReadBoolean();
            int  money       = inc.ReadInt32();

            // uint length = inc.ReadUInt32();
            //
            // for (int i = 0; i < length; i++)
            // {
            //     string key = inc.ReadString();
            //     byte value = inc.ReadByte();
            //     Metadata.SetValue(key, value);
            // }

            Campaign.Money = money;

            if (shouldReset)
            {
                ResetUpgrades();
            }

            // spentMoney is local, so this message box should only appear for those who have spent money on upgrades
            if (spentMoney > 0)
            {
                GUIMessageBox msgBox = new GUIMessageBox(TextManager.Get("UpgradeRefundTitle"), TextManager.Get("UpgradeRefundBody"), new [] { TextManager.Get("Ok") });
                msgBox.Buttons[0].OnClicked += msgBox.Close;
            }

            spentMoney = 0;
            PendingUpgrades.Clear();
            PurchasedUpgrades.Clear();
            CanUpgrade = false;
        }
            public void Read(IReadMessage msg)
            {
                int    oldPos = msg.BitPosition;
                UInt32 size   = msg.ReadVariableUInt32();

                float x; float y; float z; float w;
                byte  r; byte g; byte b; byte a;
                int   ix; int iy; int width; int height;

                switch (typeString)
                {
                case "float":
                    if (size != 4)
                    {
                        break;
                    }
                    property.SetValue(parentObject, msg.ReadSingle());
                    return;

                case "int":
                    if (size != 4)
                    {
                        break;
                    }
                    property.SetValue(parentObject, msg.ReadInt32());
                    return;

                case "vector2":
                    if (size != 8)
                    {
                        break;
                    }
                    x = msg.ReadSingle();
                    y = msg.ReadSingle();
                    property.SetValue(parentObject, new Vector2(x, y));
                    return;

                case "vector3":
                    if (size != 12)
                    {
                        break;
                    }
                    x = msg.ReadSingle();
                    y = msg.ReadSingle();
                    z = msg.ReadSingle();
                    property.SetValue(parentObject, new Vector3(x, y, z));
                    return;

                case "vector4":
                    if (size != 16)
                    {
                        break;
                    }
                    x = msg.ReadSingle();
                    y = msg.ReadSingle();
                    z = msg.ReadSingle();
                    w = msg.ReadSingle();
                    property.SetValue(parentObject, new Vector4(x, y, z, w));
                    return;

                case "color":
                    if (size != 4)
                    {
                        break;
                    }
                    r = msg.ReadByte();
                    g = msg.ReadByte();
                    b = msg.ReadByte();
                    a = msg.ReadByte();
                    property.SetValue(parentObject, new Color(r, g, b, a));
                    return;

                case "rectangle":
                    if (size != 16)
                    {
                        break;
                    }
                    ix     = msg.ReadInt32();
                    iy     = msg.ReadInt32();
                    width  = msg.ReadInt32();
                    height = msg.ReadInt32();
                    property.SetValue(parentObject, new Rectangle(ix, iy, width, height));
                    return;

                default:
                    msg.BitPosition = oldPos;     //reset position to properly read the string
                    string incVal = msg.ReadString();
                    property.TrySetValue(parentObject, incVal);
                    return;
                }

                //size didn't match: skip this
                msg.BitPosition += (int)(8 * size);
            }
        //static because we may need to instantiate the campaign if it hasn't been done yet
        public static void ClientRead(IReadMessage msg)
        {
            bool   isFirstRound         = msg.ReadBoolean();
            byte   campaignID           = msg.ReadByte();
            UInt16 updateID             = msg.ReadUInt16();
            UInt16 saveID               = msg.ReadUInt16();
            string mapSeed              = msg.ReadString();
            UInt16 currentLocIndex      = msg.ReadUInt16();
            UInt16 selectedLocIndex     = msg.ReadUInt16();
            byte   selectedMissionIndex = msg.ReadByte();
            bool   allowDebugTeleport   = msg.ReadBoolean();
            float? reputation           = null;

            if (msg.ReadBoolean())
            {
                reputation = msg.ReadSingle();
            }

            Dictionary <string, float> factionReps = new Dictionary <string, float>();
            byte factionsCount = msg.ReadByte();

            for (int i = 0; i < factionsCount; i++)
            {
                factionReps.Add(msg.ReadString(), msg.ReadSingle());
            }

            bool forceMapUI = msg.ReadBoolean();

            int  money = msg.ReadInt32();
            bool purchasedHullRepairs  = msg.ReadBoolean();
            bool purchasedItemRepairs  = msg.ReadBoolean();
            bool purchasedLostShuttles = msg.ReadBoolean();

            byte missionCount = msg.ReadByte();
            List <Pair <string, byte> > availableMissions = new List <Pair <string, byte> >();

            for (int i = 0; i < missionCount; i++)
            {
                string missionIdentifier = msg.ReadString();
                byte   connectionIndex   = msg.ReadByte();
                availableMissions.Add(new Pair <string, byte>(missionIdentifier, connectionIndex));
            }

            UInt16?storeBalance = null;

            if (msg.ReadBoolean())
            {
                storeBalance = msg.ReadUInt16();
            }

            UInt16 buyCrateItemCount           = msg.ReadUInt16();
            List <PurchasedItem> buyCrateItems = new List <PurchasedItem>();

            for (int i = 0; i < buyCrateItemCount; i++)
            {
                string itemPrefabIdentifier = msg.ReadString();
                int    itemQuantity         = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
                buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
            }

            UInt16 purchasedItemCount           = msg.ReadUInt16();
            List <PurchasedItem> purchasedItems = new List <PurchasedItem>();

            for (int i = 0; i < purchasedItemCount; i++)
            {
                string itemPrefabIdentifier = msg.ReadString();
                int    itemQuantity         = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
                purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
            }

            UInt16          soldItemCount = msg.ReadUInt16();
            List <SoldItem> soldItems     = new List <SoldItem>();

            for (int i = 0; i < soldItemCount; i++)
            {
                string itemPrefabIdentifier = msg.ReadString();
                UInt16 id       = msg.ReadUInt16();
                bool   removed  = msg.ReadBoolean();
                byte   sellerId = msg.ReadByte();
                soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId));
            }

            ushort pendingUpgradeCount = msg.ReadUInt16();
            List <PurchasedUpgrade> pendingUpgrades = new List <PurchasedUpgrade>();

            for (int i = 0; i < pendingUpgradeCount; i++)
            {
                string          upgradeIdentifier  = msg.ReadString();
                UpgradePrefab   prefab             = UpgradePrefab.Find(upgradeIdentifier);
                string          categoryIdentifier = msg.ReadString();
                UpgradeCategory category           = UpgradeCategory.Find(categoryIdentifier);
                int             upgradeLevel       = msg.ReadByte();
                if (prefab == null || category == null)
                {
                    continue;
                }
                pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
            }

            bool          hasCharacterData = msg.ReadBoolean();
            CharacterInfo myCharacterInfo  = null;

            if (hasCharacterData)
            {
                myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
            }

            if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID)
            {
                string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);

                GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed);
                campaign             = (MultiPlayerCampaign)GameMain.GameSession.GameMode;
                campaign.CampaignID  = campaignID;
                GameMain.NetLobbyScreen.ToggleCampaignMode(true);
            }

            //server has a newer save file
            if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
            {
                campaign.PendingSaveID = saveID;
            }

            if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
            {
                campaign.SuppressStateSending = true;
                campaign.IsFirstRound         = isFirstRound;

                //we need to have the latest save file to display location/mission/store
                if (campaign.LastSaveID == saveID)
                {
                    campaign.ForceMapUI = forceMapUI;

                    UpgradeStore.WaitForServerUpdate = false;

                    campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
                    campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
                    campaign.Map.SelectMission(selectedMissionIndex);
                    campaign.Map.AllowDebugTeleport = allowDebugTeleport;
                    campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems);
                    campaign.CargoManager.SetPurchasedItems(purchasedItems);
                    campaign.CargoManager.SetSoldItems(soldItems);
                    if (storeBalance.HasValue)
                    {
                        campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value;
                    }
                    campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades);
                    campaign.UpgradeManager.PurchasedUpgrades.Clear();

                    foreach (var(identifier, rep) in factionReps)
                    {
                        Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase));
                        if (faction?.Reputation != null)
                        {
                            faction.Reputation.Value = rep;
                        }
                        else
                        {
                            DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\".");
                        }
                    }

                    if (reputation.HasValue)
                    {
                        campaign.Map.CurrentLocation.Reputation.Value = reputation.Value;
                        campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
                    }

                    foreach (var availableMission in availableMissions)
                    {
                        MissionPrefab missionPrefab = MissionPrefab.List.Find(mp => mp.Identifier == availableMission.First);
                        if (missionPrefab == null)
                        {
                            DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found.");
                            continue;
                        }
                        if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count)
                        {
                            DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count}).");
                            continue;
                        }
                        LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second];
                        campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection);
                    }

                    GameMain.NetLobbyScreen.ToggleCampaignMode(true);
                }

                bool shouldRefresh = campaign.Money != money ||
                                     campaign.PurchasedHullRepairs != purchasedHullRepairs ||
                                     campaign.PurchasedItemRepairs != purchasedItemRepairs ||
                                     campaign.PurchasedLostShuttles != purchasedLostShuttles;

                campaign.Money = money;
                campaign.PurchasedHullRepairs  = purchasedHullRepairs;
                campaign.PurchasedItemRepairs  = purchasedItemRepairs;
                campaign.PurchasedLostShuttles = purchasedLostShuttles;

                if (shouldRefresh)
                {
                    campaign?.CampaignUI?.UpgradeStore?.RefreshAll();
                }

                if (myCharacterInfo != null)
                {
                    GameMain.Client.CharacterInfo = myCharacterInfo;
                    GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
                }
                else
                {
                    GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
                }

                campaign.lastUpdateID         = updateID;
                campaign.SuppressStateSending = false;
            }
        }
        public void ServerReadCrew(IReadMessage msg, Client sender)
        {
            int[] pendingHires = null;

            bool updatePending = msg.ReadBoolean();

            if (updatePending)
            {
                ushort pendingHireLength = msg.ReadUInt16();
                pendingHires = new int[pendingHireLength];
                for (int i = 0; i < pendingHireLength; i++)
                {
                    pendingHires[i] = msg.ReadInt32();
                }
            }

            bool validateHires = msg.ReadBoolean();

            bool   renameCharacter    = msg.ReadBoolean();
            int    renamedIdentifier  = -1;
            string newName            = null;
            bool   existingCrewMember = false;

            if (renameCharacter)
            {
                renamedIdentifier  = msg.ReadInt32();
                newName            = msg.ReadString();
                existingCrewMember = msg.ReadBoolean();
            }

            bool fireCharacter   = msg.ReadBoolean();
            int  firedIdentifier = -1;

            if (fireCharacter)
            {
                firedIdentifier = msg.ReadInt32();
            }

            Location             location        = map?.CurrentLocation;
            List <CharacterInfo> hiredCharacters = new List <CharacterInfo>();
            CharacterInfo        firedCharacter  = null;

            if (location != null && AllowedToManageCampaign(sender))
            {
                if (fireCharacter)
                {
                    firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
                    if (firedCharacter != null && (firedCharacter.Character?.IsBot ?? true))
                    {
                        CrewManager.FireCharacter(firedCharacter);
                    }
                    else
                    {
                        DebugConsole.ThrowError($"Tried to fire an invalid character ({firedIdentifier})");
                    }
                }

                if (renameCharacter)
                {
                    CharacterInfo characterInfo = null;
                    if (existingCrewMember && CrewManager != null)
                    {
                        characterInfo = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
                    }
                    else if (!existingCrewMember && location.HireManager != null)
                    {
                        characterInfo = location.HireManager.AvailableCharacters.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier);
                    }

                    if (characterInfo != null && (characterInfo.Character?.IsBot ?? true))
                    {
                        if (existingCrewMember)
                        {
                            CrewManager.RenameCharacter(characterInfo, newName);
                        }
                        else
                        {
                            location.HireManager.RenameCharacter(characterInfo, newName);
                        }
                    }
                    else
                    {
                        DebugConsole.ThrowError($"Tried to rename an invalid character ({renamedIdentifier})");
                    }
                }

                if (location.HireManager != null)
                {
                    if (validateHires)
                    {
                        foreach (CharacterInfo hireInfo in location.HireManager.PendingHires)
                        {
                            if (TryHireCharacter(location, hireInfo))
                            {
                                hiredCharacters.Add(hireInfo);
                            }
                            ;
                        }
                    }

                    if (updatePending)
                    {
                        List <CharacterInfo> pendingHireInfos = new List <CharacterInfo>();
                        foreach (int identifier in pendingHires)
                        {
                            CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == identifier);
                            if (match == null)
                            {
                                DebugConsole.ThrowError($"Tried to add a character that doesn't exist ({identifier}) to pending hires");
                                continue;
                            }

                            pendingHireInfos.Add(match);
                            if (pendingHireInfos.Count + CrewManager.CharacterInfos.Count() >= CrewManager.MaxCrewSize)
                            {
                                break;
                            }
                        }
                        location.HireManager.PendingHires = pendingHireInfos;
                    }

                    location.HireManager.AvailableCharacters.ForEachMod(info =>
                    {
                        if (!location.HireManager.PendingHires.Contains(info))
                        {
                            location.HireManager.RenameCharacter(info, info.OriginalName);
                        }
                    });
                }
            }

            // bounce back
            if (renameCharacter && existingCrewMember)
            {
                SendCrewState(hiredCharacters, (renamedIdentifier, newName), firedCharacter);
            }
            else
            {
                SendCrewState(hiredCharacters, default, firedCharacter);
Beispiel #6
0
        protected void ReadConnectionInitializationStep(IReadMessage inc)
        {
            ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();

            IWriteMessage outMsg;

            switch (step)
            {
            case ConnectionInitialization.SteamTicketAndVersion:
                if (initializationStep != ConnectionInitialization.SteamTicketAndVersion)
                {
                    return;
                }
                outMsg = new WriteOnlyMessage();
                outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
                outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
                outMsg.Write(Name);
                outMsg.Write(ownerKey);
                outMsg.Write(SteamManager.GetSteamID());
                if (steamAuthTicket == null)
                {
                    outMsg.Write((UInt16)0);
                }
                else
                {
                    outMsg.Write((UInt16)steamAuthTicket.Data.Length);
                    outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);
                }
                outMsg.Write(GameMain.Version.ToString());
                outMsg.Write(GameMain.Config.Language);

                SendMsgInternal(DeliveryMethod.Reliable, outMsg);
                break;

            case ConnectionInitialization.ContentPackageOrder:
                if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
                    initializationStep == ConnectionInitialization.Password)
                {
                    initializationStep = ConnectionInitialization.ContentPackageOrder;
                }
                if (initializationStep != ConnectionInitialization.ContentPackageOrder)
                {
                    return;
                }
                outMsg = new WriteOnlyMessage();
                outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
                outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);

                string serverName = inc.ReadString();

                UInt32 cpCount = inc.ReadVariableUInt32();
                ServerContentPackage        corePackage     = null;
                List <ServerContentPackage> regularPackages = new List <ServerContentPackage>();
                List <ServerContentPackage> missingPackages = new List <ServerContentPackage>();
                for (int i = 0; i < cpCount; i++)
                {
                    string name       = inc.ReadString();
                    string hash       = inc.ReadString();
                    UInt64 workshopId = inc.ReadUInt64();
                    var    pkg        = new ServerContentPackage(name, hash, workshopId);
                    if (pkg.CorePackage != null)
                    {
                        corePackage = pkg;
                    }
                    else if (pkg.RegularPackage != null)
                    {
                        regularPackages.Add(pkg);
                    }
                    else
                    {
                        missingPackages.Add(pkg);
                    }
                }

                if (missingPackages.Count > 0)
                {
                    var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0);

                    if (nonDownloadable.Any())
                    {
                        string disconnectMsg;
                        if (nonDownloadable.Count() == 1)
                        {
                            disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}";
                        }
                        else
                        {
                            List <string> packageStrs = new List <string>();
                            nonDownloadable.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
                            disconnectMsg = $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}";
                        }
                        Close(disconnectMsg, disableReconnect: true);
                        OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg);
                    }
                    else
                    {
                        Close(disableReconnect: true);

                        string missingModNames   = "\n";
                        int    displayedModCount = 0;
                        foreach (ServerContentPackage missingPackage in missingPackages)
                        {
                            missingModNames += "\n- " + GetPackageStr(missingPackage);
                            displayedModCount++;
                            if (GUI.Font.MeasureString(missingModNames).Y > GameMain.GraphicsHeight * 0.5f)
                            {
                                missingModNames += "\n\n" + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (missingPackages.Count - displayedModCount).ToString());
                                break;
                            }
                        }
                        missingModNames += "\n\n";

                        var msgBox = new GUIMessageBox(
                            TextManager.Get("WorkshopItemDownloadTitle"),
                            TextManager.GetWithVariable("WorkshopItemDownloadPrompt", "[items]", missingModNames),
                            new string[] { TextManager.Get("Yes"), TextManager.Get("No") });
                        msgBox.Buttons[0].OnClicked = (yesBtn, userdata) =>
                        {
                            GameMain.ServerListScreen.Select();
                            GameMain.ServerListScreen.DownloadWorkshopItems(missingPackages.Select(p => p.WorkshopId), serverName, ServerConnection.EndPointString);
                            return(true);
                        };
                        msgBox.Buttons[0].OnClicked += msgBox.Close;
                        msgBox.Buttons[1].OnClicked  = msgBox.Close;
                    }

                    return;
                }

                if (!contentPackageOrderReceived)
                {
                    GameMain.Config.BackUpModOrder();
                    GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList());
                    contentPackageOrderReceived = true;
                }

                SendMsgInternal(DeliveryMethod.Reliable, outMsg);
                break;

            case ConnectionInitialization.Password:
                if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
                {
                    initializationStep = ConnectionInitialization.Password;
                }
                if (initializationStep != ConnectionInitialization.Password)
                {
                    return;
                }
                bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
                int  retries      = 0;
                if (incomingSalt)
                {
                    passwordSalt = inc.ReadInt32();
                }
                else
                {
                    retries = inc.ReadInt32();
                }
                OnRequestPassword?.Invoke(passwordSalt, retries);
                break;
            }
        }
Beispiel #7
0
        public void ReadMessage(IReadMessage inc)
        {
            System.Diagnostics.Debug.Assert(!activeTransfers.Any(t =>
                                                                 t.Status == FileTransferStatus.Error ||
                                                                 t.Status == FileTransferStatus.Canceled ||
                                                                 t.Status == FileTransferStatus.Finished), "List of active file transfers contains entires that should have been removed");

            byte transferMessageType = inc.ReadByte();

            switch (transferMessageType)
            {
            case (byte)FileTransferMessageType.Initiate:
            {
                byte transferId       = inc.ReadByte();
                var  existingTransfer = activeTransfers.Find(t => t.ID == transferId);
                finishedTransfers.RemoveAll(t => t.First == transferId);
                byte fileType = inc.ReadByte();
                //ushort chunkLen = inc.ReadUInt16();
                int    fileSize = inc.ReadInt32();
                string fileName = inc.ReadString();

                if (existingTransfer != null)
                {
                    if (fileType != (byte)existingTransfer.FileType ||
                        fileSize != existingTransfer.FileSize ||
                        fileName != existingTransfer.FileName)
                    {
                        GameMain.Client.CancelFileTransfer(transferId);
                        DebugConsole.ThrowError("File transfer error: file transfer initiated with an ID that's already in use");
                    }
                    else         //resend acknowledgement packet
                    {
                        GameMain.Client.UpdateFileTransfer(transferId, 0);
                    }
                    return;
                }

                if (!ValidateInitialData(fileType, fileName, fileSize, out string errorMsg))
                {
                    GameMain.Client.CancelFileTransfer(transferId);
                    DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")");
                    return;
                }

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.Log("Received file transfer initiation message: ");
                    DebugConsole.Log("  File: " + fileName);
                    DebugConsole.Log("  Size: " + fileSize);
                    DebugConsole.Log("  ID: " + transferId);
                }

                string downloadFolder = downloadFolders[(FileTransferType)fileType];
                if (!Directory.Exists(downloadFolder))
                {
                    try
                    {
                        Directory.CreateDirectory(downloadFolder);
                    }
                    catch (Exception e)
                    {
                        DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \"" + downloadFolder + "\".", e);
                        return;
                    }
                }

                FileTransferIn newTransfer = new FileTransferIn(inc.Sender, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType)
                {
                    ID       = transferId,
                    Status   = FileTransferStatus.Receiving,
                    FileSize = fileSize
                };

                int maxRetries = 4;
                for (int i = 0; i <= maxRetries; i++)
                {
                    try
                    {
                        newTransfer.OpenStream();
                    }
                    catch (IOException e)
                    {
                        if (i < maxRetries)
                        {
                            DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}, retrying in 250 ms...", Color.Red);
                            Thread.Sleep(250);
                        }
                        else
                        {
                            DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red);
                            GameMain.Client.CancelFileTransfer(transferId);
                            newTransfer.Status = FileTransferStatus.Error;
                            OnTransferFailed(newTransfer);
                            return;
                        }
                    }
                }
                activeTransfers.Add(newTransfer);

                GameMain.Client.UpdateFileTransfer(transferId, 0);         //send acknowledgement packet
            }
            break;

            case (byte)FileTransferMessageType.TransferOnSameMachine:
            {
                byte   transferId = inc.ReadByte();
                byte   fileType   = inc.ReadByte();
                string filePath   = inc.ReadString();

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.Log("Received file transfer message on the same machine: ");
                    DebugConsole.Log("  File: " + filePath);
                    DebugConsole.Log("  ID: " + transferId);
                }

                if (!File.Exists(filePath))
                {
                    DebugConsole.ThrowError("File transfer on the same machine failed, file \"" + filePath + "\" not found.");
                    GameMain.Client.CancelFileTransfer(transferId);
                    return;
                }

                FileTransferIn directTransfer = new FileTransferIn(inc.Sender, filePath, (FileTransferType)fileType)
                {
                    ID       = transferId,
                    Status   = FileTransferStatus.Finished,
                    FileSize = 0
                };
                OnFinished(directTransfer);
            }
            break;

            case (byte)FileTransferMessageType.Data:
            {
                byte transferId = inc.ReadByte();

                var activeTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
                if (activeTransfer == null)
                {
                    //it's possible for the server to send some extra data
                    //before it acknowledges that the download is finished,
                    //so let's suppress the error message in that case
                    finishedTransfers.RemoveAll(t => t.Second + 5.0 < Timing.TotalTime);
                    if (!finishedTransfers.Any(t => t.First == transferId))
                    {
                        GameMain.Client.CancelFileTransfer(transferId);
                        DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message");
                    }
                    return;
                }

                int offset = inc.ReadInt32();
                if (offset != activeTransfer.Received)
                {
                    if (offset < activeTransfer.Received)
                    {
                        GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received);
                    }
                    return;
                }

                int bytesToRead = inc.ReadUInt16();

                if (activeTransfer.Received + bytesToRead > activeTransfer.FileSize)
                {
                    GameMain.Client.CancelFileTransfer(transferId);
                    DebugConsole.ThrowError("File transfer error: Received more data than expected (total received: " + activeTransfer.Received +
                                            ", msg received: " + (inc.LengthBytes - inc.BytePosition) +
                                            ", msg length: " + inc.LengthBytes +
                                            ", msg read: " + inc.BytePosition +
                                            ", filesize: " + activeTransfer.FileSize);
                    activeTransfer.Status = FileTransferStatus.Error;
                    StopTransfer(activeTransfer);
                    return;
                }

                try
                {
                    activeTransfer.ReadBytes(inc, bytesToRead);
                }
                catch (Exception e)
                {
                    GameMain.Client.CancelFileTransfer(transferId);
                    DebugConsole.ThrowError("File transfer error: " + e.Message);
                    activeTransfer.Status = FileTransferStatus.Error;
                    StopTransfer(activeTransfer, true);
                    return;
                }

                if (activeTransfer.Status == FileTransferStatus.Finished)
                {
                    GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received, true);
                    activeTransfer.Dispose();

                    if (ValidateReceivedData(activeTransfer, out string errorMessage))
                    {
                        finishedTransfers.Add(new Pair <int, double>(transferId, Timing.TotalTime));
                        StopTransfer(activeTransfer);
                        OnFinished(activeTransfer);
                    }
                    else
                    {
                        new GUIMessageBox("File transfer aborted", errorMessage);

                        activeTransfer.Status = FileTransferStatus.Error;
                        StopTransfer(activeTransfer, true);
                    }
                }
            }
            break;

            case (byte)FileTransferMessageType.Cancel:
            {
                byte transferId       = inc.ReadByte();
                var  matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
                if (matchingTransfer != null)
                {
                    new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\".");
                    StopTransfer(matchingTransfer);
                }
                break;
            }
            }
        }
Beispiel #8
0
        public void ReadFileRequest(IReadMessage inc, Client client)
        {
            byte messageType = inc.ReadByte();

            if (messageType == (byte)FileTransferMessageType.Cancel)
            {
                byte transferId       = inc.ReadByte();
                var  matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
                if (matchingTransfer != null)
                {
                    CancelTransfer(matchingTransfer);
                }

                return;
            }
            else if (messageType == (byte)FileTransferMessageType.Data)
            {
                byte transferId       = inc.ReadByte();
                var  matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId);
                if (matchingTransfer != null)
                {
                    matchingTransfer.Acknowledged = true;
                    int offset = inc.ReadInt32();
                    matchingTransfer.KnownReceivedOffset = offset > matchingTransfer.KnownReceivedOffset ? offset : matchingTransfer.KnownReceivedOffset;
                    if (matchingTransfer.SentOffset < matchingTransfer.KnownReceivedOffset)
                    {
                        matchingTransfer.SentOffset = matchingTransfer.KnownReceivedOffset;
                    }

                    if (matchingTransfer.KnownReceivedOffset >= matchingTransfer.Data.Length)
                    {
                        matchingTransfer.Status = FileTransferStatus.Finished;
                    }
                }
            }

            byte fileType = inc.ReadByte();

            switch (fileType)
            {
            case (byte)FileTransferType.Submarine:
                string fileName           = inc.ReadString();
                string fileHash           = inc.ReadString();
                var    requestedSubmarine = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == fileName && s.MD5Hash.Hash == fileHash);

                if (requestedSubmarine != null)
                {
                    StartTransfer(inc.Sender, FileTransferType.Submarine, requestedSubmarine.FilePath);
                }
                break;

            case (byte)FileTransferType.CampaignSave:
                if (GameMain.GameSession != null &&
                    !ActiveTransfers.Any(t => t.Connection == inc.Sender && t.FileType == FileTransferType.CampaignSave))
                {
                    StartTransfer(inc.Sender, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
                    if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign)
                    {
                        client.LastCampaignSaveSendTime = new Pair <ushort, float>(campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
                    }
                }
                break;
            }
        }
Beispiel #9
0
        public void ServerReadCrew(IReadMessage msg, Client sender)
        {
            int[] pendingHires = null;

            bool updatePending = msg.ReadBoolean();

            if (updatePending)
            {
                ushort pendingHireLength = msg.ReadUInt16();
                pendingHires = new int[pendingHireLength];
                for (int i = 0; i < pendingHireLength; i++)
                {
                    pendingHires[i] = msg.ReadInt32();
                }
            }

            bool validateHires = msg.ReadBoolean();
            bool fireCharacter = msg.ReadBoolean();

            int firedIdentifier = -1;

            if (fireCharacter)
            {
                firedIdentifier = msg.ReadInt32();
            }

            Location location = map?.CurrentLocation;

            CharacterInfo firedCharacter = null;

            if (location != null && AllowedToManageCampaign(sender))
            {
                if (fireCharacter)
                {
                    firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier);
                    if (firedCharacter != null && (firedCharacter.Character?.IsBot ?? true))
                    {
                        CrewManager.FireCharacter(firedCharacter);
                    }
                    else
                    {
                        DebugConsole.ThrowError($"Tried to fire an invalid character ({firedIdentifier})");
                    }
                }

                if (location.HireManager != null)
                {
                    if (validateHires)
                    {
                        foreach (CharacterInfo hireInfo in location.HireManager.PendingHires)
                        {
                            TryHireCharacter(location, hireInfo);
                        }
                    }

                    if (updatePending)
                    {
                        List <CharacterInfo> pendingHireInfos = new List <CharacterInfo>();
                        foreach (int identifier in pendingHires)
                        {
                            CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifier() == identifier);
                            if (match == null)
                            {
                                DebugConsole.ThrowError($"Tried to hire a character that doesn't exist ({identifier})");
                                continue;
                            }

                            pendingHireInfos.Add(match);
                        }
                        location.HireManager.PendingHires = pendingHireInfos;
                    }
                }
            }

            // bounce back
            SendCrewState(validateHires, firedCharacter);
        }
Beispiel #10
0
        //static because we may need to instantiate the campaign if it hasn't been done yet
        public static void ClientRead(IReadMessage msg)
        {
            byte   campaignID           = msg.ReadByte();
            UInt16 updateID             = msg.ReadUInt16();
            UInt16 saveID               = msg.ReadUInt16();
            string mapSeed              = msg.ReadString();
            UInt16 currentLocIndex      = msg.ReadUInt16();
            UInt16 selectedLocIndex     = msg.ReadUInt16();
            byte   selectedMissionIndex = msg.ReadByte();

            UInt16 startWatchmanID = msg.ReadUInt16();
            UInt16 endWatchmanID   = msg.ReadUInt16();

            int  money = msg.ReadInt32();
            bool purchasedHullRepairs  = msg.ReadBoolean();
            bool purchasedItemRepairs  = msg.ReadBoolean();
            bool purchasedLostShuttles = msg.ReadBoolean();

            UInt16 purchasedItemCount           = msg.ReadUInt16();
            List <PurchasedItem> purchasedItems = new List <PurchasedItem>();

            for (int i = 0; i < purchasedItemCount; i++)
            {
                string itemPrefabIdentifier = msg.ReadString();
                int    itemQuantity         = msg.ReadRangedInteger(0, CargoManager.MaxQuantity);
                purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
            }

            bool          hasCharacterData = msg.ReadBoolean();
            CharacterInfo myCharacterInfo  = null;

            if (hasCharacterData)
            {
                myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
            }

            MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;

            if (campaign == null || campaignID != campaign.CampaignID)
            {
                string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);

                GameMain.GameSession = new GameSession(null, savePath,
                                                       GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));

                campaign            = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
                campaign.CampaignID = campaignID;
                campaign.GenerateMap(mapSeed);
                GameMain.NetLobbyScreen.ToggleCampaignMode(true);
            }


            //server has a newer save file
            if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
            {
                /*//stop any active campaign save transfers, they're outdated now
                 * List<FileReceiver.FileTransferIn> saveTransfers =
                 *  GameMain.Client.FileReceiver.ActiveTransfers.FindAll(t => t.FileType == FileTransferType.CampaignSave);
                 *
                 * foreach (var transfer in saveTransfers)
                 * {
                 *  GameMain.Client.FileReceiver.StopTransfer(transfer);
                 * }
                 *
                 * GameMain.Client.RequestFile(FileTransferType.CampaignSave, null, null);*/
                campaign.PendingSaveID = saveID;
            }

            if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
            {
                campaign.SuppressStateSending = true;

                //we need to have the latest save file to display location/mission/store
                if (campaign.LastSaveID == saveID)
                {
                    campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
                    campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
                    campaign.Map.SelectMission(selectedMissionIndex);
                    campaign.CargoManager.SetPurchasedItems(purchasedItems);
                }

                campaign.startWatchmanID = startWatchmanID;
                campaign.endWatchmanID   = endWatchmanID;

                campaign.Money = money;
                campaign.PurchasedHullRepairs  = purchasedHullRepairs;
                campaign.PurchasedItemRepairs  = purchasedItemRepairs;
                campaign.PurchasedLostShuttles = purchasedLostShuttles;

                if (myCharacterInfo != null)
                {
                    GameMain.Client.CharacterInfo = myCharacterInfo;
                    GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
                }
                else
                {
                    GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
                }

                campaign.lastUpdateID         = updateID;
                campaign.SuppressStateSending = false;
            }
        }
Beispiel #11
0
        private void ReadConnectionInitializationStep(IReadMessage inc)
        {
            if (!isActive)
            {
                return;
            }

            ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();

            IWriteMessage outMsg;

            //DebugConsole.NewMessage(step + " " + initializationStep);
            switch (step)
            {
            case ConnectionInitialization.SteamTicketAndVersion:
                if (initializationStep != ConnectionInitialization.SteamTicketAndVersion)
                {
                    return;
                }
                outMsg = new WriteOnlyMessage();
                outMsg.Write((byte)DeliveryMethod.Reliable);
                outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
                outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
                outMsg.Write(Name);
                outMsg.Write(SteamManager.GetSteamID());
                outMsg.Write((UInt16)steamAuthTicket.Data.Length);
                outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);

                outMsg.Write(GameMain.Version.ToString());

                IEnumerable <ContentPackage> mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
                outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count());
                foreach (ContentPackage contentPackage in mpContentPackages)
                {
                    outMsg.Write(contentPackage.Name);
                    outMsg.Write(contentPackage.MD5hash.Hash);
                }

                heartbeatTimer = 5.0;
                Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
                sentBytes += outMsg.LengthBytes;
                break;

            case ConnectionInitialization.ContentPackageOrder:
                if (initializationStep == ConnectionInitialization.SteamTicketAndVersion ||
                    initializationStep == ConnectionInitialization.Password)
                {
                    initializationStep = ConnectionInitialization.ContentPackageOrder;
                }
                if (initializationStep != ConnectionInitialization.ContentPackageOrder)
                {
                    return;
                }
                outMsg = new WriteOnlyMessage();
                outMsg.Write((byte)DeliveryMethod.Reliable);
                outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
                outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder);

                UInt32 cpCount = inc.ReadVariableUInt32();
                List <ContentPackage> serverContentPackages = new List <ContentPackage>();
                for (int i = 0; i < cpCount; i++)
                {
                    string hash = inc.ReadString();
                    serverContentPackages.Add(GameMain.Config.SelectedContentPackages.Find(cp => cp.MD5hash.Hash == hash));
                }

                if (!contentPackageOrderReceived)
                {
                    GameMain.Config.ReorderSelectedContentPackages(cp => serverContentPackages.Contains(cp) ?
                                                                   serverContentPackages.IndexOf(cp) :
                                                                   serverContentPackages.Count + GameMain.Config.SelectedContentPackages.IndexOf(cp));
                    contentPackageOrderReceived = true;
                }

                Steamworks.SteamNetworking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes, 0, Steamworks.P2PSend.Reliable);
                sentBytes += outMsg.LengthBytes;
                break;

            case ConnectionInitialization.Password:
                if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
                {
                    initializationStep = ConnectionInitialization.Password;
                }
                if (initializationStep != ConnectionInitialization.Password)
                {
                    return;
                }
                bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
                int  retries      = 0;
                if (incomingSalt)
                {
                    passwordSalt = inc.ReadInt32();
                }
                else
                {
                    retries = inc.ReadInt32();
                }
                OnRequestPassword?.Invoke(passwordSalt, retries);
                break;
            }
        }
Beispiel #12
0
 public CampaignSettings(IReadMessage inc)
 {
     RadiationEnabled = inc.ReadBoolean();
     MaxMissionCount  = inc.ReadInt32();
 }
Beispiel #13
0
        protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
        {
            pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;

            ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();

            if (pendingClient.InitializationStep != initializationStep)
            {
                return;
            }

            pendingClient.UpdateTime = Timing.TotalTime + Timing.Step;

            switch (initializationStep)
            {
            case ConnectionInitialization.SteamTicketAndVersion:
                string name         = Client.SanitizeName(inc.ReadString());
                int    ownerKey     = inc.ReadInt32();
                UInt64 steamId      = inc.ReadUInt64();
                UInt16 ticketLength = inc.ReadUInt16();
                byte[] ticketBytes  = inc.ReadBytes(ticketLength);

                if (!Client.IsValidName(name, serverSettings))
                {
                    RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "");
                    return;
                }

                string version             = inc.ReadString();
                bool   isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false;
                if (!isCompatibleVersion)
                {
                    RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion,
                                        $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}");

                    GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error);
                    DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red);
                    return;
                }

                string language = inc.ReadString();
                pendingClient.Connection.Language = language;

                Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower()));
                if (nameTaken != null)
                {
                    RemovePendingClient(pendingClient, DisconnectReason.NameTaken, "");
                    GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error);
                    return;
                }

                if (!pendingClient.AuthSessionStarted)
                {
                    ProcessAuthTicket(name, ownerKey, steamId, pendingClient, ticketBytes);
                }
                break;

            case ConnectionInitialization.Password:
                int    pwLength    = inc.ReadByte();
                byte[] incPassword = inc.ReadBytes(pwLength);
                if (pendingClient.PasswordSalt == null)
                {
                    DebugConsole.ThrowError("Received password message from client without salt");
                    return;
                }
                if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
                {
                    pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder;
                }
                else
                {
                    pendingClient.Retries++;
                    if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan)
                    {
                        string banMsg = "Failed to enter correct password too many times";
                        BanPendingClient(pendingClient, banMsg, null);

                        RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
                        return;
                    }
                }
                pendingClient.UpdateTime = Timing.TotalTime;
                break;

            case ConnectionInitialization.ContentPackageOrder:
                pendingClient.InitializationStep = ConnectionInitialization.Success;
                pendingClient.UpdateTime         = Timing.TotalTime;
                break;
            }
        }
Beispiel #14
0
        public void ServerRead(IReadMessage inc, Client sender)
        {
            if (GameMain.Server == null || sender == null)
            {
                return;
            }

            byte     voteTypeByte = inc.ReadByte();
            VoteType voteType     = VoteType.Unknown;

            try
            {
                voteType = (VoteType)voteTypeByte;
            }
            catch (Exception e)
            {
                DebugConsole.ThrowError("Failed to cast vote type \"" + voteTypeByte + "\"", e);
                return;
            }

            switch (voteType)
            {
            case VoteType.Sub:
                int           equalityCheckVal = inc.ReadInt32();
                string        hash             = equalityCheckVal > 0 ? string.Empty : inc.ReadString();
                SubmarineInfo sub = equalityCheckVal > 0 ?
                                    SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Type == SubmarineType.Player && s.EqualityCheckVal == equalityCheckVal) :
                                    SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Type == SubmarineType.Player && s.MD5Hash.Hash == hash);
                sender.SetVote(voteType, sub);
                break;

            case VoteType.Mode:
                string         modeIdentifier = inc.ReadString();
                GameModePreset mode           = GameModePreset.List.Find(gm => gm.Identifier == modeIdentifier);
                if (mode == null || !mode.Votable)
                {
                    break;
                }
                sender.SetVote(voteType, mode);
                break;

            case VoteType.EndRound:
                if (!sender.HasSpawned)
                {
                    return;
                }
                sender.SetVote(voteType, inc.ReadBoolean());

                GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote <bool>(VoteType.EndRound));
                GameMain.NetworkMember.EndVoteMax   = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);

                break;

            case VoteType.Kick:
                byte kickedClientID = inc.ReadByte();

                Client kicked = GameMain.Server.ConnectedClients.Find(c => c.ID == kickedClientID);
                if (kicked != null && kicked.Connection != GameMain.Server.OwnerConnection && !kicked.HasKickVoteFrom(sender))
                {
                    kicked.AddKickVote(sender);
                    Client.UpdateKickVotes(GameMain.Server.ConnectedClients);
                    GameMain.Server.SendChatMessage($"ServerMessage.HasVotedToKick~[initiator]={sender.Name}~[target]={kicked.Name}", ChatMessageType.Server, null);
                }

                break;

            case VoteType.StartRound:
                bool ready = inc.ReadBoolean();
                if (ready != sender.GetVote <bool>(VoteType.StartRound))
                {
                    sender.SetVote(VoteType.StartRound, ready);
                    GameServer.Log(GameServer.ClientLogName(sender) + (ready ? " is ready to start the game." : " is not ready to start the game."), ServerLog.MessageType.ServerMessage);
                }
                break;

            case VoteType.PurchaseAndSwitchSub:
            case VoteType.PurchaseSub:
            case VoteType.SwitchSub:
                bool startVote = inc.ReadBoolean();
                if (startVote)
                {
                    StartSubmarineVote(inc, voteType, sender);
                }
                else
                {
                    sender.SetVote(voteType, (int)inc.ReadByte());
                }

                GameMain.Server.SubmarineVoteYesCount = GameMain.Server.ConnectedClients.Count(c => c.GetVote <int>(SubVote.VoteType) == 2);
                GameMain.Server.SubmarineVoteNoCount  = GameMain.Server.ConnectedClients.Count(c => c.GetVote <int>(SubVote.VoteType) == 1);
                GameMain.Server.SubmarineVoteMax      = GameMain.Server.ConnectedClients.Count(c => c.InGame);
                break;
            }

            inc.ReadPadBits();

            GameMain.Server.UpdateVoteStatus();
        }
        private void ReadConnectionInitializationStep(IReadMessage inc)
        {
            if (!isActive)
            {
                return;
            }

            ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();

            //DebugConsole.NewMessage(step + " " + initializationStep);
            switch (step)
            {
            case ConnectionInitialization.SteamTicketAndVersion:
                if (initializationStep != ConnectionInitialization.SteamTicketAndVersion)
                {
                    return;
                }
                IWriteMessage outMsg = new WriteOnlyMessage();
                outMsg.Write((byte)DeliveryMethod.Reliable);
                outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep);
                outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion);
                outMsg.Write(Name);
                outMsg.Write(SteamManager.GetSteamID());
                outMsg.Write((UInt16)steamAuthTicket.Data.Length);
                outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length);

                outMsg.Write(GameMain.Version.ToString());

                IEnumerable <ContentPackage> mpContentPackages = GameMain.SelectedPackages.Where(cp => cp.HasMultiplayerIncompatibleContent);
                outMsg.WriteVariableUInt32((UInt32)mpContentPackages.Count());
                foreach (ContentPackage contentPackage in mpContentPackages)
                {
                    outMsg.Write(contentPackage.Name);
                    outMsg.Write(contentPackage.MD5hash.Hash);
                }

                heartbeatTimer = 5.0;
                SteamManager.Instance.Networking.SendP2PPacket(hostSteamId, outMsg.Buffer, outMsg.LengthBytes,
                                                               Facepunch.Steamworks.Networking.SendType.Reliable);
                break;

            case ConnectionInitialization.Password:
                if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
                {
                    initializationStep = ConnectionInitialization.Password;
                }
                if (initializationStep != ConnectionInitialization.Password)
                {
                    return;
                }
                bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
                int  retries      = 0;
                if (incomingSalt)
                {
                    passwordSalt = inc.ReadInt32();
                }
                else
                {
                    retries = inc.ReadInt32();
                }
                OnRequestPassword?.Invoke(passwordSalt, retries);
                break;
            }
        }
        public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            switch (type)
            {
            case ServerNetObject.ENTITY_POSITION:
                bool facingRight = AnimController.Dir > 0.0f;

                lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;

                AnimController.Frozen = false;
                Enabled = true;

                UInt16 networkUpdateID = 0;
                if (msg.ReadBoolean())
                {
                    networkUpdateID = msg.ReadUInt16();
                }
                else
                {
                    bool aimInput = msg.ReadBoolean();
                    keys[(int)InputType.Aim].Held = aimInput;
                    keys[(int)InputType.Aim].SetState(false, aimInput);

                    bool shootInput = msg.ReadBoolean();
                    keys[(int)InputType.Shoot].Held = shootInput;
                    keys[(int)InputType.Shoot].SetState(false, shootInput);

                    bool useInput = msg.ReadBoolean();
                    keys[(int)InputType.Use].Held = useInput;
                    keys[(int)InputType.Use].SetState(false, useInput);

                    if (AnimController is HumanoidAnimController)
                    {
                        bool crouching = msg.ReadBoolean();
                        keys[(int)InputType.Crouch].Held = crouching;
                        keys[(int)InputType.Crouch].SetState(false, crouching);
                    }

                    bool attackInput = msg.ReadBoolean();
                    keys[(int)InputType.Attack].Held = attackInput;
                    keys[(int)InputType.Attack].SetState(false, attackInput);

                    double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
                    cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
                    TransformCursorPos();

                    bool ragdollInput = msg.ReadBoolean();
                    keys[(int)InputType.Ragdoll].Held = ragdollInput;
                    keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);

                    facingRight = msg.ReadBoolean();
                }

                bool      entitySelected    = msg.ReadBoolean();
                Character selectedCharacter = null;
                Item      selectedItem      = null;

                AnimController.Animation animation = AnimController.Animation.None;
                if (entitySelected)
                {
                    ushort characterID = msg.ReadUInt16();
                    ushort itemID      = msg.ReadUInt16();
                    selectedCharacter = FindEntityByID(characterID) as Character;
                    selectedItem      = FindEntityByID(itemID) as Item;
                    if (characterID != NullEntityID)
                    {
                        bool doingCpr = msg.ReadBoolean();
                        if (doingCpr && SelectedCharacter != null)
                        {
                            animation = AnimController.Animation.CPR;
                        }
                    }
                }

                Vector2 pos = new Vector2(
                    msg.ReadSingle(),
                    msg.ReadSingle());
                float   MaxVel         = NetConfig.MaxPhysicsBodyVelocity;
                Vector2 linearVelocity = new Vector2(
                    msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
                    msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
                linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);

                bool  fixedRotation   = msg.ReadBoolean();
                float?rotation        = null;
                float?angularVelocity = null;
                if (!fixedRotation)
                {
                    rotation = msg.ReadSingle();
                    float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
                    angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
                    angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
                }

                bool readStatus = msg.ReadBoolean();
                if (readStatus)
                {
                    ReadStatus(msg);
                    AIController?.ClientRead(msg);
                }

                msg.ReadPadBits();

                int index = 0;
                if (GameMain.Client.Character == this && CanMove)
                {
                    var posInfo = new CharacterStateInfo(
                        pos, rotation,
                        networkUpdateID,
                        facingRight ? Direction.Right : Direction.Left,
                        selectedCharacter, selectedItem, animation);

                    while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
                    {
                        index++;
                    }
                    memState.Insert(index, posInfo);
                }
                else
                {
                    var posInfo = new CharacterStateInfo(
                        pos, rotation,
                        linearVelocity, angularVelocity,
                        sendingTime, facingRight ? Direction.Right : Direction.Left,
                        selectedCharacter, selectedItem, animation);

                    while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
                    {
                        index++;
                    }
                    memState.Insert(index, posInfo);
                }

                break;

            case ServerNetObject.ENTITY_EVENT:
                int eventType = msg.ReadRangedInteger(0, 13);
                switch (eventType)
                {
                case 0:         //NetEntityEvent.Type.InventoryState
                    if (Inventory == null)
                    {
                        string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")";
                        DebugConsole.ThrowError(errorMsg);
                        GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);

                        //read anyway to prevent messing up reading the rest of the message
                        _ = msg.ReadUInt16();
                        byte inventoryItemCount = msg.ReadByte();
                        for (int i = 0; i < inventoryItemCount; i++)
                        {
                            msg.ReadUInt16();
                        }
                    }
                    else
                    {
                        Inventory.ClientRead(type, msg, sendingTime);
                    }
                    break;

                case 1:         //NetEntityEvent.Type.Control
                    byte ownerID = msg.ReadByte();
                    ResetNetState();
                    if (ownerID == GameMain.Client.ID)
                    {
                        if (controlled != null)
                        {
                            LastNetworkUpdateID = controlled.LastNetworkUpdateID;
                        }

                        if (!IsDead)
                        {
                            Controlled = this;
                        }
                        IsRemotePlayer                          = false;
                        GameMain.Client.HasSpawned              = true;
                        GameMain.Client.Character               = this;
                        GameMain.LightManager.LosEnabled        = true;
                        GameMain.LightManager.LosAlpha          = 1f;
                        GameMain.Client.WaitForNextRoundRespawn = null;
                    }
                    else
                    {
                        if (controlled == this)
                        {
                            Controlled     = null;
                            IsRemotePlayer = ownerID > 0;
                        }
                    }
                    break;

                case 2:         //NetEntityEvent.Type.Status
                    ReadStatus(msg);
                    break;

                case 3:         //NetEntityEvent.Type.UpdateSkills
                    int skillCount = msg.ReadByte();
                    for (int i = 0; i < skillCount; i++)
                    {
                        string skillIdentifier = msg.ReadString();
                        float  skillLevel      = msg.ReadSingle();
                        info?.SetSkillLevel(skillIdentifier, skillLevel);
                    }
                    break;

                case 4:         // NetEntityEvent.Type.SetAttackTarget
                case 5:         //NetEntityEvent.Type.ExecuteAttack
                    int     attackLimbIndex = msg.ReadByte();
                    UInt16  targetEntityID  = msg.ReadUInt16();
                    int     targetLimbIndex = msg.ReadByte();
                    Vector2 targetSimPos    = new Vector2(msg.ReadSingle(), msg.ReadSingle());
                    //255 = entity already removed, no need to do anything
                    if (attackLimbIndex == 255 || Removed)
                    {
                        break;
                    }
                    if (attackLimbIndex >= AnimController.Limbs.Length)
                    {
                        DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
                        break;
                    }
                    Limb attackLimb = AnimController.Limbs[attackLimbIndex];
                    Limb targetLimb = null;
                    if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity))
                    {
                        DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target entity not found (ID {targetEntityID})");
                        break;
                    }
                    if (targetEntity is Character targetCharacter)
                    {
                        if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
                        {
                            DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
                            break;
                        }
                        targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
                    }
                    if (attackLimb?.attack != null && Controlled != this)
                    {
                        if (eventType == 4)
                        {
                            SetAttackTarget(attackLimb, targetEntity, targetSimPos);
                            PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
                        }
                        else
                        {
                            attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
                        }
                    }
                    break;

                case 6:         //NetEntityEvent.Type.AssignCampaignInteraction
                    byte campaignInteractionType = msg.ReadByte();
                    bool requireConsciousness    = msg.ReadBoolean();
                    (GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
                    RequireConsciousnessForCustomInteract = requireConsciousness;
                    break;

                case 7:         //NetEntityEvent.Type.ObjectiveManagerState
                    // 1 = order, 2 = objective
                    int msgType = msg.ReadRangedInteger(0, 2);
                    if (msgType == 0)
                    {
                        break;
                    }
                    bool validData = msg.ReadBoolean();
                    if (!validData)
                    {
                        break;
                    }
                    if (msgType == 1)
                    {
                        int    orderIndex  = msg.ReadRangedInteger(0, Order.PrefabList.Count);
                        var    orderPrefab = Order.PrefabList[orderIndex];
                        string option      = null;
                        if (orderPrefab.HasOptions)
                        {
                            int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
                            if (optionIndex > -1)
                            {
                                option = orderPrefab.AllOptions[optionIndex];
                            }
                        }
                        GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
                    }
                    else if (msgType == 2)
                    {
                        string identifier = msg.ReadString();
                        string option     = msg.ReadString();
                        ushort objectiveTargetEntityId = msg.ReadUInt16();
                        var    objectiveTargetEntity   = FindEntityByID(objectiveTargetEntityId);
                        GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
                    }
                    break;

                case 8:         //NetEntityEvent.Type.TeamChange
                    byte newTeamId = msg.ReadByte();
                    ChangeTeam((CharacterTeamType)newTeamId);
                    break;

                case 9:         //NetEntityEvent.Type.AddToCrew
                    GameMain.GameSession.CrewManager.AddCharacter(this);
                    CharacterTeamType teamID    = (CharacterTeamType)msg.ReadByte();
                    ushort            itemCount = msg.ReadUInt16();
                    for (int i = 0; i < itemCount; i++)
                    {
                        ushort itemID = msg.ReadUInt16();
                        if (!(Entity.FindEntityByID(itemID) is Item item))
                        {
                            continue;
                        }
                        item.AllowStealing = true;
                        var wifiComponent = item.GetComponent <WifiComponent>();
                        if (wifiComponent != null)
                        {
                            wifiComponent.TeamID = teamID;
                        }
                        var idCard = item.GetComponent <IdCard>();
                        if (idCard != null)
                        {
                            idCard.TeamID = teamID;
                            idCard.SubmarineSpecificID = 0;
                        }
                    }
                    break;

                case 10:         //NetEntityEvent.Type.UpdateExperience
                    int experienceAmount = msg.ReadInt32();
                    info?.SetExperience(experienceAmount);
                    break;

                case 11:         //NetEntityEvent.Type.UpdateTalents:
                    ushort talentCount = msg.ReadUInt16();
                    for (int i = 0; i < talentCount; i++)
                    {
                        bool   addedThisRound   = msg.ReadBoolean();
                        UInt32 talentIdentifier = msg.ReadUInt32();
                        GiveTalent(talentIdentifier, addedThisRound);
                    }
                    break;

                case 12:         //NetEntityEvent.Type.UpdateMoney:
                    int moneyAmount = msg.ReadInt32();
                    SetMoney(moneyAmount);
                    break;

                case 13:         //NetEntityEvent.Type.UpdatePermanentStats:
                    byte      savedStatValueCount = msg.ReadByte();
                    StatTypes statType            = (StatTypes)msg.ReadByte();
                    info?.ClearSavedStatValues(statType);
                    for (int i = 0; i < savedStatValueCount; i++)
                    {
                        string statIdentifier = msg.ReadString();
                        float  statValue      = msg.ReadSingle();
                        bool   removeOnDeath  = msg.ReadBoolean();
                        info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
                    }
                    break;
                }
                msg.ReadPadBits();
                break;
            }
        }