示例#1
0
        partial void EndReadyCheck()
        {
            if (IsFinished)
            {
                return;
            }
            IsFinished = true;
            foreach (Client client in ActivePlayers)
            {
                if (client != null && !client.Spectating)
                {
                    IWriteMessage msg = new WriteOnlyMessage();
                    msg.Write((byte)ServerPacketHeader.READY_CHECK);
                    msg.Write((byte)ReadyCheckState.End);
                    msg.Write((ushort)Clients.Count);
                    foreach (var(id, state) in Clients)
                    {
                        msg.Write(id);
                        msg.Write((byte)state);
                    }

                    GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
                }
            }
        }
示例#2
0
        /// <summary>
        /// Notify the server of crew changes
        /// </summary>
        /// <param name="updatePending">When set to true will tell the server to update the pending hires</param>
        /// <param name="firedCharacter">When not null tell the server to fire this character</param>
        /// <param name="validateHires">When set to true will tell the server to validate pending hires</param>
        public void SendCrewState(bool updatePending, CharacterInfo firedCharacter = null, bool validateHires = false)
        {
            if (campaign is MultiPlayerCampaign)
            {
                IWriteMessage msg = new WriteOnlyMessage();
                msg.Write((byte)ClientPacketHeader.CREW);

                msg.Write(updatePending);
                if (updatePending)
                {
                    msg.Write((ushort)PendingHires.Count);
                    foreach (CharacterInfo pendingHire in PendingHires)
                    {
                        msg.Write(pendingHire.GetIdentifier());
                    }
                }

                msg.Write(validateHires);

                msg.Write(firedCharacter != null);
                if (firedCharacter != null)
                {
                    msg.Write(firedCharacter.GetIdentifier());
                }

                GameMain.Client.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
            }
        }
示例#3
0
        public void InitializeReadyCheck(string author, Client?sender = null)
        {
            foreach (Client client in ActivePlayers)
            {
                if (client != null && !client.Spectating)
                {
                    IWriteMessage msg = new WriteOnlyMessage();
                    msg.Write((byte)ServerPacketHeader.READY_CHECK);
                    msg.Write((byte)ReadyCheckState.Start);
                    msg.Write(endTime);
                    msg.Write(author);

                    if (sender != null)
                    {
                        msg.Write(true);
                        msg.Write(sender.ID);
                    }
                    else
                    {
                        msg.Write(false);
                    }

                    msg.Write((ushort)ActivePlayers.Count);
                    foreach (byte clientId in Clients.Keys)
                    {
                        msg.Write(clientId);
                    }

                    GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
                }
            }
        }
示例#4
0
        private static void SendState(ReadyStatus status)
        {
            IWriteMessage msg = new WriteOnlyMessage();

            msg.Write((byte)ClientPacketHeader.READY_CHECK);
            msg.Write((byte)ReadyCheckState.Update);
            msg.Write((byte)status);
            GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
        }
示例#5
0
        public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
        {
            msg.Write(ID);
            IWriteMessage tempBuffer = new WriteOnlyMessage();

            subBody.Body.ServerWrite(tempBuffer, c, extraData);
            msg.Write((byte)tempBuffer.LengthBytes);
            msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
            msg.WritePadBits();
        }
示例#6
0
        public void ServerWritePosition(IWriteMessage msg, Client c, object[] extraData = null)
        {
            msg.Write(ID);

            IWriteMessage tempBuffer = new WriteOnlyMessage();

            body.ServerWrite(tempBuffer, c, extraData);
            msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
            msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
            msg.WritePadBits();
        }
示例#7
0
 private void NotifyUnlock(LocationConnection connection)
 {
     foreach (Client client in GameMain.Server.ConnectedClients)
     {
         IWriteMessage outmsg = new WriteOnlyMessage();
         outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
         outmsg.Write((byte)EventManager.NetworkEventType.UNLOCKPATH);
         outmsg.Write((UInt16)GameMain.GameSession.Map.Connections.IndexOf(connection));
         GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
     }
 }
示例#8
0
 private void NotifyMissionUnlock(MissionPrefab prefab)
 {
     foreach (Client client in GameMain.Server.ConnectedClients)
     {
         IWriteMessage outmsg = new WriteOnlyMessage();
         outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
         outmsg.Write((byte)EventManager.NetworkEventType.MISSION);
         outmsg.Write(prefab.Identifier);
         GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
     }
 }
 private void SendUpgradeResetMessage(Dictionary <string, int> newUpgrades)
 {
     foreach (Client c in GameMain.Server.ConnectedClients)
     {
         IWriteMessage outmsg = new WriteOnlyMessage();
         outmsg.Write((byte)ServerPacketHeader.RESET_UPGRADES);
         outmsg.Write(true);
         outmsg.Write(Campaign.Money);
         // outmsg.Write((uint)newUpgrades.Count);
         // foreach (var (key, value) in newUpgrades)
         // {
         //     outmsg.Write(key);
         //     outmsg.Write((byte)value);
         // }
         GameMain.Server?.ServerPeer?.Send(outmsg, c.Connection, DeliveryMethod.Reliable);
     }
 }
示例#10
0
        private void ServerWrite(IEnumerable <Entity> targets)
        {
            IWriteMessage outmsg = new WriteOnlyMessage();

            outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
            outmsg.Write((byte)EventManager.NetworkEventType.STATUSEFFECT);
            outmsg.Write(ParentEvent.Prefab.Identifier);
            outmsg.Write((UInt16)actionIndex);
            outmsg.Write((UInt16)targets.Count());
            foreach (Entity target in targets)
            {
                outmsg.Write(target.ID);
            }
            foreach (Client c in GameMain.Server.ConnectedClients)
            {
                GameMain.Server.ServerPeer?.Send(outmsg, c.Connection, DeliveryMethod.Reliable);
            }
        }
示例#11
0
        private void UpdateReadyCheck(byte otherClient, ReadyStatus state)
        {
            if (Clients.All(pair => pair.Value != ReadyStatus.Unanswered))
            {
                EndReadyCheck();
                return;
            }

            foreach (Client client in ActivePlayers)
            {
                IWriteMessage msg = new WriteOnlyMessage();
                msg.Write((byte)ServerPacketHeader.READY_CHECK);
                msg.Write((byte)ReadyCheckState.Update);
                msg.Write(time); // sync time
                msg.Write((byte)state);
                msg.Write(otherClient);
                GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
            }
        }
示例#12
0
        public static void CreateReadyCheck()
        {
            if (lastReadyCheck < DateTime.Now)
            {
#if !DEBUG
                lastReadyCheck = DateTime.Now.AddMinutes(1);
#endif
                IWriteMessage msg = new WriteOnlyMessage();
                msg.Write((byte)ClientPacketHeader.READY_CHECK);
                msg.Write((byte)ReadyCheckState.Start);
                GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
                return;
            }

            GUIMessageBox msgBox = new GUIMessageBox(readyCheckHeader, readyCheckPleaseWait((lastReadyCheck - DateTime.Now).Seconds), new[] { closeButton });
            msgBox.Buttons[0].OnClicked = delegate
            {
                msgBox.Close();
                return(true);
            };
        }
示例#13
0
        /// <summary>
        /// Notifies the clients of the current bot situation like syncing pending and available hires
        /// available hires are also synced
        /// </summary>
        /// <param name="validateHires">When set to true notifies the clients that the hires have been validated.</param>
        /// <param name="firedCharacter">When not null will inform the clients that his character has been fired.</param>
        /// <remarks>
        /// It might be obsolete to sync available hires. I found that the available hires are always the same between
        /// the client and the server when there's only one person on the server but when a second person joins both of
        /// their available hires are different from the server.
        /// </remarks>
        public void SendCrewState(bool validateHires, CharacterInfo firedCharacter)
        {
            List <CharacterInfo> availableHires = new List <CharacterInfo>();
            List <CharacterInfo> pendingHires   = new List <CharacterInfo>();

            if (map.CurrentLocation != null && map.CurrentLocation.Type.HasHireableCharacters)
            {
                availableHires = map.CurrentLocation.GetHireableCharacters().ToList();
                pendingHires   = map.CurrentLocation?.HireManager.PendingHires;
            }

            foreach (Client client in GameMain.Server.ConnectedClients)
            {
                IWriteMessage msg = new WriteOnlyMessage();
                msg.Write((byte)ServerPacketHeader.CREW);

                msg.Write((ushort)availableHires.Count);
                foreach (CharacterInfo hire in availableHires)
                {
                    hire.ServerWrite(msg);
                    msg.Write(hire.Salary);
                }

                msg.Write((ushort)pendingHires.Count);
                foreach (CharacterInfo pendingHire in pendingHires)
                {
                    msg.Write(pendingHire.GetIdentifier());
                }

                msg.Write(validateHires);

                msg.Write(firedCharacter != null);
                if (firedCharacter != null)
                {
                    msg.Write(firedCharacter.GetIdentifier());
                }

                GameMain.Server.ServerPeer.Send(msg, client.Connection, DeliveryMethod.Reliable);
            }
        }
        private void ServerWrite(Character speaker, Client client)
        {
            IWriteMessage outmsg = new WriteOnlyMessage();

            outmsg.Write((byte)ServerPacketHeader.EVENTACTION);
            outmsg.Write((byte)EventManager.NetworkEventType.CONVERSATION);
            outmsg.Write(Identifier);
            outmsg.Write(EventSprite);
            outmsg.Write((byte)DialogType);
            outmsg.Write(ContinueConversation);
            if (interrupt)
            {
                outmsg.Write(speaker?.ID ?? Entity.NullEntityID);
                outmsg.Write(string.Empty);
                outmsg.Write(false);
                outmsg.Write((byte)0);
                outmsg.Write((byte)0);
            }
            else
            {
                outmsg.Write(speaker?.ID ?? Entity.NullEntityID);
                outmsg.Write(Text ?? string.Empty);
                outmsg.Write(FadeToBlack);
                outmsg.Write((byte)Options.Count);
                for (int i = 0; i < Options.Count; i++)
                {
                    outmsg.Write(Options[i].Text);
                }

                int[] endings = GetEndingOptions();
                outmsg.Write((byte)endings.Length);
                foreach (var end in endings)
                {
                    outmsg.Write((byte)end);
                }
            }
            GameMain.Server?.ServerPeer?.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
        }
        public virtual void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
        {
            if (GameMain.Server == null)
            {
                return;
            }

            if (extraData != null)
            {
                switch ((NetEntityEvent.Type)extraData[0])
                {
                case NetEntityEvent.Type.InventoryState:
                    msg.WriteRangedInteger(0, 0, 5);
                    msg.Write(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
                    Inventory.ServerWrite(msg, c);
                    break;

                case NetEntityEvent.Type.Control:
                    msg.WriteRangedInteger(1, 0, 5);
                    Client owner = (Client)extraData[1];
                    msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0);
                    break;

                case NetEntityEvent.Type.Status:
                    msg.WriteRangedInteger(2, 0, 5);
                    WriteStatus(msg);
                    break;

                case NetEntityEvent.Type.UpdateSkills:
                    msg.WriteRangedInteger(3, 0, 5);
                    if (Info?.Job == null)
                    {
                        msg.Write((byte)0);
                    }
                    else
                    {
                        msg.Write((byte)Info.Job.Skills.Count);
                        foreach (Skill skill in Info.Job.Skills)
                        {
                            msg.Write(skill.Identifier);
                            msg.Write(skill.Level);
                        }
                    }
                    break;

                case NetEntityEvent.Type.ExecuteAttack:
                    Limb   attackLimb      = extraData[1] as Limb;
                    UInt16 targetEntityID  = (UInt16)extraData[2];
                    int    targetLimbIndex = extraData.Length > 3 ? (int)extraData[3] : 0;
                    msg.WriteRangedInteger(4, 0, 5);
                    msg.Write((byte)(Removed ? 255 : Array.IndexOf(AnimController.Limbs, attackLimb)));
                    msg.Write(targetEntityID);
                    msg.Write((byte)targetLimbIndex);
                    break;

                case NetEntityEvent.Type.AssignCampaignInteraction:
                    msg.WriteRangedInteger(5, 0, 5);
                    msg.Write((byte)CampaignInteractionType);
                    break;

                default:
                    DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")");
                    break;
                }
                msg.WritePadBits();
            }
            else
            {
                msg.Write(ID);

                IWriteMessage tempBuffer = new WriteOnlyMessage();

                if (this == c.Character)
                {
                    tempBuffer.Write(true);
                    if (LastNetworkUpdateID < memInput.Count + 1)
                    {
                        tempBuffer.Write((UInt16)0);
                    }
                    else
                    {
                        tempBuffer.Write((UInt16)(LastNetworkUpdateID - memInput.Count - 1));
                    }
                }
                else
                {
                    tempBuffer.Write(false);

                    bool aiming = false;
                    bool use    = false;
                    bool attack = false;
                    bool shoot  = false;

                    if (IsRemotePlayer)
                    {
                        aiming = dequeuedInput.HasFlag(InputNetFlags.Aim);
                        use    = dequeuedInput.HasFlag(InputNetFlags.Use);
                        attack = dequeuedInput.HasFlag(InputNetFlags.Attack);
                        shoot  = dequeuedInput.HasFlag(InputNetFlags.Shoot);
                    }
                    else if (keys != null)
                    {
                        aiming            = keys[(int)InputType.Aim].GetHeldQueue;
                        use               = keys[(int)InputType.Use].GetHeldQueue;
                        attack            = keys[(int)InputType.Attack].GetHeldQueue;
                        shoot             = keys[(int)InputType.Shoot].GetHeldQueue;
                        networkUpdateSent = true;
                    }

                    tempBuffer.Write(aiming);
                    tempBuffer.Write(shoot);
                    tempBuffer.Write(use);
                    if (AnimController is HumanoidAnimController)
                    {
                        tempBuffer.Write(((HumanoidAnimController)AnimController).Crouching);
                    }
                    tempBuffer.Write(attack);

                    Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
                    tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI)));

                    tempBuffer.Write(IsRagdolled || Stun > 0.0f || IsDead || IsIncapacitated);

                    tempBuffer.Write(AnimController.Dir > 0.0f);
                }

                if (SelectedCharacter != null || SelectedConstruction != null)
                {
                    tempBuffer.Write(true);
                    tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID);
                    tempBuffer.Write(SelectedConstruction != null ? SelectedConstruction.ID : NullEntityID);
                    if (SelectedCharacter != null)
                    {
                        tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR);
                    }
                }
                else
                {
                    tempBuffer.Write(false);
                }

                tempBuffer.Write(SimPosition.X);
                tempBuffer.Write(SimPosition.Y);
                float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
                AnimController.Collider.LinearVelocity = new Vector2(
                    MathHelper.Clamp(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel),
                    MathHelper.Clamp(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel));
                tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12);
                tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12);

                bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation || !AnimController.Collider.PhysEnabled;
                tempBuffer.Write(fixedRotation);
                if (!fixedRotation)
                {
                    tempBuffer.Write(AnimController.Collider.Rotation);
                    float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
                    AnimController.Collider.AngularVelocity = NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
                    tempBuffer.WriteRangedSingle(MathHelper.Clamp(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel), -MaxAngularVel, MaxAngularVel, 8);
                }

                bool writeStatus = healthUpdateTimer <= 0.0f;
                tempBuffer.Write(writeStatus);
                if (writeStatus)
                {
                    WriteStatus(tempBuffer);
                    HealthUpdatePending = false;
                }

                tempBuffer.WritePadBits();

                msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
                msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
            }
        }
        public virtual void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
        {
            if (GameMain.Server == null)
            {
                return;
            }

            if (extraData != null)
            {
                const int min = 0, max = 13;
                switch ((NetEntityEvent.Type)extraData[0])
                {
                case NetEntityEvent.Type.InventoryState:
                    msg.WriteRangedInteger(0, min, max);
                    msg.Write(GameMain.Server.EntityEventManager.Events.Last()?.ID ?? (ushort)0);
                    Inventory.ServerWrite(msg, c);
                    break;

                case NetEntityEvent.Type.Control:
                    msg.WriteRangedInteger(1, min, max);
                    Client owner = (Client)extraData[1];
                    msg.Write(owner != null && owner.Character == this && GameMain.Server.ConnectedClients.Contains(owner) ? owner.ID : (byte)0);
                    break;

                case NetEntityEvent.Type.Status:
                    msg.WriteRangedInteger(2, min, max);
                    WriteStatus(msg);
                    break;

                case NetEntityEvent.Type.UpdateSkills:
                    msg.WriteRangedInteger(3, min, max);
                    if (Info?.Job == null)
                    {
                        msg.Write((byte)0);
                    }
                    else
                    {
                        msg.Write((byte)Info.Job.Skills.Count);
                        foreach (Skill skill in Info.Job.Skills)
                        {
                            msg.Write(skill.Identifier);
                            msg.Write(skill.Level);
                        }
                    }
                    break;

                case NetEntityEvent.Type.SetAttackTarget:
                case NetEntityEvent.Type.ExecuteAttack:
                    Limb   attackLimb      = extraData[1] as Limb;
                    UInt16 targetEntityID  = (UInt16)extraData[2];
                    int    targetLimbIndex = extraData.Length > 3 ? (int)extraData[3] : 0;
                    msg.WriteRangedInteger(extraData[0] is NetEntityEvent.Type.SetAttackTarget ? 4 : 5, min, max);
                    msg.Write((byte)(Removed ? 255 : Array.IndexOf(AnimController.Limbs, attackLimb)));
                    msg.Write(targetEntityID);
                    msg.Write((byte)targetLimbIndex);
                    msg.Write(extraData.Length > 4 ? (float)extraData[4] : 0);
                    msg.Write(extraData.Length > 5 ? (float)extraData[5] : 0);
                    break;

                case NetEntityEvent.Type.AssignCampaignInteraction:
                    msg.WriteRangedInteger(6, min, max);
                    msg.Write((byte)CampaignInteractionType);
                    msg.Write(RequireConsciousnessForCustomInteract);
                    break;

                case NetEntityEvent.Type.ObjectiveManagerState:
                    msg.WriteRangedInteger(7, min, max);
                    int type = (extraData[1] as string) switch
                    {
                        "order" => 1,
                        "objective" => 2,
                        _ => 0
                    };
                    msg.WriteRangedInteger(type, 0, 2);
                    if (!(AIController is HumanAIController controller))
                    {
                        msg.Write(false);
                        break;
                    }
                    if (type == 1)
                    {
                        var  currentOrderInfo = controller.ObjectiveManager.GetCurrentOrderInfo();
                        bool validOrder       = currentOrderInfo.HasValue;
                        msg.Write(validOrder);
                        if (!validOrder)
                        {
                            break;
                        }
                        var orderPrefab = currentOrderInfo.Value.Order.Prefab;
                        int orderIndex  = Order.PrefabList.IndexOf(orderPrefab);
                        msg.WriteRangedInteger(orderIndex, 0, Order.PrefabList.Count);
                        if (!orderPrefab.HasOptions)
                        {
                            break;
                        }
                        int optionIndex = orderPrefab.AllOptions.IndexOf(currentOrderInfo.Value.OrderOption);
                        if (optionIndex == -1)
                        {
                            DebugConsole.AddWarning($"Error while writing order data. Order option \"{(currentOrderInfo.Value.OrderOption ?? null)}\" not found in the order prefab \"{orderPrefab.Name}\".");
                        }
                        msg.WriteRangedInteger(optionIndex, -1, orderPrefab.AllOptions.Length);
                    }
                    else if (type == 2)
                    {
                        var  objective      = controller.ObjectiveManager.CurrentObjective;
                        bool validObjective = !string.IsNullOrEmpty(objective?.Identifier);
                        msg.Write(validObjective);
                        if (!validObjective)
                        {
                            break;
                        }
                        msg.Write(objective.Identifier);
                        msg.Write(objective.Option ?? "");
                        UInt16 targetEntityId = 0;
                        if (objective is AIObjectiveOperateItem operateObjective && operateObjective.OperateTarget != null)
                        {
                            targetEntityId = operateObjective.OperateTarget.ID;
                        }
                        msg.Write(targetEntityId);
                    }
                    break;

                case NetEntityEvent.Type.TeamChange:
                    msg.WriteRangedInteger(8, min, max);
                    msg.Write((byte)TeamID);
                    break;

                case NetEntityEvent.Type.AddToCrew:
                    msg.WriteRangedInteger(9, min, max);
                    msg.Write((byte)(CharacterTeamType)extraData[1]);     // team id
                    ushort[] inventoryItemIDs = (ushort[])extraData[2];
                    msg.Write((ushort)inventoryItemIDs.Length);
                    for (int i = 0; i < inventoryItemIDs.Length; i++)
                    {
                        msg.Write(inventoryItemIDs[i]);
                    }
                    break;

                case NetEntityEvent.Type.UpdateExperience:
                    msg.WriteRangedInteger(10, min, max);
                    msg.Write(Info.ExperiencePoints);
                    break;

                case NetEntityEvent.Type.UpdateTalents:
                    msg.WriteRangedInteger(11, min, max);
                    msg.Write((ushort)characterTalents.Count);
                    foreach (var unlockedTalent in characterTalents)
                    {
                        msg.Write(unlockedTalent.AddedThisRound);
                        msg.Write(unlockedTalent.Prefab.UIntIdentifier);
                    }
                    break;

                case NetEntityEvent.Type.UpdateMoney:
                    msg.WriteRangedInteger(12, min, max);
                    msg.Write(GameMain.GameSession.Campaign.Money);
                    break;

                case NetEntityEvent.Type.UpdatePermanentStats:
                    msg.WriteRangedInteger(13, min, max);
                    if (Info == null || extraData.Length < 2 || !(extraData[1] is StatTypes statType))
                    {
                        msg.Write((byte)0);
                        msg.Write((byte)0);
                    }
                    else if (!Info.SavedStatValues.ContainsKey(statType))
                    {
                        msg.Write((byte)0);
                        msg.Write((byte)statType);
                    }
                    else
                    {
                        msg.Write((byte)Info.SavedStatValues[statType].Count);
                        msg.Write((byte)statType);
                        foreach (var savedStatValue in Info.SavedStatValues[statType])
                        {
                            msg.Write(savedStatValue.StatIdentifier);
                            msg.Write(savedStatValue.StatValue);
                            msg.Write(savedStatValue.RemoveOnDeath);
                        }
                    }
                    break;

                default:
                    DebugConsole.ThrowError("Invalid NetworkEvent type for entity " + ToString() + " (" + (NetEntityEvent.Type)extraData[0] + ")");
                    break;
                }
                msg.WritePadBits();
            }
            else
            {
                msg.Write(ID);

                IWriteMessage tempBuffer = new WriteOnlyMessage();

                if (this == c.Character)
                {
                    tempBuffer.Write(true);
                    if (LastNetworkUpdateID < memInput.Count + 1)
                    {
                        tempBuffer.Write((UInt16)0);
                    }
                    else
                    {
                        tempBuffer.Write((UInt16)(LastNetworkUpdateID - memInput.Count - 1));
                    }
                }
                else
                {
                    tempBuffer.Write(false);

                    bool aiming = false;
                    bool use    = false;
                    bool attack = false;
                    bool shoot  = false;

                    if (IsRemotePlayer)
                    {
                        aiming = dequeuedInput.HasFlag(InputNetFlags.Aim);
                        use    = dequeuedInput.HasFlag(InputNetFlags.Use);
                        attack = dequeuedInput.HasFlag(InputNetFlags.Attack);
                        shoot  = dequeuedInput.HasFlag(InputNetFlags.Shoot);
                    }
                    else if (keys != null)
                    {
                        aiming            = keys[(int)InputType.Aim].GetHeldQueue;
                        use               = keys[(int)InputType.Use].GetHeldQueue;
                        attack            = keys[(int)InputType.Attack].GetHeldQueue;
                        shoot             = keys[(int)InputType.Shoot].GetHeldQueue;
                        networkUpdateSent = true;
                    }

                    tempBuffer.Write(aiming);
                    tempBuffer.Write(shoot);
                    tempBuffer.Write(use);
                    if (AnimController is HumanoidAnimController)
                    {
                        tempBuffer.Write(((HumanoidAnimController)AnimController).Crouching);
                    }
                    tempBuffer.Write(attack);

                    Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
                    tempBuffer.Write((UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI)));

                    tempBuffer.Write(IsRagdolled || Stun > 0.0f || IsDead || IsIncapacitated);

                    tempBuffer.Write(AnimController.Dir > 0.0f);
                }

                if (SelectedCharacter != null || SelectedConstruction != null)
                {
                    tempBuffer.Write(true);
                    tempBuffer.Write(SelectedCharacter != null ? SelectedCharacter.ID : NullEntityID);
                    tempBuffer.Write(SelectedConstruction != null ? SelectedConstruction.ID : NullEntityID);
                    if (SelectedCharacter != null)
                    {
                        tempBuffer.Write(AnimController.Anim == AnimController.Animation.CPR);
                    }
                }
                else
                {
                    tempBuffer.Write(false);
                }

                tempBuffer.Write(SimPosition.X);
                tempBuffer.Write(SimPosition.Y);
                float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
                AnimController.Collider.LinearVelocity = new Vector2(
                    MathHelper.Clamp(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel),
                    MathHelper.Clamp(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel));
                tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12);
                tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12);

                bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation || !AnimController.Collider.PhysEnabled;
                tempBuffer.Write(fixedRotation);
                if (!fixedRotation)
                {
                    tempBuffer.Write(AnimController.Collider.Rotation);
                    float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
                    AnimController.Collider.AngularVelocity = NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
                    tempBuffer.WriteRangedSingle(MathHelper.Clamp(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel), -MaxAngularVel, MaxAngularVel, 8);
                }

                bool writeStatus = healthUpdateTimer <= 0.0f;
                tempBuffer.Write(writeStatus);
                if (writeStatus)
                {
                    WriteStatus(tempBuffer);
                    AIController?.ServerWrite(tempBuffer);
                    HealthUpdatePending = false;
                }

                tempBuffer.WritePadBits();

                msg.WriteVariableUInt32((uint)tempBuffer.LengthBytes);
                msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
            }
        }