Example #1
0
        public void ClientAdminRead(IReadMessage incMsg)
        {
            bool hasPermission = incMsg.ReadBoolean();

            if (!hasPermission)
            {
                incMsg.ReadPadBits();
                return;
            }

            bool isOwner = incMsg.ReadBoolean();

            incMsg.ReadPadBits();

            bannedPlayers.Clear();
            UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();

            for (int i = 0; i < (int)bannedPlayerCount; i++)
            {
                string name               = incMsg.ReadString();
                UInt16 uniqueIdentifier   = incMsg.ReadUInt16();
                bool   isRangeBan         = incMsg.ReadBoolean();
                bool   includesExpiration = incMsg.ReadBoolean();
                incMsg.ReadPadBits();

                DateTime?expiration = null;
                if (includesExpiration)
                {
                    double hoursFromNow = incMsg.ReadDouble();
                    expiration = DateTime.Now + TimeSpan.FromHours(hoursFromNow);
                }

                string reason = incMsg.ReadString();

                string endPoint = "";
                UInt64 steamID  = 0;
                if (isOwner)
                {
                    endPoint = incMsg.ReadString();
                    steamID  = incMsg.ReadUInt64();
                }
                else
                {
                    endPoint = "Endpoint concealed by host";
                    steamID  = 0;
                }
                bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID, reason, expiration));
            }

            if (banFrame != null)
            {
                var parent = banFrame.Parent;
                parent.RemoveChild(banFrame);
                CreateBanFrame(parent);
            }
        }
Example #2
0
        public void ClientAdminRead(IReadMessage incMsg)
        {
            bool hasPermission = incMsg.ReadBoolean();

            if (!hasPermission)
            {
                incMsg.ReadPadBits();
                return;
            }

            bool isOwner = incMsg.ReadBoolean();

            incMsg.ReadPadBits();

            bannedPlayers.Clear();
            UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32();

            for (int i = 0; i < (int)bannedPlayerCount; i++)
            {
                string name             = incMsg.ReadString();
                UInt16 uniqueIdentifier = incMsg.ReadUInt16();
                bool   isRangeBan       = incMsg.ReadBoolean(); incMsg.ReadPadBits();

                string ip      = "";
                UInt64 steamID = 0;
                if (isOwner)
                {
                    ip      = incMsg.ReadString();
                    steamID = incMsg.ReadUInt64();
                }
                else
                {
                    ip      = "IP concealed by host";
                    steamID = 0;
                }
                bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, ip, steamID));
            }

            if (banFrame != null)
            {
                var parent = banFrame.Parent;
                parent.RemoveChild(banFrame);
                CreateBanFrame(parent);
            }
        }
        private void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
        {
            if (netServer == null)
            {
                return;
            }

            pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;

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

            //DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep);

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

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

            switch (initializationStep)
            {
            case ConnectionInitialization.SteamTicketAndVersion:
                string name         = Client.SanitizeName(inc.ReadString());
                UInt64 steamId      = inc.ReadUInt64();
                UInt16 ticketLength = inc.ReadUInt16();
                inc.BitPosition += ticketLength * 8;     //skip ticket, owner handles steam authentication

                if (!Client.IsValidName(name, serverSettings))
                {
                    RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid");
                    return;
                }

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

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

                int contentPackageCount = (int)inc.ReadVariableUInt32();
                List <ClientContentPackage> clientContentPackages = new List <ClientContentPackage>();
                for (int i = 0; i < contentPackageCount; i++)
                {
                    string packageName = inc.ReadString();
                    string packageHash = inc.ReadString();
                    clientContentPackages.Add(new ClientContentPackage(packageName, packageHash));
                }

                //check if the client is missing any of our packages
                List <ContentPackage> missingPackages = new List <ContentPackage>();
                foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages)
                {
                    if (!serverContentPackage.HasMultiplayerIncompatibleContent)
                    {
                        continue;
                    }
                    bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash);
                    if (!packageFound)
                    {
                        missingPackages.Add(serverContentPackage);
                    }
                }

                //check if the client is using packages we don't have
                List <ClientContentPackage> redundantPackages = new List <ClientContentPackage>();
                foreach (ClientContentPackage clientContentPackage in clientContentPackages)
                {
                    bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash);
                    if (!packageFound)
                    {
                        redundantPackages.Add(clientContentPackage);
                    }
                }

                if (missingPackages.Count == 1)
                {
                    RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
                                        $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}");
                    GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error);
                    return;
                }
                else if (missingPackages.Count > 1)
                {
                    List <string> packageStrs = new List <string>();
                    missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
                    RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage,
                                        $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}");
                    GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
                    return;
                }
                if (redundantPackages.Count == 1)
                {
                    RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
                                        $"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}");
                    GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error);
                    return;
                }
                if (redundantPackages.Count > 1)
                {
                    List <string> packageStrs = new List <string>();
                    redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp)));
                    RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage,
                                        $"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}");
                    GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error);
                    return;
                }

                if (!pendingClient.AuthSessionStarted)
                {
                    pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password: ConnectionInitialization.Success;

                    pendingClient.Name = name;
                    pendingClient.AuthSessionStarted = true;
                }
                break;

            case ConnectionInitialization.Password:
                int    pwLength    = inc.ReadByte();
                byte[] incPassword = inc.ReadBytes(pwLength);
                if (pendingClient.PasswordSalt == null)
                {
                    DebugConsole.ThrowError("Received password message from client without salt");
                    return;
                }
                if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value))
                {
                    pendingClient.InitializationStep = ConnectionInitialization.Success;
                }
                else
                {
                    pendingClient.Retries++;

                    if (pendingClient.Retries >= 3)
                    {
                        string banMsg = "Failed to enter correct password too many times";
                        serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID, banMsg, null);

                        RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg);
                        return;
                    }
                }
                pendingClient.UpdateTime = Timing.TotalTime;
                break;
            }
        }
Example #4
0
        protected void ReadConnectionInitializationStep(IReadMessage inc)
        {
            ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte();

            IWriteMessage outMsg;

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

                SendMsgInternal(DeliveryMethod.Reliable, outMsg);
                break;

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

                string serverName = inc.ReadString();

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

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

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

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

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

                    return;
                }

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

                SendMsgInternal(DeliveryMethod.Reliable, outMsg);
                break;

            case ConnectionInitialization.Password:
                if (initializationStep == ConnectionInitialization.SteamTicketAndVersion)
                {
                    initializationStep = ConnectionInitialization.Password;
                }
                if (initializationStep != ConnectionInitialization.Password)
                {
                    return;
                }
                bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits();
                int  retries      = 0;
                if (incomingSalt)
                {
                    passwordSalt = inc.ReadInt32();
                }
                else
                {
                    retries = inc.ReadInt32();
                }
                OnRequestPassword?.Invoke(passwordSalt, retries);
                break;
            }
        }
Example #5
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;
            }
        }
        private void HandleDataMessage(IReadMessage inc)
        {
            if (!started)
            {
                return;
            }

            UInt64 senderSteamId = inc.ReadUInt64();

            byte incByte      = inc.ReadByte();
            bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
            bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
            bool isDisconnectMessage            = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
            bool isServerMessage    = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
            bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;

            if (isServerMessage)
            {
                DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString());
                return;
            }

            if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats
            {
                PendingClient      pendingClient   = pendingClients.Find(c => c.SteamID == senderSteamId);
                SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId);

                pendingClient?.Heartbeat();
                connectedClient?.Heartbeat();

                if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason))
                {
                    if (pendingClient != null)
                    {
                        RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason);
                    }
                    else if (connectedClient != null)
                    {
                        Disconnect(connectedClient, DisconnectReason.Banned.ToString() + "/ " + banReason);
                    }
                    return;
                }
                else if (isDisconnectMessage)
                {
                    if (pendingClient != null)
                    {
                        string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}";
                        RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg);
                    }
                    else if (connectedClient != null)
                    {
                        string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={connectedClient.Name}";
                        Disconnect(connectedClient, disconnectMsg, false);
                    }
                    return;
                }
                else if (isHeartbeatMessage)
                {
                    //message exists solely as a heartbeat, ignore its contents
                    return;
                }
                else if (isConnectionInitializationStep)
                {
                    if (pendingClient != null)
                    {
                        ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null));
                    }
                    else
                    {
                        ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte();
                        if (initializationStep == ConnectionInitialization.ConnectionStarted)
                        {
                            pendingClients.Add(new PendingClient(senderSteamId));
                        }
                    }
                }
                else if (connectedClient != null)
                {
                    UInt16 length = inc.ReadUInt16();

                    IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, connectedClient);
                    OnMessageReceived?.Invoke(connectedClient, msg);
                }
            }
            else //sender is owner
            {
                if (OwnerConnection != null)
                {
                    (OwnerConnection as SteamP2PConnection).Heartbeat();
                }

                if (isDisconnectMessage)
                {
                    DebugConsole.ThrowError("Received disconnect message from owner");
                    return;
                }
                if (isServerMessage)
                {
                    DebugConsole.ThrowError("Received server message from owner");
                    return;
                }
                if (isConnectionInitializationStep)
                {
                    if (OwnerConnection == null)
                    {
                        string ownerName = inc.ReadString();
                        OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID)
                        {
                            Status = NetworkConnectionStatus.Connected
                        };

                        OnInitializationComplete?.Invoke(OwnerConnection);
                    }
                    return;
                }
                if (isHeartbeatMessage)
                {
                    return;
                }
                else
                {
                    UInt16 length = inc.ReadUInt16();

                    IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, OwnerConnection);
                    OnMessageReceived?.Invoke(OwnerConnection, msg);
                }
            }
        }
Example #7
0
        private void HandleDataMessage(IReadMessage inc)
        {
            if (!isActive)
            {
                return;
            }

            UInt64         recipientSteamId = inc.ReadUInt64();
            DeliveryMethod deliveryMethod   = (DeliveryMethod)inc.ReadByte();

            int p2pDataStart = inc.BytePosition;

            byte incByte = inc.ReadByte();

            bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0;
            bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0;
            bool isDisconnectMessage            = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0;
            bool isServerMessage    = (incByte & (byte)PacketHeader.IsServerMessage) != 0;
            bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0;

            if (recipientSteamId != selfSteamID)
            {
                if (!isServerMessage)
                {
                    DebugConsole.ThrowError("Received non-server message meant for remote peer");
                    return;
                }

                RemotePeer peer = remotePeers.Find(p => p.SteamID == recipientSteamId);

                if (peer == null)
                {
                    return;
                }

                if (isDisconnectMessage)
                {
                    DisconnectPeer(peer, inc.ReadString());
                    return;
                }

                Steamworks.P2PSend sendType;
                switch (deliveryMethod)
                {
                case DeliveryMethod.Reliable:
                case DeliveryMethod.ReliableOrdered:
                    //the documentation seems to suggest that the Reliable send type
                    //enforces packet order (TODO: verify)
                    sendType = Steamworks.P2PSend.Reliable;
                    break;

                default:
                    sendType = Steamworks.P2PSend.Unreliable;
                    break;
                }

                byte[] p2pData;

                if (isConnectionInitializationStep)
                {
                    p2pData    = new byte[inc.LengthBytes - p2pDataStart + 8];
                    p2pData[0] = inc.Buffer[p2pDataStart];
                    Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 64, p2pData, 8);
                    Array.Copy(inc.Buffer, p2pDataStart + 1, p2pData, 9, inc.LengthBytes - p2pDataStart - 1);
                }
                else
                {
                    p2pData = new byte[inc.LengthBytes - p2pDataStart];
                    Array.Copy(inc.Buffer, p2pDataStart, p2pData, 0, p2pData.Length);
                }

                if (p2pData.Length + 4 >= MsgConstants.MTU)
                {
                    DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
                    sendType = Steamworks.P2PSend.Reliable;
                }

                bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType);
                sentBytes += p2pData.Length;

                if (!successSend)
                {
                    if (sendType != Steamworks.P2PSend.Reliable)
                    {
                        DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)");
                        sendType    = Steamworks.P2PSend.Reliable;
                        successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType);
                        sentBytes  += p2pData.Length;
                    }
                    if (!successSend)
                    {
                        DebugConsole.ThrowError("Failed to send message to remote peer! (" + p2pData.Length.ToString() + " bytes)");
                    }
                }
            }
            else
            {
                if (isDisconnectMessage)
                {
                    DebugConsole.ThrowError("Received disconnect message from owned server");
                    return;
                }
                if (!isServerMessage)
                {
                    DebugConsole.ThrowError("Received non-server message from owned server");
                    return;
                }
                if (isHeartbeatMessage)
                {
                    return; //timeout is handled by Lidgren, ignore this message
                }
                if (isConnectionInitializationStep)
                {
                    IWriteMessage outMsg = new WriteOnlyMessage();
                    outMsg.Write(selfSteamID);
                    outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep));
                    outMsg.Write(Name);

                    byte[] msgToSend = (byte[])outMsg.Buffer.Clone();
                    Array.Resize(ref msgToSend, outMsg.LengthBytes);
                    ChildServerRelay.Write(msgToSend);
                    return;
                }
                else
                {
                    if (initializationStep != ConnectionInitialization.Success)
                    {
                        OnInitializationComplete?.Invoke();
                        initializationStep = ConnectionInitialization.Success;
                    }
                    UInt16       length = inc.ReadUInt16();
                    IReadMessage msg    = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, ServerConnection);
                    OnMessageReceived?.Invoke(msg);

                    return;
                }
            }
        }
Example #8
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;
            }
        }
Example #9
0
        protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc)
        {
            pendingClient.TimeOut = NetworkConnection.TimeoutThreshold;

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

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

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

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

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

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

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

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

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

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

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

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

            case ConnectionInitialization.ContentPackageOrder:
                pendingClient.InitializationStep = ConnectionInitialization.Success;
                pendingClient.UpdateTime         = Timing.TotalTime;
                break;
            }
        }