Example #1
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            int eventIndex     = msg.ReadRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent));
            int nodeCount      = msg.ReadRangedInteger(0, MaxNodesPerNetworkEvent);
            int nodeStartIndex = eventIndex * MaxNodesPerNetworkEvent;

            Vector2[] nodePositions = new Vector2[nodeStartIndex + nodeCount];
            for (int i = 0; i < nodes.Count && i < nodePositions.Length; i++)
            {
                nodePositions[i] = nodes[i];
            }

            for (int i = 0; i < nodeCount; i++)
            {
                nodePositions[nodeStartIndex + i] = new Vector2(msg.ReadSingle(), msg.ReadSingle());
            }

            if (nodePositions.Any(n => !MathUtils.IsValid(n)))
            {
                nodes.Clear();
                return;
            }

            nodes = nodePositions.ToList();
            UpdateSections();
            Drawable = nodes.Any();
        }
        private void ReadStatus(IReadMessage msg)
        {
            bool isDead = msg.ReadBoolean();

            if (isDead)
            {
                CauseOfDeathType causeOfDeathType       = (CauseOfDeathType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1);
                AfflictionPrefab causeOfDeathAffliction = null;
                if (causeOfDeathType == CauseOfDeathType.Affliction)
                {
                    int afflictionIndex = msg.ReadRangedInteger(0, AfflictionPrefab.List.Count - 1);
                    if (afflictionIndex < 0 || afflictionIndex >= AfflictionPrefab.List.Count)
                    {
                        string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction index out of bounds (index: {afflictionIndex}, affliction count: {AfflictionPrefab.List.Count})";
                        causeOfDeathType = CauseOfDeathType.Unknown;
                        GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    }
                    else
                    {
                        causeOfDeathAffliction = AfflictionPrefab.List[afflictionIndex];
                    }
                }

                byte severedLimbCount = msg.ReadByte();
                if (!IsDead)
                {
                    if (causeOfDeathType == CauseOfDeathType.Pressure)
                    {
                        Implode(true);
                    }
                    else
                    {
                        Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f), true);
                    }
                }

                for (int i = 0; i < severedLimbCount; i++)
                {
                    int severedJointIndex = msg.ReadByte();
                    if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length)
                    {
                        string errorMsg = $"Error in CharacterNetworking.ReadStatus: severed joint index out of bounds (index: {severedJointIndex}, joint count: {AnimController.LimbJoints.Length})";
                        GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                    }
                    else
                    {
                        AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]);
                    }
                }
            }
            else
            {
                if (IsDead)
                {
                    Revive();
                }
                CharacterHealth.ClientRead(msg);
            }
        }
Example #3
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            int msgStartPos = msg.BitPosition;

            float flowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
            bool  isActive       = msg.ReadBoolean();
            bool  hijacked       = msg.ReadBoolean();
            float?targetLevel;

            if (msg.ReadBoolean())
            {
                targetLevel = msg.ReadSingle();
            }
            else
            {
                targetLevel = null;
            }

            if (correctionTimer > 0.0f)
            {
                int msgLength = msg.BitPosition - msgStartPos;
                msg.BitPosition = msgStartPos;
                StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
                return;
            }

            FlowPercentage = flowPercentage;
            IsActive       = isActive;
            Hijacked       = hijacked;
            TargetLevel    = targetLevel;
        }
Example #4
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            if (c.Character == null)
            {
                return;
            }
            var requestedFixAction = (FixActions)msg.ReadRangedInteger(0, 2);

            if (requestedFixAction != FixActions.None)
            {
                if (!c.Character.IsTraitor && requestedFixAction == FixActions.Sabotage)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.Log($"Non traitor \"{c.Character.Name}\" attempted to sabotage item.");
                    }
                    requestedFixAction = FixActions.Repair;
                }

                if (CurrentFixer == null || CurrentFixer == c.Character && requestedFixAction != currentFixerAction)
                {
                    StartRepairing(c.Character, requestedFixAction);
                    item.CreateServerEvent(this);
                }
            }
        }
Example #5
0
        public void ClientRead(IReadMessage incMsg)
        {
            cachedServerListInfo = null;

            ServerName        = incMsg.ReadString();
            ServerMessageText = incMsg.ReadString();
            MaxPlayers        = incMsg.ReadByte();
            HasPassword       = incMsg.ReadBoolean();
            IsPublic          = incMsg.ReadBoolean();
            GameMain.NetLobbyScreen.SetPublic(IsPublic);
            AllowFileTransfers = incMsg.ReadBoolean();
            incMsg.ReadPadBits();
            TickRate = incMsg.ReadRangedInteger(1, 60);
            GameMain.NetworkMember.TickRate = TickRate;

            ReadExtraCargo(incMsg);

            Voting.ClientRead(incMsg);

            bool isAdmin = incMsg.ReadBoolean();

            incMsg.ReadPadBits();
            if (isAdmin)
            {
                ClientAdminRead(incMsg);
            }
        }
Example #6
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            int       itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
            UInt16    userID    = msg.ReadUInt16();
            Character user      = Entity.FindEntityByID(userID) as Character;

            if (itemIndex == -1 || user == null)
            {
                CancelFabricating();
            }
            else
            {
                //if already fabricating the selected item, return
                if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex)
                {
                    return;
                }
                if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count)
                {
                    return;
                }

                SelectItem(user, fabricationRecipes[itemIndex]);
                StartFabricating(fabricationRecipes[itemIndex], user);
            }
        }
Example #7
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newFlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
            bool  newIsActive       = msg.ReadBoolean();

            if (item.CanClientAccess(c))
            {
                if (newFlowPercentage != FlowPercentage)
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the pumping speed of " + item.Name + " to " + (int)(newFlowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
                }
                if (newIsActive != IsActive)
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + (newIsActive ? " turned on " : " turned off ") + item.Name, ServerLog.MessageType.ItemInteraction);
                }
                if (pumpSpeedLockTimer <= 0.0f)
                {
                    TargetLevel = null;
                }

                FlowPercentage = newFlowPercentage;
                IsActive       = newIsActive;
            }

            //notify all clients of the changed state
            item.CreateServerEvent(this);
        }
Example #8
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            int itemIndex = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);

            item.CreateServerEvent(this);

            if (!item.CanClientAccess(c))
            {
                return;
            }

            if (itemIndex == -1)
            {
                CancelFabricating(c.Character);
            }
            else
            {
                //if already fabricating the selected item, return
                if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex)
                {
                    return;
                }
                if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count)
                {
                    return;
                }

                StartFabricating(fabricationRecipes[itemIndex], c.Character);
            }
        }
Example #9
0
        public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
        {
            remoteWaterVolume      = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
            remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);

            bool hasFireSources = message.ReadBoolean();

            remoteFireSources = new List <Vector3>();
            if (hasFireSources)
            {
                int fireSourceCount = message.ReadRangedInteger(0, 16);
                for (int i = 0; i < fireSourceCount; i++)
                {
                    remoteFireSources.Add(new Vector3(
                                              MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
                                              MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
                                              message.ReadRangedSingle(0.0f, 1.0f, 8)));
                }
            }

            if (serverUpdateDelay > 0.0f)
            {
                return;
            }

            ApplyRemoteState();
        }
Example #10
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            FabricatorState newState          = (FabricatorState)msg.ReadByte();
            float           newTimeUntilReady = msg.ReadSingle();
            int             itemIndex         = msg.ReadRangedInteger(-1, fabricationRecipes.Count - 1);
            UInt16          userID            = msg.ReadUInt16();
            Character       user = Entity.FindEntityByID(userID) as Character;

            State          = newState;
            timeUntilReady = newTimeUntilReady;

            if (newState == FabricatorState.Stopped || itemIndex == -1 || user == null)
            {
                CancelFabricating();
            }
            else if (newState == FabricatorState.Active || newState == FabricatorState.Paused)
            {
                //if already fabricating the selected item, return
                if (fabricatedItem != null && fabricationRecipes.IndexOf(fabricatedItem) == itemIndex)
                {
                    return;
                }
                if (itemIndex < 0 || itemIndex >= fabricationRecipes.Count)
                {
                    return;
                }

                SelectItem(user, fabricationRecipes[itemIndex]);
                StartFabricating(fabricationRecipes[itemIndex], user);
            }
        }
Example #11
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);

            switch (newState)
            {
            case State.Transporting:
                ReturnCountdownStarted = msg.ReadBoolean();
                maxTransportTime       = msg.ReadSingle();
                float transportTimeLeft = msg.ReadSingle();

                ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(transportTimeLeft * 1000.0f));
                RespawnCountdownStarted = false;
                if (CurrentState != newState)
                {
                    CoroutineManager.StopCoroutines("forcepos");
                    //CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f), "forcepos");
                }
                break;

            case State.Waiting:
                RespawnCountdownStarted = msg.ReadBoolean();
                ResetShuttle();
                float newRespawnTime = msg.ReadSingle();
                RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(newRespawnTime * 1000.0f));
                break;

            case State.Returning:
                RespawnCountdownStarted = false;
                break;
            }
            CurrentState = newState;

            msg.ReadPadBits();
        }
Example #12
0
 public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
 {
     deteriorationTimer          = msg.ReadSingle();
     deteriorateAlwaysResetTimer = msg.ReadSingle();
     DeteriorateAlways           = msg.ReadBoolean();
     CurrentFixer       = msg.ReadBoolean() ? Character.Controlled : null;
     currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
 }
Example #13
0
        //used when clients use the water/fire console commands
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newWaterVolume = msg.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;

            bool           hasFireSources  = msg.ReadBoolean();
            int            fireSourceCount = 0;
            List <Vector3> newFireSources  = new List <Vector3>();

            if (hasFireSources)
            {
                fireSourceCount = msg.ReadRangedInteger(0, 16);
                for (int i = 0; i < fireSourceCount; i++)
                {
                    newFireSources.Add(new Vector3(
                                           MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
                                           MathHelper.Clamp(msg.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
                                           msg.ReadRangedSingle(0.0f, 1.0f, 8)));
                }
            }

            if (!c.HasPermission(ClientPermissions.ConsoleCommands) ||
                !c.PermittedConsoleCommands.Any(command => command.names.Contains("fire") || command.names.Contains("editfire")))
            {
                return;
            }

            WaterVolume = newWaterVolume;

            for (int i = 0; i < fireSourceCount; i++)
            {
                Vector2 pos = new Vector2(
                    rect.X + rect.Width * newFireSources[i].X,
                    rect.Y - rect.Height + (rect.Height * newFireSources[i].Y));
                float size = newFireSources[i].Z * rect.Width;

                var newFire = i < FireSources.Count ?
                              FireSources[i] :
                              new FireSource(Submarine == null ? pos : pos + Submarine.Position, null, true);
                newFire.Position = pos;
                newFire.Size     = new Vector2(size, newFire.Size.Y);

                //ignore if the fire wasn't added to this room (invalid position)?
                if (!FireSources.Contains(newFire))
                {
                    newFire.Remove();
                    continue;
                }
            }

            for (int i = FireSources.Count - 1; i >= fireSourceCount; i--)
            {
                FireSources[i].Remove();
                if (i < FireSources.Count)
                {
                    FireSources.RemoveAt(i);
                }
            }
        }
Example #14
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            deteriorationTimer          = msg.ReadSingle();
            deteriorateAlwaysResetTimer = msg.ReadSingle();
            DeteriorateAlways           = msg.ReadBoolean();
            ushort currentFixerID = msg.ReadUInt16();

            currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
            CurrentFixer       = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
        }
Example #15
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            if (correctionTimer > 0.0f)
            {
                StartDelayedCorrection(type, msg.ExtractBits(5), sendingTime);
                return;
            }

            targetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;
        }
Example #16
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            Health = msg.ReadRangedSingle(0, MaxHealth, 8);
            int startOffset = msg.ReadRangedInteger(-1, MaximumVines);

            if (startOffset > -1)
            {
                int             vineCount = msg.ReadRangedInteger(0, VineChunkSize);
                List <VineTile> tiles     = new List <VineTile>();
                for (int i = 0; i < vineCount; i++)
                {
                    VineTileType vineType = (VineTileType)msg.ReadRangedInteger(0b0000, 0b1111);
                    int          flowerConfig = msg.ReadRangedInteger(0, 0xFFF);
                    int          leafConfig = msg.ReadRangedInteger(0, 0xFFF);
                    sbyte        posX = (sbyte)msg.ReadByte(), posY = (sbyte)msg.ReadByte();
                    Vector2      pos = new Vector2(posX * VineTile.Size, posY * VineTile.Size);

                    tiles.Add(new VineTile(this, pos, vineType, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig)));
                }

                // is this even needed??
                lock (mutex)
                {
                    for (var i = 0; i < vineCount; i++)
                    {
                        int index = i + startOffset;
                        if (index >= Vines.Count)
                        {
                            Vines.Add(tiles[i]);
                            continue;
                        }

                        VineTile oldVine = Vines[index];
                        VineTile newVine = tiles[i];
                        newVine.GrowthStep = oldVine.GrowthStep;
                        Vines[index]       = newVine;
                    }
                }
            }

            UpdateBranchHealth();
            ResetPlanterSize();
        }
Example #17
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            if (correctionTimer > 0.0f)
            {
                StartDelayedCorrection(type, msg.ExtractBits(5 + 1), sendingTime);
                return;
            }

            FlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
            IsActive       = msg.ReadBoolean();
        }
Example #18
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newRechargeSpeed = msg.ReadRangedInteger(0, 10) / 10.0f * maxRechargeSpeed;

            if (item.CanClientAccess(c))
            {
                RechargeSpeed = newRechargeSpeed;
                GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the recharge speed of " + item.Name + " to " + (int)((rechargeSpeed / maxRechargeSpeed) * 100.0f) + " %", ServerLog.MessageType.ItemInteraction);
            }

            item.CreateServerEvent(this);
        }
Example #19
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            deteriorationTimer          = msg.ReadSingle();
            deteriorateAlwaysResetTimer = msg.ReadSingle();
            DeteriorateAlways           = msg.ReadBoolean();
            tinkeringDuration           = msg.ReadSingle();
            tinkeringStrength           = msg.ReadSingle();
            ushort currentFixerID = msg.ReadUInt16();

            currentFixerAction = (FixActions)msg.ReadRangedInteger(0, 2);
            CurrentFixer       = currentFixerID != 0 ? Entity.FindEntityByID(currentFixerID) as Character : null;
            item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
        }
Example #20
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            int signalIndex = msg.ReadRangedInteger(0, Signals.Length - 1);

            if (!item.CanClientAccess(c))
            {
                return;
            }
            if (!SendSignal(signalIndex))
            {
                return;
            }
            GameServer.Log($"{GameServer.CharacterLogName(c.Character)} sent a signal \"{Signals[signalIndex]}\" from {item.Name}", ServerLog.MessageType.ItemInteraction);
            item.CreateServerEvent(this, new object[] { signalIndex });
        }
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            if (correctionTimer > 0.0f)
            {
                StartDelayedCorrection(type, msg.ExtractBits(5 + 16), sendingTime);
                return;
            }

            targetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;
            UInt16 userID = msg.ReadUInt16();

            if (userID != Entity.NullEntityID)
            {
                User = Entity.FindEntityByID(userID) as Character;
            }
        }
Example #22
0
        public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
        {
            if (correctionTimer > 0.0f)
            {
                StartDelayedCorrection(type, msg.ExtractBits(4 + 8), sendingTime);
                return;
            }

            float rechargeRate = msg.ReadRangedInteger(0, 10) / 10.0f;

            RechargeSpeed = rechargeRate * MaxRechargeSpeed;
#if CLIENT
            rechargeSpeedSlider.BarScroll = rechargeRate;
#endif
            Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * capacity;
        }
Example #23
0
        public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            float newTargetForce = msg.ReadRangedInteger(-10, 10) * 10.0f;

            if (item.CanClientAccess(c))
            {
                if (Math.Abs(newTargetForce - targetForce) > 0.01f)
                {
                    GameServer.Log(GameServer.CharacterLogName(c.Character) + " set the force of " + item.Name + " to " + (int)(newTargetForce) + " %", ServerLog.MessageType.ItemInteraction);
                }

                targetForce = newTargetForce;
            }

            //notify all clients of the changed state
            item.CreateServerEvent(this);
        }
Example #24
0
        public void ClientRead(IReadMessage incMsg)
        {
            ServerName        = incMsg.ReadString();
            ServerMessageText = incMsg.ReadString();
            MaxPlayers        = incMsg.ReadByte();
            HasPassword       = incMsg.ReadBoolean();
            isPublic          = incMsg.ReadBoolean();
            incMsg.ReadPadBits();
            TickRate = incMsg.ReadRangedInteger(1, 60);
            GameMain.NetworkMember.TickRate = TickRate;

            ReadExtraCargo(incMsg);

            Voting.ClientRead(incMsg);

            bool isAdmin = incMsg.ReadBoolean();

            incMsg.ReadPadBits();
            if (isAdmin)
            {
                ClientAdminRead(incMsg);
            }
        }
        //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 ServerRead(IReadMessage incMsg, Client c)
        {
            if (!c.HasPermission(Networking.ClientPermissions.ManageSettings))
            {
                return;
            }

            NetFlags flags = (NetFlags)incMsg.ReadByte();

            bool changed = false;

            if (flags.HasFlag(NetFlags.Name))
            {
                string serverName = incMsg.ReadString();
                if (ServerName != serverName)
                {
                    changed = true;
                }
                ServerName = serverName;
            }

            if (flags.HasFlag(NetFlags.Message))
            {
                string serverMessageText = incMsg.ReadString();
                if (ServerMessageText != serverMessageText)
                {
                    changed = true;
                }
                ServerMessageText = serverMessageText;
            }

            if (flags.HasFlag(NetFlags.Properties))
            {
                changed |= ReadExtraCargo(incMsg);

                UInt32 count = incMsg.ReadUInt32();

                for (int i = 0; i < count; i++)
                {
                    UInt32 key = incMsg.ReadUInt32();

                    if (netProperties.ContainsKey(key))
                    {
                        object prevValue = netProperties[key].Value;
                        netProperties[key].Read(incMsg);
                        if (!netProperties[key].PropEquals(prevValue, netProperties[key]))
                        {
                            GameServer.Log(GameServer.ClientLogName(c) + " changed " + netProperties[key].Name + " to " + netProperties[key].Value.ToString(), ServerLog.MessageType.ServerMessage);
                        }
                        changed = true;
                    }
                    else
                    {
                        UInt32 size = incMsg.ReadVariableUInt32();
                        incMsg.BitPosition += (int)(8 * size);
                    }
                }

                bool changedMonsterSettings = incMsg.ReadBoolean(); incMsg.ReadPadBits();
                changed |= changedMonsterSettings;
                if (changedMonsterSettings)
                {
                    ReadMonsterEnabled(incMsg);
                }
                changed |= BanList.ServerAdminRead(incMsg, c);
                changed |= Whitelist.ServerAdminRead(incMsg, c);
            }

            if (flags.HasFlag(NetFlags.Misc))
            {
                int orBits  = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
                int andBits = incMsg.ReadRangedInteger(0, (int)Barotrauma.MissionType.All) & (int)Barotrauma.MissionType.All;
                GameMain.NetLobbyScreen.MissionType = (Barotrauma.MissionType)(((int)GameMain.NetLobbyScreen.MissionType | orBits) & andBits);

                int traitorSetting = (int)TraitorsEnabled + incMsg.ReadByte() - 1;
                if (traitorSetting < 0)
                {
                    traitorSetting = 2;
                }
                if (traitorSetting > 2)
                {
                    traitorSetting = 0;
                }
                TraitorsEnabled = (YesNoMaybe)traitorSetting;

                int botCount = BotCount + incMsg.ReadByte() - 1;
                if (botCount < 0)
                {
                    botCount = MaxBotCount;
                }
                if (botCount > MaxBotCount)
                {
                    botCount = 0;
                }
                BotCount = botCount;

                int botSpawnMode = (int)BotSpawnMode + incMsg.ReadByte() - 1;
                if (botSpawnMode < 0)
                {
                    botSpawnMode = 1;
                }
                if (botSpawnMode > 1)
                {
                    botSpawnMode = 0;
                }
                BotSpawnMode = (BotSpawnMode)botSpawnMode;

                float levelDifficulty = incMsg.ReadSingle();
                if (levelDifficulty >= 0.0f)
                {
                    SelectedLevelDifficulty = levelDifficulty;
                }

                UseRespawnShuttle = incMsg.ReadBoolean();

                bool changedAutoRestart = incMsg.ReadBoolean();
                bool autoRestart        = incMsg.ReadBoolean();
                if (changedAutoRestart)
                {
                    AutoRestart = autoRestart;
                }

                changed |= true;
            }

            if (flags.HasFlag(NetFlags.LevelSeed))
            {
                GameMain.NetLobbyScreen.LevelSeed = incMsg.ReadString();
                changed |= true;
            }

            if (changed)
            {
                if (KarmaPreset == "custom")
                {
                    GameMain.NetworkMember?.KarmaManager?.SaveCustomPreset();
                    GameMain.NetworkMember?.KarmaManager?.Save();
                }
                SaveSettings();
                GameMain.NetLobbyScreen.LastUpdateID++;
            }
        }
        public virtual void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
        {
            if (GameMain.Server == null)
            {
                return;
            }

            switch (type)
            {
            case ClientNetObject.CHARACTER_INPUT:

                if (c.Character != this)
                {
#if DEBUG
                    DebugConsole.Log("Received a character update message from a client who's not controlling the character");
#endif
                    return;
                }

                UInt16 networkUpdateID = msg.ReadUInt16();
                byte   inputCount      = msg.ReadByte();

                if (AllowInput)
                {
                    Enabled = true;
                }

                for (int i = 0; i < inputCount; i++)
                {
                    InputNetFlags newInput    = (InputNetFlags)msg.ReadRangedInteger(0, (int)InputNetFlags.MaxVal);
                    UInt16        newAim      = 0;
                    UInt16        newInteract = 0;

                    if (newInput != InputNetFlags.None && newInput != InputNetFlags.FacingLeft)
                    {
                        c.KickAFKTimer = 0.0f;
                    }
                    else if (AnimController.Dir < 0.0f != newInput.HasFlag(InputNetFlags.FacingLeft))
                    {
                        //character changed the direction they're facing
                        c.KickAFKTimer = 0.0f;
                    }

                    newAim = msg.ReadUInt16();
                    if (newInput.HasFlag(InputNetFlags.Select) ||
                        newInput.HasFlag(InputNetFlags.Deselect) ||
                        newInput.HasFlag(InputNetFlags.Use) ||
                        newInput.HasFlag(InputNetFlags.Health) ||
                        newInput.HasFlag(InputNetFlags.Grab))
                    {
                        newInteract = msg.ReadUInt16();
                    }

                    if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60))
                    {
                        if ((i > 0 && memInput[i - 1].intAim != newAim))
                        {
                            c.KickAFKTimer = 0.0f;
                        }
                        NetInputMem newMem = new NetInputMem
                        {
                            states          = newInput,
                            intAim          = newAim,
                            interact        = newInteract,
                            networkUpdateID = (ushort)(networkUpdateID - i)
                        };
                        memInput.Insert(i, newMem);
                        LastInputTime = Timing.TotalTime;
                    }
                }

                if (NetIdUtils.IdMoreRecent(networkUpdateID, LastNetworkUpdateID))
                {
                    LastNetworkUpdateID = networkUpdateID;
                }
                else if (NetIdUtils.Difference(networkUpdateID, LastNetworkUpdateID) > 500)
                {
#if DEBUG || UNSTABLE
                    DebugConsole.AddWarning($"Large disrepancy between a client character's network update ID server-side and client-side (client: {networkUpdateID}, server: {LastNetworkUpdateID}). Resetting the ID.");
#endif
                    LastNetworkUpdateID = networkUpdateID;
                }
                if (memInput.Count > 60)
                {
                    //deleting inputs from the queue here means the server is way behind and data needs to be dropped
                    //we'll make the server drop down to 30 inputs for good measure
                    memInput.RemoveRange(30, memInput.Count - 30);
                }
                break;

            case ClientNetObject.ENTITY_STATE:
                int eventType = msg.ReadRangedInteger(0, 3);
                switch (eventType)
                {
                case 0:
                    Inventory.ServerRead(type, msg, c);
                    break;

                case 1:
                    bool doingCPR = msg.ReadBoolean();
                    if (c.Character != this)
                    {
#if DEBUG
                        DebugConsole.Log("Received a character update message from a client who's not controlling the character");
#endif
                        return;
                    }

                    AnimController.Anim = doingCPR ? AnimController.Animation.CPR : AnimController.Animation.None;
                    break;

                case 2:
                    if (c.Character != this)
                    {
#if DEBUG
                        DebugConsole.Log("Received a character update message from a client who's not controlling the character");
#endif
                        return;
                    }

                    if (IsIncapacitated)
                    {
                        var causeOfDeath = CharacterHealth.GetCauseOfDeath();
                        Kill(causeOfDeath.First, causeOfDeath.Second);
                    }
                    break;
                }
                break;
            }
            msg.ReadPadBits();
        }
        public void ServerRead(IReadMessage msg, Client sender)
        {
            UInt16 currentLocIndex  = msg.ReadUInt16();
            UInt16 selectedLocIndex = msg.ReadUInt16();

            byte       selectedMissionCount   = msg.ReadByte();
            List <int> selectedMissionIndices = new List <int>();

            for (int i = 0; i < selectedMissionCount; i++)
            {
                selectedMissionIndices.Add(msg.ReadByte());
            }

            bool purchasedHullRepairs  = msg.ReadBoolean();
            bool purchasedItemRepairs  = msg.ReadBoolean();
            bool purchasedLostShuttles = msg.ReadBoolean();

            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 purchasedUpgradeCount = msg.ReadUInt16();
            List <PurchasedUpgrade> purchasedUpgrades = new List <PurchasedUpgrade>();

            for (int i = 0; i < purchasedUpgradeCount; i++)
            {
                string        upgradeIdentifier = msg.ReadString();
                UpgradePrefab prefab            = UpgradePrefab.Find(upgradeIdentifier);

                string          categoryIdentifier = msg.ReadString();
                UpgradeCategory category           = UpgradeCategory.Find(categoryIdentifier);

                int upgradeLevel = msg.ReadByte();

                if (category == null || prefab == null)
                {
                    continue;
                }
                purchasedUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel));
            }

            ushort purchasedItemSwapCount = msg.ReadUInt16();
            List <PurchasedItemSwap> purchasedItemSwaps = new List <PurchasedItemSwap>();

            for (int i = 0; i < purchasedItemSwapCount; i++)
            {
                UInt16 itemToRemoveID = msg.ReadUInt16();
                Item   itemToRemove   = Entity.FindEntityByID(itemToRemoveID) as Item;

                string     itemToInstallIdentifier = msg.ReadString();
                ItemPrefab itemToInstall           = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier);

                if (itemToRemove == null)
                {
                    continue;
                }

                purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall));
            }

            if (!AllowedToManageCampaign(sender))
            {
                DebugConsole.ThrowError("Client \"" + sender.Name + "\" does not have a permission to manage the campaign");
                return;
            }

            Location location            = Map.CurrentLocation;
            int      hullRepairCost      = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost;
            int      itemRepairCost      = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost;
            int      shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost;

            if (purchasedHullRepairs != this.PurchasedHullRepairs)
            {
                if (purchasedHullRepairs && Money >= hullRepairCost)
                {
                    this.PurchasedHullRepairs = true;
                    Money -= hullRepairCost;
                }
                else if (!purchasedHullRepairs)
                {
                    this.PurchasedHullRepairs = false;
                    Money += hullRepairCost;
                }
            }
            if (purchasedItemRepairs != this.PurchasedItemRepairs)
            {
                if (purchasedItemRepairs && Money >= itemRepairCost)
                {
                    this.PurchasedItemRepairs = true;
                    Money -= itemRepairCost;
                }
                else if (!purchasedItemRepairs)
                {
                    this.PurchasedItemRepairs = false;
                    Money += itemRepairCost;
                }
            }
            if (purchasedLostShuttles != this.PurchasedLostShuttles)
            {
                if (GameMain.GameSession?.SubmarineInfo != null &&
                    GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
                {
                    GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
                }
                else if (purchasedLostShuttles && Money >= shuttleRetrieveCost)
                {
                    this.PurchasedLostShuttles = true;
                    Money -= shuttleRetrieveCost;
                }
                else if (!purchasedItemRepairs)
                {
                    this.PurchasedLostShuttles = false;
                    Money += shuttleRetrieveCost;
                }
            }

            if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport)
            {
                Map.SetLocation(currentLocIndex);
            }

            Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
            if (Map.SelectedLocation == null)
            {
                Map.SelectRandomLocation(preferUndiscovered: true);
            }
            if (Map.SelectedConnection != null)
            {
                Map.SelectMission(selectedMissionIndices);
            }

            CheckTooManyMissions(Map.CurrentLocation, sender);

            List <PurchasedItem> currentBuyCrateItems = new List <PurchasedItem>(CargoManager.ItemsInBuyCrate);

            currentBuyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, -i.Quantity));
            buyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, i.Quantity));

            CargoManager.SellBackPurchasedItems(new List <PurchasedItem>(CargoManager.PurchasedItems));
            CargoManager.PurchaseItems(purchasedItems, false);

            // for some reason CargoManager.SoldItem is never cleared by the server, I've added a check to SellItems that ignores all
            // sold items that are removed so they should be discarded on the next message
            CargoManager.BuyBackSoldItems(new List <SoldItem>(CargoManager.SoldItems));
            CargoManager.SellItems(soldItems);

            foreach (var(prefab, category, _) in purchasedUpgrades)
            {
                UpgradeManager.PurchaseUpgrade(prefab, category);

                // unstable logging
                int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation);
                int level = UpgradeManager.GetUpgradeLevel(prefab, category);
                GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage);
            }

            foreach (var purchasedItemSwap in purchasedItemSwaps)
            {
                if (purchasedItemSwap.ItemToInstall == null)
                {
                    UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove);
                }
                else
                {
                    UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall);
                }
            }
            foreach (Item item in Item.ItemList)
            {
                if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item))
                {
                    UpgradeManager.CancelItemSwap(item);
                    item.PendingItemSwap = null;
                }
            }
        }
Example #29
0
        public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
        {
            bool isBallastFloraUpdate = message.ReadBoolean();

            if (isBallastFloraUpdate)
            {
                BallastFloraBehavior.NetworkHeader header = (BallastFloraBehavior.NetworkHeader)message.ReadByte();
                if (header == BallastFloraBehavior.NetworkHeader.Spawn)
                {
                    string identifier = message.ReadString();
                    float  x          = message.ReadSingle();
                    float  y          = message.ReadSingle();
                    BallastFlora = new BallastFloraBehavior(this, BallastFloraPrefab.Find(identifier), new Vector2(x, y), firstGrowth: true)
                    {
                        PowerConsumptionTimer = message.ReadSingle()
                    };
                }
                else
                {
                    BallastFlora?.ClientRead(message, header);
                }
                return;
            }
            remoteWaterVolume      = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
            remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);

            bool hasFireSources = message.ReadBoolean();

            remoteFireSources = new List <Vector3>();
            if (hasFireSources)
            {
                int fireSourceCount = message.ReadRangedInteger(0, 16);
                for (int i = 0; i < fireSourceCount; i++)
                {
                    remoteFireSources.Add(new Vector3(
                                              MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
                                              MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
                                              message.ReadRangedSingle(0.0f, 1.0f, 8)));
                }
            }

            bool hasExtraData = message.ReadBoolean();

            if (hasExtraData)
            {
                bool hasSectionUpdate = message.ReadBoolean();
                if (hasSectionUpdate)
                {
                    int sectorToUpdate = message.ReadRangedInteger(0, BackgroundSections.Count - 1);
                    int start          = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
                    int end            = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
                    for (int i = start; i < end; i++)
                    {
                        float colorStrength           = message.ReadRangedSingle(0.0f, 1.0f, 8);
                        Color color                   = new Color(message.ReadUInt32());
                        var   remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
                        if (remoteBackgroundSection != null)
                        {
                            remoteBackgroundSection.SetColorStrength(colorStrength);
                            remoteBackgroundSection.SetColor(color);
                        }
                        else
                        {
                            remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), i, colorStrength, color, 0));
                        }
                    }
                    paintAmount = BackgroundSections.Sum(s => s.ColorStrength);
                }
                else
                {
                    int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull);
                    if (decalCount == 0)
                    {
                        decals.Clear();
                    }
                    remoteDecals.Clear();
                    for (int i = 0; i < decalCount; i++)
                    {
                        UInt32 decalId        = message.ReadUInt32();
                        int    spriteIndex    = message.ReadByte();
                        float  normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
                        float  normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
                        float  decalScale     = message.ReadRangedSingle(0.0f, 2.0f, 12);
                        remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale));
                    }
                }
            }

            if (serverUpdateDelay > 0.0f)
            {
                return;
            }

            ApplyRemoteState();
        }
        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 as EnemyAIController)?.PetBehavior?.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, 5);
                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
                        UInt16 lastEventID = msg.ReadUInt16();
                        byte   itemCount   = msg.ReadByte();
                        for (int i = 0; i < itemCount; 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;
                    }
                    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, WorldPosition + Vector2.UnitY * 150.0f);
                    }
                    break;

                case 4:         //NetEntityEvent.Type.ExecuteAttack
                    int    attackLimbIndex = msg.ReadByte();
                    UInt16 targetEntityID  = msg.ReadUInt16();
                    int    targetLimbIndex = msg.ReadByte();

                    //255 = entity already removed, no need to do anything
                    if (attackLimbIndex == 255 || Removed)
                    {
                        break;
                    }

                    if (attackLimbIndex >= AnimController.Limbs.Length)
                    {
                        DebugConsole.ThrowError($"Received invalid 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 ExecuteAttack message. Target entity not found (ID {targetEntityID})");
                        break;
                    }
                    if (targetEntity is Character targetCharacter)
                    {
                        if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
                        {
                            DebugConsole.ThrowError($"Received invalid 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)
                    {
                        attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
                    }
                    break;

                case 5:         //NetEntityEvent.Type.AssignCampaignInteraction
                    byte campaignInteractionType = msg.ReadByte();
                    (GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
                    break;
                }
                msg.ReadPadBits();
                break;
            }
        }