Exemplo n.º 1
0
        public void CreateEvent(IServerSerializable entity, object[] extraData = null)
        {
            if (entity == null || !(entity is Entity))
            {
                DebugConsole.ThrowError("Can't create an entity event for " + entity + "!");
                return;
            }

            var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1));

            if (extraData != null)
            {
                newEvent.SetData(extraData);
            }

            //remove events that have been sent to all clients, they are redundant now
            //keep at least one event in the list (lastSentToAll == e.ID) so we can use it to keep track of the latest ID
            events.RemoveAll(e => NetIdUtils.IdMoreRecent(lastSentToAll, e.ID));

            if (server.ConnectedClients.Count(c => c.InGame) == 0 && events.Count > 1)
            {
                events.RemoveRange(0, events.Count - 1);
            }

            for (int i = events.Count - 1; i >= 0; i--)
            {
                //we already have an identical event that's waiting to be sent
                // -> no need to add a new one
                if (events[i].IsDuplicate(newEvent) && !events[i].Sent)
                {
                    return;
                }
            }

            ID++;

            events.Add(newEvent);

            if (!uniqueEvents.Any(e => e.IsDuplicate(newEvent)))
            {
                //create a copy of the event and give it a new ID
                var uniqueEvent = new ServerEntityEvent(entity, (UInt16)(uniqueEvents.Count + 1));
                uniqueEvent.SetData(extraData);

                uniqueEvents.Add(uniqueEvent);
            }
        }
Exemplo n.º 2
0
        public static void ClientRead(NetIncomingMessage msg)
        {
            UInt16          ID   = msg.ReadUInt16();
            ChatMessageType type = (ChatMessageType)msg.ReadByte();
            string          txt  = msg.ReadString();

            string    senderName         = msg.ReadString();
            Character senderCharacter    = null;
            bool      hasSenderCharacter = msg.ReadBoolean();

            if (hasSenderCharacter)
            {
                senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                if (senderCharacter != null)
                {
                    senderName = senderCharacter.Name;
                }
            }

            if (NetIdUtils.IdMoreRecent(ID, LastID))
            {
                switch (type)
                {
                case ChatMessageType.MessageBox:
                    new GUIMessageBox("", txt);
                    break;

                case ChatMessageType.Console:
                    DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
                    break;

                case ChatMessageType.ServerLog:
                    if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
                    {
                        return;
                    }
                    GameMain.Client.ServerLog?.WriteLine(txt, messageType);
                    break;

                default:
                    GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
                    break;
                }

                LastID = ID;
            }
        }
Exemplo n.º 3
0
        public void Write(IWriteMessage msg, NetworkConnection serverConnection)
        {
            if (events.Count == 0 || serverConnection == null)
            {
                return;
            }

            List <NetEntityEvent> eventsToSync = new List <NetEntityEvent>();

            //find the index of the first event the server hasn't received
            int startIndex = events.Count;

            while (startIndex > 0 &&
                   NetIdUtils.IdMoreRecent(events[startIndex - 1].ID, thisClient.LastSentEntityEventID))
            {
                startIndex--;
            }

            //remove events the server has already received
            events.RemoveRange(0, startIndex);

            for (int i = 0; i < events.Count; i++)
            {
                //find the first event that hasn't been sent in roundtriptime or at all
                eventLastSent.TryGetValue(events[i].ID, out float lastSent);

                if (lastSent > Lidgren.Network.NetTime.Now - 0.2) //TODO: reimplement serverConnection.AverageRoundtripTime
                {
                    continue;
                }

                eventsToSync.AddRange(events.GetRange(i, events.Count - i));
                break;
            }
            if (eventsToSync.Count == 0)
            {
                return;
            }

            foreach (NetEntityEvent entityEvent in eventsToSync)
            {
                eventLastSent[entityEvent.ID] = (float)Lidgren.Network.NetTime.Now;
            }

            msg.Write((byte)ClientNetObject.ENTITY_STATE);
            Write(msg, eventsToSync, out _);
        }
Exemplo n.º 4
0
        public virtual bool Read(IReadMessage msg, bool discardData = false)
        {
            if (!CanReceive)
            {
                throw new Exception("Called Read on a VoipQueue not set up for receiving");
            }

            UInt16 incLatestBufferID = msg.ReadUInt16();

            if ((firstRead || NetIdUtils.IdMoreRecent(incLatestBufferID, LatestBufferID)) && !discardData)
            {
                ForceLocal = msg.ReadBoolean(); msg.ReadPadBits();

                firstRead = false;
                lock (buffers)
                {
                    for (int i = 0; i < BUFFER_COUNT; i++)
                    {
                        bufferLengths[i] = msg.ReadByte();
                        buffers[i]       = msg.ReadBytes(bufferLengths[i]);
                    }
                }
                newestBufferInd = BUFFER_COUNT - 1;
                LatestBufferID  = incLatestBufferID;
                LastReadTime    = DateTime.Now;
                return(true);
            }
            else
            {
                msg.ReadBoolean(); msg.ReadPadBits();
                for (int i = 0; i < BUFFER_COUNT; i++)
                {
                    byte len = msg.ReadByte();
                    msg.BitPosition += len * 8;
                }
                return(false);
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Returns a list of events that should be sent to the client from the eventList
        /// </summary>
        /// <param name="client"></param>
        /// <param name="eventList"></param>
        /// <returns></returns>
        private List <NetEntityEvent> GetEventsToSync(Client client, List <ServerEntityEvent> eventList)
        {
            List <NetEntityEvent> eventsToSync = new List <NetEntityEvent>();

            if (eventList.Count == 0)
            {
                return(eventsToSync);
            }

            //find the index of the first event the client hasn't received
            int startIndex = eventList.Count;

            while (startIndex > 0 &&
                   NetIdUtils.IdMoreRecent(eventList[startIndex - 1].ID, client.LastRecvEntityEventID))
            {
                startIndex--;
            }

            for (int i = startIndex; i < eventList.Count; i++)
            {
                //find the first event that hasn't been sent in roundtriptime or at all
                client.EntityEventLastSent.TryGetValue(eventList[i].ID, out double lastSent);

                float avgRoundtripTime = 0.01f; //TODO: reimplement client.Connection.AverageRoundtripTime
                float minInterval      = Math.Max(avgRoundtripTime, (float)server.UpdateInterval.TotalSeconds * 2);

                if (lastSent > Lidgren.Network.NetTime.Now - Math.Min(minInterval, 0.5f))
                {
                    continue;
                }

                eventsToSync.AddRange(eventList.GetRange(i, eventList.Count - i));
                break;
            }

            return(eventsToSync);
        }
Exemplo n.º 6
0
        public static void ClientRead(NetIncomingMessage msg)
        {
            UInt16          ID   = msg.ReadUInt16();
            ChatMessageType type = (ChatMessageType)msg.ReadByte();
            string          txt  = msg.ReadString();

            string    senderName         = "";
            Character senderCharacter    = null;
            bool      hasSenderCharacter = msg.ReadBoolean();

            if (hasSenderCharacter)
            {
                senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                if (senderCharacter != null)
                {
                    senderName = senderCharacter.Name;
                }
            }
            else
            {
                senderName = msg.ReadString();
            }

            if (NetIdUtils.IdMoreRecent(ID, LastID))
            {
                if (type == ChatMessageType.MessageBox)
                {
                    new GUIMessageBox("", txt);
                }
                else
                {
                    GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
                }
                LastID = ID;
            }
        }
Exemplo n.º 7
0
        public static void ClientRead(IReadMessage msg)
        {
            UInt16                     id           = msg.ReadUInt16();
            ChatMessageType            type         = (ChatMessageType)msg.ReadByte();
            PlayerConnectionChangeType changeType   = PlayerConnectionChangeType.None;
            string                     txt          = "";
            string                     styleSetting = string.Empty;

            if (type != ChatMessageType.Order)
            {
                changeType = (PlayerConnectionChangeType)msg.ReadByte();
                txt        = msg.ReadString();
            }

            string    senderName      = msg.ReadString();
            Character senderCharacter = null;
            Client    senderClient    = null;
            bool      hasSenderClient = msg.ReadBoolean();

            if (hasSenderClient)
            {
                UInt64 clientId = msg.ReadUInt64();
                senderClient = GameMain.Client.ConnectedClients.Find(c => c.SteamID == clientId || c.ID == clientId);
                if (senderClient != null)
                {
                    senderName = senderClient.Name;
                }
            }
            bool hasSenderCharacter = msg.ReadBoolean();

            if (hasSenderCharacter)
            {
                senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                if (senderCharacter != null)
                {
                    senderName = senderCharacter.Name;
                }
            }
            msg.ReadPadBits();

            switch (type)
            {
            case ChatMessageType.Default:
                break;

            case ChatMessageType.Order:
                int       orderIndex        = msg.ReadByte();
                UInt16    targetCharacterID = msg.ReadUInt16();
                Character targetCharacter   = Entity.FindEntityByID(targetCharacterID) as Character;
                Entity    targetEntity      = Entity.FindEntityByID(msg.ReadUInt16());

                Order  orderPrefab = null;
                int?   optionIndex = null;
                string orderOption = null;

                // The option of a Dismiss order is written differently so we know what order we target
                // now that the game supports multiple current orders simultaneously
                if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count)
                {
                    orderPrefab = Order.PrefabList[orderIndex];
                    if (orderPrefab.Identifier != "dismissed")
                    {
                        optionIndex = msg.ReadByte();
                    }
                    // Does the dismiss order have a specified target?
                    else if (msg.ReadBoolean())
                    {
                        int identifierCount = msg.ReadByte();
                        if (identifierCount > 0)
                        {
                            int   dismissedOrderIndex  = msg.ReadByte();
                            Order dismissedOrderPrefab = null;
                            if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count)
                            {
                                dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex];
                                orderOption          = dismissedOrderPrefab.Identifier;
                            }
                            if (identifierCount > 1)
                            {
                                int dismissedOrderOptionIndex = msg.ReadByte();
                                if (dismissedOrderPrefab != null)
                                {
                                    var options = dismissedOrderPrefab.Options;
                                    if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length)
                                    {
                                        orderOption += $".{options[dismissedOrderOptionIndex]}";
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    optionIndex = msg.ReadByte();
                }

                int                   orderPriority       = msg.ReadByte();
                OrderTarget           orderTargetPosition = null;
                Order.OrderTargetType orderTargetType     = (Order.OrderTargetType)msg.ReadByte();
                int                   wallSectionIndex    = 0;
                if (msg.ReadBoolean())
                {
                    var x    = msg.ReadSingle();
                    var y    = msg.ReadSingle();
                    var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
                    orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true);
                }
                else if (orderTargetType == Order.OrderTargetType.WallSection)
                {
                    wallSectionIndex = msg.ReadByte();
                }

                if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
                    if (NetIdUtils.IdMoreRecent(id, LastID))
                    {
                        LastID = id;
                    }
                    return;
                }
                else
                {
                    orderPrefab ??= Order.PrefabList[orderIndex];
                }

                orderOption ??= optionIndex.HasValue && optionIndex >= 0 && optionIndex < orderPrefab.Options.Length ? orderPrefab.Options[optionIndex.Value] : "";
                txt = orderPrefab.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);

                if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
                {
                    Order order = null;
                    switch (orderTargetType)
                    {
                    case Order.OrderTargetType.Entity:
                        order = new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter);
                        break;

                    case Order.OrderTargetType.Position:
                        order = new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter);
                        break;

                    case Order.OrderTargetType.WallSection:
                        order = new Order(orderPrefab, targetEntity as Structure, wallSectionIndex, orderGiver: senderCharacter);
                        break;
                    }

                    if (order != null)
                    {
                        if (order.TargetAllCharacters)
                        {
                            var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
                            GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
                        }
                        else if (targetCharacter != null)
                        {
                            targetCharacter.SetOrder(order, orderOption, orderPriority, senderCharacter);
                        }
                    }
                }

                if (NetIdUtils.IdMoreRecent(id, LastID))
                {
                    GameMain.Client.AddChatMessage(
                        new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter));
                    LastID = id;
                }
                return;

            case ChatMessageType.ServerMessageBox:
                txt = TextManager.GetServerMessage(txt);
                break;

            case ChatMessageType.ServerMessageBoxInGame:
                styleSetting = msg.ReadString();
                txt          = TextManager.GetServerMessage(txt);
                break;
            }

            if (NetIdUtils.IdMoreRecent(id, LastID))
            {
                switch (type)
                {
                case ChatMessageType.MessageBox:
                case ChatMessageType.ServerMessageBox:
                    //only show the message box if the text differs from the text in the currently visible box
                    if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt)
                    {
                        new GUIMessageBox("", txt);
                    }
                    break;

                case ChatMessageType.ServerMessageBoxInGame:
                    new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
                    break;

                case ChatMessageType.Console:
                    DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
                    break;

                case ChatMessageType.ServerLog:
                    if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
                    {
                        return;
                    }
                    GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
                    break;

                default:
                    GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType);
                    break;
                }
                LastID = id;
            }
        }
Exemplo n.º 8
0
        public static void ClientRead(NetIncomingMessage msg)
        {
            UInt16          ID   = msg.ReadUInt16();
            ChatMessageType type = (ChatMessageType)msg.ReadByte();
            string          txt  = "";

            if (type != ChatMessageType.Order)
            {
                txt = msg.ReadString();
            }

            string    senderName         = msg.ReadString();
            Character senderCharacter    = null;
            bool      hasSenderCharacter = msg.ReadBoolean();

            if (hasSenderCharacter)
            {
                senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                if (senderCharacter != null)
                {
                    senderName = senderCharacter.Name;
                }
            }

            if (type == ChatMessageType.Order)
            {
                int       orderIndex        = msg.ReadByte();
                UInt16    targetCharacterID = msg.ReadUInt16();
                Character targetCharacter   = Entity.FindEntityByID(targetCharacterID) as Character;
                Entity    targetEntity      = Entity.FindEntityByID(msg.ReadUInt16());
                int       optionIndex       = msg.ReadByte();

                Order order = null;
                if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
                    if (NetIdUtils.IdMoreRecent(ID, LastID))
                    {
                        LastID = ID;
                    }
                    return;
                }
                else
                {
                    order = Order.PrefabList[orderIndex];
                }
                string orderOption = "";
                if (optionIndex >= 0 && optionIndex < order.Options.Length)
                {
                    orderOption = order.Options[optionIndex];
                }
                txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.RoomName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);

                if (order.TargetAllCharacters)
                {
                    GameMain.GameSession?.CrewManager?.AddOrder(
                        new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)),
                        order.Prefab.FadeOutTime);
                }
                else if (targetCharacter != null)
                {
                    targetCharacter.SetOrder(
                        new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType)),
                        orderOption, senderCharacter);
                }

                if (NetIdUtils.IdMoreRecent(ID, LastID))
                {
                    GameMain.Client.AddChatMessage(
                        new OrderChatMessage(order, orderOption, txt, targetEntity, targetCharacter, senderCharacter));
                    LastID = ID;
                }
                return;
            }

            if (NetIdUtils.IdMoreRecent(ID, LastID))
            {
                switch (type)
                {
                case ChatMessageType.MessageBox:
                    new GUIMessageBox("", txt);
                    break;

                case ChatMessageType.Console:
                    DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
                    break;

                case ChatMessageType.ServerLog:
                    if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
                    {
                        return;
                    }
                    GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
                    break;

                default:
                    GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter);
                    break;
                }
                LastID = ID;
            }
        }
Exemplo n.º 9
0
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsUnconscious &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
                    if (GameSettings.VerboseLogging)
                    {
                        string errorMsg = "Failed to read server event for entity \"" + entityName + "\"!";
                        GameServer.Log(errorMsg + "\n" + e.StackTrace, ServerLog.MessageType.Error);
                        DebugConsole.ThrowError(errorMsg, e);
                    }
                    GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace);
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.InGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAll = inGameClients[0].LastRecvEntityEventID;
                if (server.OwnerConnection != null)
                {
                    var owner = clients.Find(c => c.Connection == server.OwnerConnection);
                    if (owner != null)
                    {
                        lastSentToAll = owner.LastRecvEntityEventID;
                    }
                }
                inGameClients.ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.LastRecvEntityEventID))
                                             {
                                                 lastSentToAll = c.LastRecvEntityEventID;
                                             }
                                      });

                clients.Where(c => c.NeedsMidRoundSync).ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.FirstNewEventID))
                                                                       {
                                                                           lastSentToAll = (ushort)(c.FirstNewEventID - 1);
                                                                       }
                                                                });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null && (Timing.TotalTime - firstEventToResend.CreateTime) > 10.0f)
                {
                    //it's been 10 seconds since this event was created, kick everyone that hasn't received it yet, this is way too old
                    //  UNLESS the event was created when the client was still midround syncing, in which case we'll wait until the timeout
                    //  runs out before kicking the client
                    List <Client> toKick = inGameClients.FindAll(c =>
                                                                 NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.LastRecvEntityEventID) &&
                                                                 (firstEventToResend.CreateTime > c.MidRoundSyncTimeOut || Timing.TotalTime > c.MidRoundSyncTimeOut));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected old event " + (c.LastRecvEntityEventID + 1).ToString() + ")", Color.Red);
                        GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected old event "
                                       + (c.LastRecvEntityEventID + 1).ToString() +
                                       " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago)" +
                                       " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncOldEvent");
                    }
                                   );
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.LastRecvEntityEventID + 1)));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
                        GameServer.Log("Disconnecting client " + c.Name + " due to excessive desync (expected removed event " + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", "ServerMessage.ExcessiveDesyncRemovedEvent");
                    });
                }
            }

            var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            foreach (Client timedOutClient in timedOutClients)
            {
                GameServer.Log("Disconnecting client " + timedOutClient.Name + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
                GameMain.Server.DisconnectClient(timedOutClient, "", "ServerMessage.SyncTimeout");
            }

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }
Exemplo n.º 10
0
        public static void ServerRead(IReadMessage msg, Client c)
        {
            c.KickAFKTimer = 0.0f;

            UInt16          ID   = msg.ReadUInt16();
            ChatMessageType type = (ChatMessageType)msg.ReadByte();
            string          txt;

            Character        orderTargetCharacter = null;
            Entity           orderTargetEntity    = null;
            OrderChatMessage orderMsg             = null;
            OrderTarget      orderTargetPosition  = null;

            Order.OrderTargetType orderTargetType = Order.OrderTargetType.Entity;
            int?wallSectionIndex = null;

            if (type == ChatMessageType.Order)
            {
                int orderIndex = msg.ReadByte();
                orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                orderTargetEntity    = Entity.FindEntityByID(msg.ReadUInt16()) as Entity;

                Order  orderPrefab      = null;
                int?   orderOptionIndex = null;
                string orderOption      = null;

                // The option of a Dismiss order is written differently so we know what order we target
                // now that the game supports multiple current orders simultaneously
                if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count)
                {
                    orderPrefab = Order.PrefabList[orderIndex];
                    if (orderPrefab.Identifier != "dismissed")
                    {
                        orderOptionIndex = msg.ReadByte();
                    }
                    // Does the dismiss order have a specified target?
                    else if (msg.ReadBoolean())
                    {
                        int identifierCount = msg.ReadByte();
                        if (identifierCount > 0)
                        {
                            int   dismissedOrderIndex  = msg.ReadByte();
                            Order dismissedOrderPrefab = null;
                            if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count)
                            {
                                dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex];
                                orderOption          = dismissedOrderPrefab.Identifier;
                            }
                            if (identifierCount > 1)
                            {
                                int dismissedOrderOptionIndex = msg.ReadByte();
                                if (dismissedOrderPrefab != null)
                                {
                                    var options = dismissedOrderPrefab.Options;
                                    if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length)
                                    {
                                        orderOption += $".{options[dismissedOrderOptionIndex]}";
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    orderOptionIndex = msg.ReadByte();
                }

                int orderPriority = msg.ReadByte();
                orderTargetType = (Order.OrderTargetType)msg.ReadByte();
                if (msg.ReadBoolean())
                {
                    var x    = msg.ReadSingle();
                    var y    = msg.ReadSingle();
                    var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull;
                    orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, true);
                }
                else if (orderTargetType == Order.OrderTargetType.WallSection)
                {
                    wallSectionIndex = msg.ReadByte();
                }

                if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderIndex}).");
                    if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID))
                    {
                        c.LastSentChatMsgID = ID;
                    }
                    return;
                }

                orderPrefab ??= Order.PrefabList[orderIndex];
                orderOption ??= orderOptionIndex == null || orderOptionIndex < 0 || orderOptionIndex >= orderPrefab.Options.Length ? "" : orderPrefab.Options[orderOptionIndex.Value];
                orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderPriority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character)
                {
                    WallSectionIndex = wallSectionIndex
                };
                txt = orderMsg.Text;
            }
            else
            {
                txt = msg.ReadString() ?? "";
            }

            if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID))
            {
                return;
            }

            c.LastSentChatMsgID = ID;

            if (txt.Length > MaxLength)
            {
                txt = txt.Substring(0, MaxLength);
            }

            c.LastSentChatMessages.Add(txt);
            if (c.LastSentChatMessages.Count > 10)
            {
                c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10);
            }

            float similarity = 0.0f;

            for (int i = 0; i < c.LastSentChatMessages.Count; i++)
            {
                float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i);

                if (string.IsNullOrEmpty(txt))
                {
                    similarity += closeFactor;
                }
                else
                {
                    int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
                    similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
                }
            }
            //order/report messages can be sent a little faster than normal messages without triggering the spam filter
            if (orderMsg != null)
            {
                similarity *= 0.25f;
            }

            bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection;

            if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner)
            {
                GameMain.Server.KarmaManager.OnSpamFilterTriggered(c);

                c.ChatSpamCount++;
                if (c.ChatSpamCount > 3)
                {
                    //kick for spamming too much
                    GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked"));
                }
                else
                {
                    ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                    c.ChatSpamTimer = 10.0f;
                    GameMain.Server.SendDirectChatMessage(denyMsg, c);
                }
                return;
            }

            c.ChatSpamSpeed += similarity + 0.5f;

            if (c.ChatSpamTimer > 0.0f && !isOwner)
            {
                ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                c.ChatSpamTimer = 10.0f;
                GameMain.Server.SendDirectChatMessage(denyMsg, c);
                return;
            }

            if (type == ChatMessageType.Order)
            {
                if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead)
                {
                    return;
                }
                Order order = null;
                if (orderMsg.Order.IsReport)
                {
                    HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order);
                }
                else if (orderTargetCharacter != null && !orderMsg.Order.TargetAllCharacters)
                {
                    switch (orderTargetType)
                    {
                    case Order.OrderTargetType.Entity:
                        order = new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: orderMsg.Sender);
                        break;

                    case Order.OrderTargetType.Position:
                        order = new Order(orderMsg.Order.Prefab, orderTargetPosition, orderGiver: orderMsg.Sender);
                        break;
                    }
                    if (order != null)
                    {
                        orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.OrderPriority, orderMsg.Sender);
                    }
                }
                else if (orderMsg.Order.IsIgnoreOrder)
                {
                    switch (orderTargetType)
                    {
                    case Order.OrderTargetType.Entity:
                        if (orderTargetEntity is IIgnorable ignorableEntity)
                        {
                            ignorableEntity.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis";
                        }
                        break;

                    case Order.OrderTargetType.WallSection:
                        if (!wallSectionIndex.HasValue)
                        {
                            break;
                        }
                        if (orderTargetEntity is Structure s && s.GetSection(wallSectionIndex.Value) is IIgnorable ignorableWall)
                        {
                            ignorableWall.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis";
                        }
                        break;
                    }
                }
                GameMain.Server.SendOrderChatMessage(orderMsg);
            }
            else
            {
                GameMain.Server.SendChatMessage(txt, null, c);
            }
        }
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsUnconscious &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.ThrowError("Failed to read server event for entity \"" + entityName + "\"!", e);
                    }
                    GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace);
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.InGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAll = inGameClients[0].LastRecvEntityEventID;
                inGameClients.ForEach(c => { if (NetIdUtils.IdMoreRecent((ushort)(lastSentToAll - 1), c.LastRecvEntityEventID))
                                             {
                                                 lastSentToAll = (ushort)(c.LastRecvEntityEventID - 1);
                                             }
                                      });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null && (Timing.TotalTime - firstEventToResend.CreateTime) > (10.0f * GameMain.NilMod.DesyncTimerMultiplier))
                {
                    //it's been 10 seconds since this event was created
                    //kick everyone that hasn't received it yet, this is way too old
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.LastRecvEntityEventID));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected old event " + c.LastRecvEntityEventID.ToString() + ")", Microsoft.Xna.Framework.Color.Red);
                        server.DisconnectClient(c, "", "You have been disconnected because of excessive desync (Expected old event, inform the server host increasing 'DesyncTimerMultiplier' could help.");
                    }
                                   );
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.LastRecvEntityEventID + 1)));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked due to excessive desync (expected " + c.LastRecvEntityEventID.ToString() + ", last available is " + events[0].ID.ToString() + ")", Microsoft.Xna.Framework.Color.Red);
                        server.DisconnectClient(c, "", "You have been disconnected because of excessive desync (Event no longer exists)");
                    }
                                   );
                }
            }

            var timedOutClients = clients.FindAll(c => c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            timedOutClients.ForEach(c => GameMain.Server.DisconnectClient(c, "", "You have been disconnected because syncing your client with the server took too long."));

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsIncapacitated &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    string entityName = bufferedEvent.TargetEntity == null ? "null" : bufferedEvent.TargetEntity.ToString();
                    if (GameSettings.VerboseLogging)
                    {
                        string errorMsg = "Failed to read server event for entity \"" + entityName + "\"!";
                        GameServer.Log(errorMsg + "\n" + e.StackTrace.CleanupStackTrace(), ServerLog.MessageType.Error);
                        DebugConsole.ThrowError(errorMsg, e);
                    }
                    GameAnalyticsManager.AddErrorEventOnce("ServerEntityEventManager.Read:ReadFailed" + entityName,
                                                           GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
                                                           "Failed to read server event for entity \"" + entityName + "\"!\n" + e.StackTrace.CleanupStackTrace());
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.InGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAnyone = inGameClients[0].LastRecvEntityEventID;
                lastSentToAll    = inGameClients[0].LastRecvEntityEventID;

                if (server.OwnerConnection != null)
                {
                    var owner = clients.Find(c => c.Connection == server.OwnerConnection);
                    if (owner != null)
                    {
                        lastSentToAll = owner.LastRecvEntityEventID;
                    }
                }
                inGameClients.ForEach(c =>
                {
                    if (NetIdUtils.IdMoreRecent(lastSentToAll, c.LastRecvEntityEventID))
                    {
                        lastSentToAll = c.LastRecvEntityEventID;
                    }
                    if (NetIdUtils.IdMoreRecent(c.LastRecvEntityEventID, lastSentToAnyone))
                    {
                        lastSentToAnyone = c.LastRecvEntityEventID;
                    }
                });
                lastSentToAnyoneTime = events.Find(e => e.ID == lastSentToAnyone)?.CreateTime ?? Timing.TotalTime;

                if (Timing.TotalTime - lastWarningTime > 5.0 &&
                    Timing.TotalTime - lastSentToAnyoneTime > 10.0 &&
                    Timing.TotalTime > GameMain.GameSession.RoundStartTime + NetConfig.RoundStartSyncDuration)
                {
                    lastWarningTime = Timing.TotalTime;
                    GameServer.Log("WARNING: ServerEntityEventManager is lagging behind! Last sent id: " + lastSentToAnyone.ToString() + ", latest create id: " + ID.ToString(), ServerLog.MessageType.ServerMessage);
                    events.ForEach(e => e.ResetCreateTime());
                    //TODO: reset clients if this happens, maybe do it if a majority are behind rather than all of them?
                }

                clients.Where(c => c.NeedsMidRoundSync).ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.FirstNewEventID))
                                                                       {
                                                                           lastSentToAll = (ushort)(c.FirstNewEventID - 1);
                                                                       }
                                                                });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null &&
                    Timing.TotalTime > GameMain.GameSession.RoundStartTime + NetConfig.RoundStartSyncDuration &&
                    ((lastSentToAnyoneTime - firstEventToResend.CreateTime) > NetConfig.OldReceivedEventKickTime || (Timing.TotalTime - firstEventToResend.CreateTime) > NetConfig.OldEventKickTime))
                {
                    //  This event is 10 seconds older than the last one we've successfully sent,
                    //  kick everyone that hasn't received it yet, this is way too old
                    //  UNLESS the event was created when the client was still midround syncing,
                    //  in which case we'll wait until the timeout runs out before kicking the client
                    List <Client> toKick = inGameClients.FindAll(c =>
                                                                 NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.LastRecvEntityEventID) &&
                                                                 (firstEventToResend.CreateTime > c.MidRoundSyncTimeOut || lastSentToAnyoneTime > c.MidRoundSyncTimeOut || Timing.TotalTime > c.MidRoundSyncTimeOut + 10.0));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a very old network event (" + (c.LastRecvEntityEventID + 1).ToString() + ")", Color.Red);
                        GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a very old network event ("
                                       + (c.LastRecvEntityEventID + 1).ToString() +
                                       " (created " + (Timing.TotalTime - firstEventToResend.CreateTime).ToString("0.##") + " s ago, " +
                                       (lastSentToAnyoneTime - firstEventToResend.CreateTime).ToString("0.##") + " s older than last event sent to anyone)" +
                                       " Events queued: " + events.Count + ", last sent to all: " + lastSentToAll, ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncOldEvent + "/ServerMessage.ExcessiveDesyncOldEvent");
                    }
                                   );
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.LastRecvEntityEventID + 1)));
                    toKick.ForEach(c =>
                    {
                        DebugConsole.NewMessage(c.Name + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", Color.Red);
                        GameServer.Log(GameServer.ClientLogName(c) + " was kicked because they were expecting a removed network event (" + (c.LastRecvEntityEventID + 1).ToString() + ", last available is " + events[0].ID.ToString() + ")", ServerLog.MessageType.Error);
                        server.DisconnectClient(c, "", DisconnectReason.ExcessiveDesyncRemovedEvent + "/ServerMessage.ExcessiveDesyncRemovedEvent");
                    });
                }
            }

            var timedOutClients = clients.FindAll(c => c.Connection != GameMain.Server.OwnerConnection && c.InGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            foreach (Client timedOutClient in timedOutClients)
            {
                GameServer.Log("Disconnecting client " + GameServer.ClientLogName(timedOutClient) + ". Syncing the client with the server took too long.", ServerLog.MessageType.Error);
                GameMain.Server.DisconnectClient(timedOutClient, "", DisconnectReason.SyncTimeout + "/ServerMessage.SyncTimeout");
            }

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }
        public static void ServerRead(IReadMessage msg, Client c)
        {
            c.KickAFKTimer = 0.0f;

            UInt16          ID   = msg.ReadUInt16();
            ChatMessageType type = (ChatMessageType)msg.ReadByte();
            string          txt;

            Character        orderTargetCharacter = null;
            Entity           orderTargetEntity    = null;
            OrderChatMessage orderMsg             = null;
            OrderTarget      orderTargetPosition  = null;

            Order.OrderTargetType orderTargetType = Order.OrderTargetType.Entity;
            int?wallSectionIndex = null;

            if (type == ChatMessageType.Order)
            {
                var orderMessageInfo = OrderChatMessage.ReadOrder(msg);
                if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderMessageInfo.OrderIndex}).");
                    if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID))
                    {
                        c.LastSentChatMsgID = ID;
                    }
                    return;
                }
                orderTargetCharacter = orderMessageInfo.TargetCharacter;
                orderTargetEntity    = orderMessageInfo.TargetEntity;
                orderTargetPosition  = orderMessageInfo.TargetPosition;
                orderTargetType      = orderMessageInfo.TargetType;
                wallSectionIndex     = orderMessageInfo.WallSectionIndex;
                var    orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex];
                string orderOption = orderMessageInfo.OrderOption ??
                                     (orderMessageInfo.OrderOptionIndex == null || orderMessageInfo.OrderOptionIndex < 0 || orderMessageInfo.OrderOptionIndex >= orderPrefab.Options.Length ?
                                      "" : orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value]);
                orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character)
                {
                    WallSectionIndex = wallSectionIndex
                };
                txt = orderMsg.Text;
            }
            else
            {
                txt = msg.ReadString() ?? "";
            }

            if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID))
            {
                return;
            }

            c.LastSentChatMsgID = ID;

            if (txt.Length > MaxLength)
            {
                txt = txt.Substring(0, MaxLength);
            }

            c.LastSentChatMessages.Add(txt);
            if (c.LastSentChatMessages.Count > 10)
            {
                c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10);
            }

            float similarity = 0.0f;

            for (int i = 0; i < c.LastSentChatMessages.Count; i++)
            {
                float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i);

                if (string.IsNullOrEmpty(txt))
                {
                    similarity += closeFactor;
                }
                else
                {
                    int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
                    similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
                }
            }
            //order/report messages can be sent a little faster than normal messages without triggering the spam filter
            if (orderMsg != null)
            {
                similarity *= 0.25f;
            }

            bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection;

            if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner)
            {
                GameMain.Server.KarmaManager.OnSpamFilterTriggered(c);

                c.ChatSpamCount++;
                if (c.ChatSpamCount > 3)
                {
                    //kick for spamming too much
                    GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked"));
                }
                else
                {
                    ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                    c.ChatSpamTimer = 10.0f;
                    GameMain.Server.SendDirectChatMessage(denyMsg, c);
                }
                return;
            }

            c.ChatSpamSpeed += similarity + 0.5f;

            if (c.ChatSpamTimer > 0.0f && !isOwner)
            {
                ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                c.ChatSpamTimer = 10.0f;
                GameMain.Server.SendDirectChatMessage(denyMsg, c);
                return;
            }

            var should = GameMain.Lua.hook.Call("chatMessage", new DynValue[] { DynValue.NewString(txt), UserData.Create(c) });


            if (should != null)
            {
                if (should.CastToBool())
                {
                    return;
                }
                else
                {
                }
            }

            if (type == ChatMessageType.Order)
            {
                if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead)
                {
                    return;
                }
                if (orderMsg.Order.IsReport)
                {
                    HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order);
                }
                Order order = orderTargetType switch
                {
                    Order.OrderTargetType.Entity =>
                    new Order(orderMsg.Order, orderTargetEntity, orderMsg.Order?.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: orderMsg.Sender),
                    Order.OrderTargetType.Position =>
                    new Order(orderMsg.Order, orderTargetPosition, orderGiver: orderMsg.Sender),
                    Order.OrderTargetType.WallSection when orderTargetEntity is Structure s && wallSectionIndex.HasValue =>
                    new Order(orderMsg.Order, s, wallSectionIndex, orderGiver: orderMsg.Sender),
                    _ => throw new NotImplementedException()
                };
                if (order != null)
                {
                    if (order.TargetAllCharacters)
                    {
                        if (order.IsIgnoreOrder)
                        {
                            switch (orderTargetType)
                            {
                            case Order.OrderTargetType.Entity:
                                if (!(orderTargetEntity is IIgnorable ignorableEntity))
                                {
                                    break;
                                }
                                ignorableEntity.OrderedToBeIgnored = order.Identifier == "ignorethis";
                                break;

                            case Order.OrderTargetType.Position:
                                throw new NotImplementedException();

                            case Order.OrderTargetType.WallSection:
                                if (!wallSectionIndex.HasValue)
                                {
                                    break;
                                }
                                if (!(orderTargetEntity is Structure s))
                                {
                                    break;
                                }
                                if (!(s.GetSection(wallSectionIndex.Value) is IIgnorable ignorableWall))
                                {
                                    break;
                                }
                                ignorableWall.OrderedToBeIgnored = order.Identifier == "ignorethis";
                                break;
                            }
                        }
                        GameMain.GameSession?.CrewManager?.AddOrder(order, order.IsIgnoreOrder ? (float?)null : order.FadeOutTime);
                    }
                    else if (orderTargetCharacter != null)
                    {
                        orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.OrderPriority, orderMsg.Sender);
                    }
                }
                GameMain.Server.SendOrderChatMessage(orderMsg);
            }
            else
            {
                GameMain.Server.SendChatMessage(txt, null, c);
            }
        }
Exemplo n.º 14
0
        public static void ClientRead(IReadMessage msg)
        {
            UInt16                     id           = msg.ReadUInt16();
            ChatMessageType            type         = (ChatMessageType)msg.ReadByte();
            PlayerConnectionChangeType changeType   = PlayerConnectionChangeType.None;
            string                     txt          = "";
            string                     styleSetting = string.Empty;

            if (type != ChatMessageType.Order)
            {
                changeType = (PlayerConnectionChangeType)msg.ReadByte();
                txt        = msg.ReadString();
            }

            string    senderName      = msg.ReadString();
            Character senderCharacter = null;
            Client    senderClient    = null;
            bool      hasSenderClient = msg.ReadBoolean();

            if (hasSenderClient)
            {
                UInt64 clientId = msg.ReadUInt64();
                senderClient = GameMain.Client.ConnectedClients.Find(c => c.SteamID == clientId || c.ID == clientId);
                if (senderClient != null)
                {
                    senderName = senderClient.Name;
                }
            }
            bool hasSenderCharacter = msg.ReadBoolean();

            if (hasSenderCharacter)
            {
                senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                if (senderCharacter != null)
                {
                    senderName = senderCharacter.Name;
                }
            }
            msg.ReadPadBits();

            switch (type)
            {
            case ChatMessageType.Default:
                break;

            case ChatMessageType.Order:
                var orderMessageInfo = OrderChatMessage.ReadOrder(msg);
                if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
                    if (NetIdUtils.IdMoreRecent(id, LastID))
                    {
                        LastID = id;
                    }
                    return;
                }
                var    orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex];
                string orderOption = orderMessageInfo.OrderOption;
                orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ?
                orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : "";
                string targetRoom;

                if (orderMessageInfo.TargetEntity is Hull targetHull)
                {
                    targetRoom = targetHull.DisplayName;
                }
                else
                {
                    targetRoom = senderCharacter?.CurrentHull?.DisplayName;
                }

                txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom,
                                                 givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
                                                 orderOption: orderOption,
                                                 priority: orderMessageInfo.Priority);

                if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
                {
                    Order order = null;
                    switch (orderMessageInfo.TargetType)
                    {
                    case Order.OrderTargetType.Entity:
                        order = new Order(orderPrefab, orderMessageInfo.TargetEntity, orderPrefab.GetTargetItemComponent(orderMessageInfo.TargetEntity as Item), orderGiver: senderCharacter);
                        break;

                    case Order.OrderTargetType.Position:
                        order = new Order(orderPrefab, orderMessageInfo.TargetPosition, orderGiver: senderCharacter);
                        break;

                    case Order.OrderTargetType.WallSection:
                        order = new Order(orderPrefab, orderMessageInfo.TargetEntity as Structure, orderMessageInfo.WallSectionIndex, orderGiver: senderCharacter);
                        break;
                    }

                    if (order != null)
                    {
                        if (order.TargetAllCharacters)
                        {
                            var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null;
                            GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime);
                        }
                        else
                        {
                            orderMessageInfo.TargetCharacter?.SetOrder(order, orderOption, orderMessageInfo.Priority, senderCharacter);
                        }
                    }
                }

                if (NetIdUtils.IdMoreRecent(id, LastID))
                {
                    GameMain.Client.AddChatMessage(
                        new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, txt, orderMessageInfo.TargetPosition ?? orderMessageInfo.TargetEntity as ISpatialEntity, orderMessageInfo.TargetCharacter, senderCharacter));
                    LastID = id;
                }
                return;

            case ChatMessageType.ServerMessageBox:
                txt = TextManager.GetServerMessage(txt);
                break;

            case ChatMessageType.ServerMessageBoxInGame:
                styleSetting = msg.ReadString();
                txt          = TextManager.GetServerMessage(txt);
                break;
            }

            if (NetIdUtils.IdMoreRecent(id, LastID))
            {
                switch (type)
                {
                case ChatMessageType.MessageBox:
                case ChatMessageType.ServerMessageBox:
                    //only show the message box if the text differs from the text in the currently visible box
                    if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt)
                    {
                        new GUIMessageBox("", txt);
                    }
                    break;

                case ChatMessageType.ServerMessageBoxInGame:
                    new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
                    break;

                case ChatMessageType.Console:
                    DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
                    break;

                case ChatMessageType.ServerLog:
                    if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
                    {
                        return;
                    }
                    GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
                    break;

                default:
                    GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType);
                    break;
                }
                LastID = id;
            }
        }
Exemplo n.º 15
0
        public static void ServerRead(NetIncomingMessage msg, Client c)
        {
            UInt16 ID  = msg.ReadUInt16();
            string txt = msg.ReadString();

            if (txt == null)
            {
                txt = "";
            }

            if (!NetIdUtils.IdMoreRecent(ID, c.lastSentChatMsgID))
            {
                return;
            }

            c.lastSentChatMsgID = ID;

            if (txt.Length > MaxLength)
            {
                txt = txt.Substring(0, MaxLength);
            }

            c.lastSentChatMessages.Add(txt);
            if (c.lastSentChatMessages.Count > 10)
            {
                c.lastSentChatMessages.RemoveRange(0, c.lastSentChatMessages.Count - 10);
            }

            float similarity = 0.0f;

            for (int i = 0; i < c.lastSentChatMessages.Count; i++)
            {
                float closeFactor     = 1.0f / (c.lastSentChatMessages.Count - i);
                int   levenshteinDist = ToolBox.LevenshteinDistance(txt, c.lastSentChatMessages[i]);
                similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
            }

            if (similarity + c.ChatSpamSpeed > 5.0f)
            {
                c.ChatSpamCount++;

                if (c.ChatSpamCount > 3)
                {
                    //kick for spamming too much
                    GameMain.Server.KickClient(c, "You have been kicked by the spam filter.");
                }
                else
                {
                    ChatMessage denyMsg = ChatMessage.Create("", "You have been blocked by the spam filter. Try again after 10 seconds.", ChatMessageType.Server, null);
                    c.ChatSpamTimer = 10.0f;
                    GameMain.Server.SendChatMessage(denyMsg, c);
                }
                return;
            }

            c.ChatSpamSpeed += similarity + 0.5f;

            if (c.ChatSpamTimer > 0.0f)
            {
                ChatMessage denyMsg = ChatMessage.Create("", "You have been blocked by the spam filter. Try again after 10 seconds.", ChatMessageType.Server, null);
                c.ChatSpamTimer = 10.0f;
                GameMain.Server.SendChatMessage(denyMsg, c);
                return;
            }

            GameMain.Server.SendChatMessage(txt, null, c);
        }
Exemplo n.º 16
0
        /// <summary>
        /// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
        /// </summary>
        public bool Read(ServerNetObject type, NetIncomingMessage msg, float sendingTime, List <IServerSerializable> entities)
        {
            UInt16 unreceivedEntityEventCount = 0;

            if (type == ServerNetObject.ENTITY_EVENT_INITIAL)
            {
                unreceivedEntityEventCount = msg.ReadUInt16();
                firstNewID = msg.ReadUInt16();

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage(
                        "received midround syncing msg, unreceived: " + unreceivedEntityEventCount +
                        ", first new ID: " + firstNewID, Microsoft.Xna.Framework.Color.Yellow);
                }
            }
            else if (firstNewID != null)
            {
                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage("midround syncing complete, switching to ID " + (UInt16)(firstNewID - 1),
                                            Microsoft.Xna.Framework.Color.Yellow);
                }

                lastReceivedID = (UInt16)(firstNewID - 1);
                firstNewID     = null;
            }

            entities.Clear();

            UInt16 firstEventID = msg.ReadUInt16();
            int    eventCount   = msg.ReadByte();

            for (int i = 0; i < eventCount; i++)
            {
                UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
                UInt16 entityID    = msg.ReadUInt16();

                if (entityID == Entity.NullEntityID)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
                                                Microsoft.Xna.Framework.Color.Orange);
                    }
                    msg.ReadPadBits();
                    entities.Add(null);
                    if (thisEventID == (UInt16)(lastReceivedID + 1))
                    {
                        lastReceivedID++;
                    }
                    continue;
                }

                byte msgLength = msg.ReadByte();

                IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
                entities.Add(entity);

                //skip the event if we've already received it or if the entity isn't found
                if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
                {
                    if (thisEventID != (UInt16)(lastReceivedID + 1))
                    {
                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.NewMessage(
                                "Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
                                NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1))
                                    ? Microsoft.Xna.Framework.Color.Red
                                    : Microsoft.Xna.Framework.Color.Yellow);
                        }
                    }
                    else if (entity == null)
                    {
                        DebugConsole.NewMessage(
                            "Received msg " + thisEventID + ", entity " + entityID + " not found",
                            Microsoft.Xna.Framework.Color.Red);
                        GameMain.Client.ReportError(ClientNetError.MISSING_ENTITY, eventID: thisEventID, entityID: entityID);
                        return(false);
                    }

                    msg.Position += msgLength * 8;
                }
                else
                {
                    long msgPosition = msg.Position;
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
                                                Microsoft.Xna.Framework.Color.Green);
                    }
                    lastReceivedID++;
                    try
                    {
                        ReadEvent(msg, entity, sendingTime);
                    }

                    catch (Exception e)
                    {
                        string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace;
                        errorMsg += "\nPrevious entities:";
                        for (int j = entities.Count - 2; j >= 0; j--)
                        {
                            errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
                        }

                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
                        }
                        GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                        msg.Position = msgPosition + msgLength * 8;
                    }
                }
                msg.ReadPadBits();
            }
            return(true);
        }
Exemplo n.º 17
0
        public static void ServerRead(IReadMessage msg, Client c)
        {
            c.KickAFKTimer = 0.0f;

            UInt16          ID   = msg.ReadUInt16();
            ChatMessageType type = (ChatMessageType)msg.ReadByte();
            string          txt  = "";

            int              orderIndex           = -1;
            Character        orderTargetCharacter = null;
            Entity           orderTargetEntity    = null;
            int              orderOptionIndex     = -1;
            OrderChatMessage orderMsg             = null;

            if (type == ChatMessageType.Order)
            {
                orderIndex           = msg.ReadByte();
                orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                orderTargetEntity    = Entity.FindEntityByID(msg.ReadUInt16()) as Entity;
                orderOptionIndex     = msg.ReadByte();

                if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError("Invalid order message from client \"" + c.Name + "\" - order index out of bounds.");
                    return;
                }

                Order  order       = Order.PrefabList[orderIndex];
                string orderOption = orderOptionIndex < 0 || orderOptionIndex >= order.Options.Length ? "" : order.Options[orderOptionIndex];
                orderMsg = new OrderChatMessage(order, orderOption, orderTargetEntity, orderTargetCharacter, c.Character);
                txt      = orderMsg.Text;
            }
            else
            {
                txt = msg.ReadString() ?? "";
            }

            if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID))
            {
                return;
            }

            c.LastSentChatMsgID = ID;

            if (txt.Length > MaxLength)
            {
                txt = txt.Substring(0, MaxLength);
            }

            c.LastSentChatMessages.Add(txt);
            if (c.LastSentChatMessages.Count > 10)
            {
                c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10);
            }

            float similarity = 0.0f;

            for (int i = 0; i < c.LastSentChatMessages.Count; i++)
            {
                float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i);

                if (string.IsNullOrEmpty(txt))
                {
                    similarity += closeFactor;
                }
                else
                {
                    int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
                    similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
                }
            }
            //order/report messages can be sent a little faster than normal messages without triggering the spam filter
            if (orderMsg != null)
            {
                similarity *= 0.25f;
            }

            bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection;

            if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner)
            {
                GameMain.Server.KarmaManager.OnSpamFilterTriggered(c);

                c.ChatSpamCount++;
                if (c.ChatSpamCount > 3)
                {
                    //kick for spamming too much
                    GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked"));
                }
                else
                {
                    ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                    c.ChatSpamTimer = 10.0f;
                    GameMain.Server.SendDirectChatMessage(denyMsg, c);
                }
                return;
            }

            c.ChatSpamSpeed += similarity + 0.5f;

            if (c.ChatSpamTimer > 0.0f && !isOwner)
            {
                ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                c.ChatSpamTimer = 10.0f;
                GameMain.Server.SendDirectChatMessage(denyMsg, c);
                return;
            }

            if (type == ChatMessageType.Order)
            {
                if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead)
                {
                    return;
                }

                ChatMessageType messageType = CanUseRadio(orderMsg.Sender) ? ChatMessageType.Radio : ChatMessageType.Default;
                if (orderMsg.Order.TargetAllCharacters)
                {
                    //do nothing
                }
                else if (orderTargetCharacter != null)
                {
                    orderTargetCharacter.SetOrder(
                        new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == orderMsg.Order.ItemComponentType)),
                        orderMsg.OrderOption, orderMsg.Sender);
                }

                GameMain.Server.SendOrderChatMessage(orderMsg);
            }
            else
            {
                GameMain.Server.SendChatMessage(txt, null, c);
            }
        }
Exemplo n.º 18
0
        public void Update(List <Client> clients)
        {
            foreach (BufferedEvent bufferedEvent in bufferedEvents)
            {
                if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)
                {
                    bufferedEvent.IsProcessed = true;
                    continue;
                }

                //delay reading the events until the inputs for the corresponding frame have been processed

                //UNLESS the character is unconscious, in which case we'll read the messages immediately (because further inputs will be ignored)
                //atm the "give in" command is the only thing unconscious characters can do, other types of events are ignored
                if (!bufferedEvent.Character.IsUnconscious &&
                    NetIdUtils.IdMoreRecent(bufferedEvent.CharacterStateID, bufferedEvent.Character.LastProcessedID))
                {
                    continue;
                }

                try
                {
                    ReadEvent(bufferedEvent.Data, bufferedEvent.TargetEntity, bufferedEvent.Sender);
                }

                catch (Exception e)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.ThrowError("Failed to read event for entity \"" + bufferedEvent.TargetEntity.ToString() + "!", e);
                    }
                }

                bufferedEvent.IsProcessed = true;
            }

            var inGameClients = clients.FindAll(c => c.inGame && !c.NeedsMidRoundSync);

            if (inGameClients.Count > 0)
            {
                lastSentToAll = inGameClients[0].lastRecvEntityEventID;
                inGameClients.ForEach(c => { if (NetIdUtils.IdMoreRecent(lastSentToAll, c.lastRecvEntityEventID))
                                             {
                                                 lastSentToAll = c.lastRecvEntityEventID;
                                             }
                                      });

                ServerEntityEvent firstEventToResend = events.Find(e => e.ID == (ushort)(lastSentToAll + 1));
                if (firstEventToResend != null && (Timing.TotalTime - firstEventToResend.CreateTime) > 10.0f)
                {
                    //it's been 10 seconds since this event was created
                    //kick everyone that hasn't received it yet, this is way too old
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent((UInt16)(lastSentToAll + 1), c.lastRecvEntityEventID));

                    toKick.ForEach(c => server.DisconnectClient(c, "", "You have been disconnected because of excessive desync"));
                }

                if (events.Count > 0)
                {
                    //the client is waiting for an event that we don't have anymore
                    //(the ID they're expecting is smaller than the ID of the first event in our list)
                    List <Client> toKick = inGameClients.FindAll(c => NetIdUtils.IdMoreRecent(events[0].ID, (UInt16)(c.lastRecvEntityEventID + 1)));
                    toKick.ForEach(c => server.DisconnectClient(c, "", "You have been disconnected because of excessive desync"));
                }
            }

            var timedOutClients = clients.FindAll(c => c.inGame && c.NeedsMidRoundSync && Timing.TotalTime > c.MidRoundSyncTimeOut);

            timedOutClients.ForEach(c => GameMain.Server.DisconnectClient(c, "", "You have been disconnected because syncing your client with the server took too long."));

            bufferedEvents.RemoveAll(b => b.IsProcessed);
        }
        public void CreateEvent(IServerSerializable entity, object[] extraData = null)
        {
            if (entity == null || !(entity is Entity))
            {
                DebugConsole.ThrowError("Can't create an entity event for " + entity + "!");
                return;
            }

            if (((Entity)entity).Removed && !(entity is Level))
            {
                DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n" + Environment.StackTrace.CleanupStackTrace());
                return;
            }
            if (((Entity)entity).IdFreed)
            {
                DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n" + Environment.StackTrace.CleanupStackTrace());
                return;
            }

            var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1));

            if (extraData != null)
            {
                newEvent.SetData(extraData);
            }

            bool inGameClientsPresent = server.ConnectedClients.Count(c => c.InGame) > 0;

            //remove old events that have been sent to all clients, they are redundant now
            //  keep at least one event in the list (lastSentToAll == e.ID) so we can use it to keep track of the latest ID
            //  and events less than 15 seconds old to give disconnected clients a bit of time to reconnect without getting desynced
            if (Timing.TotalTime > GameMain.GameSession.RoundStartTime + NetConfig.RoundStartSyncDuration)
            {
                events.RemoveAll(e =>
                                 (NetIdUtils.IdMoreRecent(lastSentToAll, e.ID) || !inGameClientsPresent) &&
                                 e.CreateTime < Timing.TotalTime - NetConfig.EventRemovalTime);
            }

            for (int i = events.Count - 1; i >= 0; i--)
            {
                //we already have an identical event that's waiting to be sent
                // -> no need to add a new one
                if (events[i].IsDuplicate(newEvent) && !events[i].Sent)
                {
                    return;
                }
            }

            ID++;

            events.Add(newEvent);

            if (!uniqueEvents.Any(e => e.IsDuplicate(newEvent)))
            {
                //create a copy of the event and give it a new ID
                var uniqueEvent = new ServerEntityEvent(entity, (UInt16)(uniqueEvents.Count + 1));
                uniqueEvent.SetData(extraData);

                uniqueEvents.Add(uniqueEvent);
            }
        }
Exemplo n.º 20
0
        public static void ServerRead(NetIncomingMessage msg, Client c)
        {
            UInt16 ID  = msg.ReadUInt16();
            string txt = msg.ReadString();

            if (txt == null)
            {
                txt = "";
            }

            if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID))
            {
                return;
            }

            c.LastSentChatMsgID = ID;

            if (txt.Length > MaxLength)
            {
                txt = txt.Substring(0, MaxLength);
            }

            c.LastSentChatMessages.Add(txt);
            if (c.LastSentChatMessages.Count > 10)
            {
                c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10);
            }

            float similarity = 0.0f;

            for (int i = 0; i < c.LastSentChatMessages.Count; i++)
            {
                float closeFactor     = 1.0f / (c.LastSentChatMessages.Count - i);
                int   levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
                similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
            }

            if (similarity + c.ChatSpamSpeed > 5.0f)
            {
                c.ChatSpamCount++;

                if (c.ChatSpamCount > 3)
                {
                    //kick for spamming too much
                    GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked"));
                }
                else
                {
                    ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                    c.ChatSpamTimer = 10.0f;
                    GameMain.Server.SendChatMessage(denyMsg, c);
                }
                return;
            }

            c.ChatSpamSpeed += similarity + 0.5f;

            if (c.ChatSpamTimer > 0.0f)
            {
                ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null);
                c.ChatSpamTimer = 10.0f;
                GameMain.Server.SendChatMessage(denyMsg, c);
                return;
            }

            //dead characters are allowed to send chat messages,
            //we'll just switch the message type to dead chat in SendChatMessage
            if (c.Character != null && (!c.Character.CanSpeak && !c.Character.IsDead))
            {
                return;
            }
            GameMain.Server.SendChatMessage(txt, null, c);
        }
        /// <summary>
        /// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
        /// </summary>
        public bool Read(ServerNetObject type, IReadMessage msg, float sendingTime, List <IServerSerializable> entities)
        {
            UInt16 unreceivedEntityEventCount = 0;

            if (type == ServerNetObject.ENTITY_EVENT_INITIAL)
            {
                unreceivedEntityEventCount = msg.ReadUInt16();
                firstNewID = msg.ReadUInt16();

                if (GameSettings.VerboseLogging)
                {
                    DebugConsole.NewMessage(
                        "received midround syncing msg, unreceived: " + unreceivedEntityEventCount +
                        ", first new ID: " + firstNewID, Microsoft.Xna.Framework.Color.Yellow);
                }
            }
            else
            {
                MidRoundSyncingDone = true;
                if (firstNewID != null)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("midround syncing complete, switching to ID " + (UInt16)(firstNewID - 1),
                                                Microsoft.Xna.Framework.Color.Yellow);
                    }
                    lastReceivedID = (UInt16)(firstNewID - 1);
                    firstNewID     = null;
                }
            }

            entities.Clear();

            UInt16 firstEventID = msg.ReadUInt16();
            int    eventCount   = msg.ReadByte();

            for (int i = 0; i < eventCount; i++)
            {
                //16 = entity ID, 8 = msg length
                if (msg.BitPosition + 16 + 8 > msg.LengthBits)
                {
                    string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
                    errorMsg += "\nPrevious entities:";
                    for (int j = entities.Count - 1; j >= 0; j--)
                    {
                        errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
                    }
                    DebugConsole.ThrowError(errorMsg);
                    return(false);
                }

                UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i);
                UInt16 entityID    = msg.ReadUInt16();

                if (entityID == Entity.NullEntityID)
                {
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
                                                Microsoft.Xna.Framework.Color.Orange);
                    }
                    msg.ReadPadBits();
                    entities.Add(null);
                    if (thisEventID == (UInt16)(lastReceivedID + 1))
                    {
                        lastReceivedID++;
                    }
                    continue;
                }

                int msgLength = (int)msg.ReadVariableUInt32();

                IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
                entities.Add(entity);

                //skip the event if we've already received it or if the entity isn't found
                if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
                {
                    if (thisEventID != (UInt16)(lastReceivedID + 1))
                    {
                        if (GameSettings.VerboseLogging)
                        {
                            DebugConsole.NewMessage(
                                "Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")",
                                NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1))
                                    ? GUI.Style.Red
                                    : Microsoft.Xna.Framework.Color.Yellow);
                        }
                    }
                    else if (entity == null)
                    {
                        DebugConsole.NewMessage(
                            "Received msg " + thisEventID + ", entity " + entityID + " not found",
                            GUI.Style.Red);
                        GameMain.Client.ReportError(ClientNetError.MISSING_ENTITY, eventID: thisEventID, entityID: entityID);
                        return(false);
                    }

                    msg.BitPosition += msgLength * 8;
                    msg.ReadPadBits();
                }
                else
                {
                    long msgPosition = msg.BitPosition;
                    if (GameSettings.VerboseLogging)
                    {
                        DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")",
                                                Microsoft.Xna.Framework.Color.Green);
                    }
                    lastReceivedID++;
                    try
                    {
                        ReadEvent(msg, entity, sendingTime);
                        msg.ReadPadBits();

                        if (msg.BitPosition != msgPosition + msgLength * 8)
                        {
                            string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString()
                                              + "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits.";
#if DEBUG
                            DebugConsole.ThrowError(errorMsg);
#endif
                            GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);

                            //TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick
                            //msg.BitPosition = (int)(msgPosition + msgLength * 8);
                        }
                    }

                    catch (Exception e)
                    {
                        string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace.CleanupStackTrace();
                        errorMsg += "\nPrevious entities:";
                        for (int j = entities.Count - 2; j >= 0; j--)
                        {
                            errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
                        }

                        DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);

                        GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                        msg.BitPosition = (int)(msgPosition + msgLength * 8);
                        msg.ReadPadBits();
                    }
                }
            }
            return(true);
        }
Exemplo n.º 22
0
        public static void ClientRead(IReadMessage msg)
        {
            UInt16                     ID           = msg.ReadUInt16();
            ChatMessageType            type         = (ChatMessageType)msg.ReadByte();
            PlayerConnectionChangeType changeType   = PlayerConnectionChangeType.None;
            string                     txt          = "";
            string                     styleSetting = string.Empty;

            if (type != ChatMessageType.Order)
            {
                changeType = (PlayerConnectionChangeType)msg.ReadByte();
                txt        = msg.ReadString();
            }

            string    senderName         = msg.ReadString();
            Character senderCharacter    = null;
            bool      hasSenderCharacter = msg.ReadBoolean();

            if (hasSenderCharacter)
            {
                senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character;
                if (senderCharacter != null)
                {
                    senderName = senderCharacter.Name;
                }
            }

            switch (type)
            {
            case ChatMessageType.Default:
                break;

            case ChatMessageType.Order:
                int       orderIndex        = msg.ReadByte();
                UInt16    targetCharacterID = msg.ReadUInt16();
                Character targetCharacter   = Entity.FindEntityByID(targetCharacterID) as Character;
                Entity    targetEntity      = Entity.FindEntityByID(msg.ReadUInt16());
                int       optionIndex       = msg.ReadByte();

                Order order = null;
                if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count)
                {
                    DebugConsole.ThrowError("Invalid order message - order index out of bounds.");
                    if (NetIdUtils.IdMoreRecent(ID, LastID))
                    {
                        LastID = ID;
                    }
                    return;
                }
                else
                {
                    order = Order.PrefabList[orderIndex];
                }
                string orderOption = "";
                if (optionIndex >= 0 && optionIndex < order.Options.Length)
                {
                    orderOption = order.Options[optionIndex];
                }
                txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption);

                if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen)
                {
                    if (order.TargetAllCharacters)
                    {
                        GameMain.GameSession?.CrewManager?.AddOrder(
                            new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
                            order.Prefab.FadeOutTime);
                    }
                    else if (targetCharacter != null)
                    {
                        targetCharacter.SetOrder(
                            new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter),
                            orderOption, senderCharacter);
                    }
                }

                if (NetIdUtils.IdMoreRecent(ID, LastID))
                {
                    GameMain.Client.AddChatMessage(
                        new OrderChatMessage(order, orderOption, txt, targetEntity, targetCharacter, senderCharacter));
                    LastID = ID;
                }
                return;

            case ChatMessageType.ServerMessageBox:
                txt = TextManager.GetServerMessage(txt);
                break;

            case ChatMessageType.ServerMessageBoxInGame:
                styleSetting = msg.ReadString();
                txt          = TextManager.GetServerMessage(txt);
                break;
            }

            if (NetIdUtils.IdMoreRecent(ID, LastID))
            {
                switch (type)
                {
                case ChatMessageType.MessageBox:
                case ChatMessageType.ServerMessageBox:
                    //only show the message box if the text differs from the text in the currently visible box
                    if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt)
                    {
                        new GUIMessageBox("", txt);
                    }
                    break;

                case ChatMessageType.ServerMessageBoxInGame:
                    new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting);
                    break;

                case ChatMessageType.Console:
                    DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]);
                    break;

                case ChatMessageType.ServerLog:
                    if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType))
                    {
                        return;
                    }
                    GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType);
                    break;

                default:
                    GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter, changeType);
                    break;
                }
                LastID = ID;
            }
        }