fCraft.Player.LoginSequence C# (CSharp) Method

LoginSequence() private method

private LoginSequence ( ) : bool
return bool
        private bool LoginSequence()
        {
            byte opcode = reader.ReadByte();

            switch ( opcode ) {
                case ( byte )OpCode.Handshake:
                    break;

                case 2:
                    GentlyKickBetaClients();
                    return false;

                case 250:
                    GentlyKickBetaClients();
                    return false;

                case ( byte )'G':
                    ServeCfg();
                    return false;

                default:
                    Logger.Log( LogType.Error,
                                "Player.LoginSequence: Unexpected opcode in the first packet from {0}: {1}.",
                                IP, opcode );
                    KickNow( "Incompatible client, or a network error.", LeaveReason.ProtocolViolation );
                    return false;
            }

            // Check protocol version
            int clientProtocolVersion = reader.ReadByte();
            if ( clientProtocolVersion != Config.ProtocolVersion ) {
                Logger.Log( LogType.Error,
                            "Player.LoginSequence: Wrong protocol version: {0}.",
                            clientProtocolVersion );
                KickNow( "Incompatible protocol version!", LeaveReason.ProtocolViolation );
                return false;
            }

            string givenName = ReadString();
            string packetPlayerName = givenName; //make a copy of the full name, in case Mojang support is needed
            bool UsedMojang = false;
            // Check name for nonstandard characters
            if ( !IsValidName( givenName ) ) {
                //check if email, provide crappy support here
                if ( givenName.Contains( "@" ) ) { //if the user is an email address
                    UsedMojang = true;
                    PlayerInfo[] temp = PlayerDB.FindPlayerInfoByEmail( givenName ); //check if they have been here before
                    if ( temp != null && temp.Length == 1 ) { //restore name if needed
                        givenName = temp[0].Name;
                    } else { //else, new player. Build a unique name
                        Logger.Log( LogType.SystemActivity, "Email account " + givenName + " connected, attemping to create unique new name" );
                        int nameAppend = PlayerDB.PlayerInfoList.Count(p => p.MojangAccount != null) + 1;
                        string trimmedName = givenName.Split( '@' )[0].Replace( "@", "" ); //this should be the first part of the name ("Jonty800"@email.com)
                        if ( trimmedName == null )
                            throw new ArgumentNullException( "trimmedName" );
                        if ( trimmedName.Length > 16 ) {
                            trimmedName = trimmedName.Substring( 0, 15 - ( nameAppend.ToString().Length ) ); //shorten name
                        }
                        foreach ( char ch in trimmedName.ToCharArray() ) { //replace invalid chars with "_"
                            if ( ( ch < '0' && ch != '.' ) || ( ch > '9' && ch < 'A' ) || ( ch > 'Z' && ch < '_' ) || ( ch > '_' && ch < 'a' ) || ch > 'z' ) {
                                trimmedName = trimmedName.Replace( ch, '_' );
                            }
                        }
                        givenName = trimmedName + "." + nameAppend.ToString(); //this is now the player's new name
                        //run a test to see if it is unique or not (unable to test)
                        PlayerInfo[] Players = PlayerDB.FindPlayers( givenName ); //gather matches
                        while ( Players.Length != 0 ) { //while matches were found
                            //Name already exists. Reroll
                            if ( givenName.Length > 14 ) { //kick if substring causes invalid name
                                Logger.Log( LogType.SuspiciousActivity,
                                "Player.LoginSequence: Unacceptable player name, player failed to get new name", IP );
                                KickNow( "Invalid characters in player name!", LeaveReason.ProtocolViolation );
                                return false;
                                //returning false breaks loops in C#, right? haha been a while
                            }
                            givenName = givenName + ( nameAppend + 1 ); //keep adding a new number to the end of the string until unique
                            Players = PlayerDB.FindPlayers( givenName ); //update Players based on new name
                        }
                    }
                } else {
                    Logger.Log( LogType.SuspiciousActivity,
                                "Player.LoginSequence: Unacceptable player name: {0} ({1})",
                                givenName, IP );
                    KickNow( "Invalid characters in player name!", LeaveReason.ProtocolViolation );
                    return false;
                }
            }

            string verificationCode = ReadString();
            byte magicNum = reader.ReadByte(); // unused
            BytesReceived += 131;

            // ReSharper disable PossibleNullReferenceException
            Position = WorldManager.MainWorld.Map.Spawn;
            // ReSharper restore PossibleNullReferenceException

            Info = PlayerDB.FindOrCreateInfoForPlayer( givenName, IP );
            ResetAllBinds();

            // negotiate protocol extensions, if applicable
            if ( magicNum == 0x42 ) {
                if ( !NegotiateProtocolExtension() )
                    return false;
            }
            if ( Server.VerifyName( packetPlayerName, verificationCode, Heartbeat.Salt ) ) {
                IsVerified = true;
                // update capitalization of player's name
                if ( !Info.Name.Equals( givenName, StringComparison.Ordinal ) ) {
                    Info.Name = givenName;
                }
                if ( UsedMojang ) {
                    Info.MojangAccount = packetPlayerName;
                }
            } else {
                NameVerificationMode nameVerificationMode = ConfigKey.VerifyNames.GetEnum<NameVerificationMode>();

                string standardMessage = String.Format( "Player.LoginSequence: Could not verify player name for {0} ({1}).",
                                                        Name, IP );
                if ( IP.Equals( IPAddress.Loopback ) && nameVerificationMode != NameVerificationMode.Always ) {
                    Logger.Log( LogType.SuspiciousActivity,
                                "{0} Player was identified as connecting from localhost and allowed in.",
                                standardMessage );
                    IsVerified = true;
                } else if ( IP.IsLAN() && ConfigKey.AllowUnverifiedLAN.Enabled() ) {
                    Logger.Log( LogType.SuspiciousActivity,
                                "{0} Player was identified as connecting from LAN and allowed in.",
                                standardMessage );
                    IsVerified = true;
                } else if ( Info.TimesVisited > 1 && Info.LastIP.Equals( IP ) ) {
                    switch ( nameVerificationMode ) {
                        case NameVerificationMode.Always:
                            Info.ProcessFailedLogin( this );
                            Logger.Log( LogType.SuspiciousActivity,
                                        "{0} IP matched previous records for that name. " +
                                        "Player was kicked anyway because VerifyNames is set to Always.",
                                        standardMessage );
                            KickNow( "Could not verify player name!", LeaveReason.UnverifiedName );
                            return false;

                        case NameVerificationMode.Balanced:
                        case NameVerificationMode.Never:
                            Logger.Log( LogType.SuspiciousActivity,
                                        "{0} IP matched previous records for that name. Player was allowed in.",
                                        standardMessage );
                            IsVerified = true;
                            break;
                    }
                } else {
                    switch ( nameVerificationMode ) {
                        case NameVerificationMode.Always:
                        case NameVerificationMode.Balanced:
                            Info.ProcessFailedLogin( this );
                            Logger.Log( LogType.SuspiciousActivity,
                                        "{0} IP did not match. Player was kicked.",
                                        standardMessage );
                            KickNow( "Could not verify player name!", LeaveReason.UnverifiedName );
                            return false;

                        case NameVerificationMode.Never:
                            Logger.Log( LogType.SuspiciousActivity,
                                        "{0} IP did not match. Player was allowed in anyway because VerifyNames is set to Never.",
                                        standardMessage );
                            Message( "&WYour name could not be verified." );
                            break;
                    }
                }
            }

            if ( Info.IsBanned ) {
                if ( Info.BannedUntil < DateTime.UtcNow && ( Info.BannedUntil != DateTime.MinValue ) ) {
                    Info.Unban( Player.Console, "Tempban expired", true, true );
                    Info.BannedUntil = DateTime.MinValue;
                }
            }
            // Check if player is banned
            if ( Info.IsBanned ) {
                Info.ProcessFailedLogin( this );
                Logger.Log( LogType.SuspiciousActivity,
                            "Banned player {0} tried to log in from {1}",
                            Name, IP );
                string bannedMessage;
                if ( Info.BannedBy != null ) {
                    if ( Info.BanReason != null ) {
                        bannedMessage = String.Format( "Banned {0} ago by {1}: {2}",
                                                       Info.TimeSinceBan.ToMiniString(),
                                                       Info.BannedBy,
                                                       Info.BanReason );
                    } else {
                        bannedMessage = String.Format( "Banned {0} ago by {1}",
                                                       Info.TimeSinceBan.ToMiniString(),
                                                       Info.BannedBy );
                    }
                } else {
                    if ( Info.BanReason != null ) {
                        bannedMessage = String.Format( "Banned {0} ago: {1}",
                                                       Info.TimeSinceBan.ToMiniString(),
                                                       Info.BanReason );
                    } else {
                        bannedMessage = String.Format( "Banned {0} ago",
                                                       Info.TimeSinceBan.ToMiniString() );
                    }
                }
                KickNow( bannedMessage, LeaveReason.LoginFailed );
                return false;
            }

            // Check if player's IP is banned
            IPBanInfo ipBanInfo = IPBanList.Get( IP );
            if ( ipBanInfo != null && Info.BanStatus != BanStatus.IPBanExempt ) {
                Info.ProcessFailedLogin( this );
                ipBanInfo.ProcessAttempt( this );
                Logger.Log( LogType.SuspiciousActivity,
                            "{0} tried to log in from a banned IP.", Name );
                string bannedMessage = String.Format( "IP-banned {0} ago by {1}: {2}",
                                                      DateTime.UtcNow.Subtract( ipBanInfo.BanDate ).ToMiniString(),
                                                      ipBanInfo.BannedBy,
                                                      ipBanInfo.BanReason );
                KickNow( bannedMessage, LeaveReason.LoginFailed );
                return false;
            }

            // Check if max number of connections is reached for IP
            if ( !Server.RegisterSession( this ) ) {
                Info.ProcessFailedLogin( this );
                Logger.Log( LogType.SuspiciousActivity,
                            "Player.LoginSequence: Denied player {0}: maximum number of connections was reached for {1}",
                            givenName, IP );
                KickNow( String.Format( "Max connections reached for {0}", IP ), LeaveReason.LoginFailed );
                return false;
            }

            // Check if player is paid (if required)
            if ( ConfigKey.PaidPlayersOnly.Enabled() ) {
                SendNow( PacketWriter.MakeHandshake( this,
                                                     ConfigKey.ServerName.GetString(),
                                                     "Please wait; Checking paid status..." ) );
                writer.Flush();
            }

            // Any additional security checks should be done right here
            if ( RaisePlayerConnectingEvent( this ) )
                return false;

            // ----==== beyond this point, player is considered connecting (allowed to join) ====----

            // Register player for future block updates
            if ( !Server.RegisterPlayer( this ) ) {
                Logger.Log( LogType.SystemActivity,
                            "Player {0} was kicked because server is full.", Name );
                string kickMessage = String.Format( "Sorry, server is full ({0}/{1})",
                                        Server.Players.Length, ConfigKey.MaxPlayers.GetInt() );
                KickNow( kickMessage, LeaveReason.ServerFull );
                return false;
            }
            Info.ProcessLogin( this );
            State = SessionState.LoadingMain;

            // ----==== Beyond this point, player is considered connected (authenticated and registered) ====----
            Logger.Log( LogType.UserActivity, "Player {0} connected from {1}.", Name, IP );

            // Figure out what the starting world should be
            World startingWorld = Info.Rank.MainWorld ?? WorldManager.MainWorld;
            startingWorld = RaisePlayerConnectedEvent( this, startingWorld );

            // Send server information
            string serverName = ConfigKey.ServerName.GetString();
            string motd;
            if ( ConfigKey.WoMEnableEnvExtensions.Enabled() ) {
                if ( IP.Equals( IPAddress.Loopback ) ) {
                    motd = "&0cfg=localhost:" + Server.Port + "/" + startingWorld.Name + "~motd";
                } else {
                    motd = "&0cfg=" + Server.ExternalIP + ":" + Server.Port + "/" + startingWorld.Name + "~motd";
                }
            } else {
                motd = ConfigKey.MOTD.GetString();
            }

            SendNow( PacketWriter.MakeHandshake( this, serverName, motd ) );

            // AutoRank
            if ( ConfigKey.AutoRankEnabled.Enabled() ) {
                Rank newRank = AutoRankManager.Check( Info );
                if ( newRank != null ) {
                    try {
                        Info.ChangeRank( AutoRank, newRank, "~AutoRank", true, true, true );
                    } catch ( PlayerOpException ex ) {
                        Logger.Log( LogType.Error,
                                    "AutoRank failed on player {0}: {1}",
                                    ex.Player.Name, ex.Message );
                    }
                }
            }

            if ( SupportsBlockPermissions ) {
                SendBlockPermissions();
            }

            bool firstTime = ( Info.TimesVisited == 1 );
            if ( !JoinWorldNow( startingWorld, true, WorldChangeReason.FirstWorld ) ) {
                Logger.Log( LogType.Warning,
                            "Could not load main world ({0}) for connecting player {1} (from {2}): " +
                            "Either main world is full, or an error occured.",
                            startingWorld.Name, Name, IP );
                KickNow( "Either main world is full, or an error occured.", LeaveReason.WorldFull );
                return false;
            }

            // ==== Beyond this point, player is considered ready (has a world) ====
            int i = 0;
            Random rand = new Random();
            foreach ( Zone zone in startingWorld.Map.Zones ) {
                Send( Packet.MakeAddSelectionBox( ( byte )i, "Whatever", ( short )zone.Bounds.XMin, ( short )zone.Bounds.ZMin, ( short )zone.Bounds.YMin,
                    ( short )zone.Bounds.XMax, ( short )zone.Bounds.ZMax, ( short )zone.Bounds.YMax, ( short )rand.Next( 255 ), ( short )rand.Next( 255 ), ( short )rand.Next( 255 ), ( short )rand.Next( 255 ) ) );
                i++;
            }

            var canSee = Server.Players.CanSee( this );

            // Announce join
            if ( ConfigKey.ShowConnectionMessages.Enabled() ) {
                // ReSharper disable AssignNullToNotNullAttribute
                string message = Server.MakePlayerConnectedMessage( this, firstTime, World );
                // ReSharper restore AssignNullToNotNullAttribute
                canSee.Message( message );
            }

            if ( !IsVerified ) {
                canSee.Message( "&WName and IP of {0}&W are unverified!", ClassyName );
            }

            if ( Info.IsHidden ) {
                if ( Can( Permission.Hide ) ) {
                    canSee.Message( "&8Player {0}&8 logged in hidden. Pssst.", ClassyName );
                } else {
                    Info.IsHidden = false;
                }
            }

            // Check if other banned players logged in from this IP
            PlayerInfo[] bannedPlayerNames = PlayerDB.FindPlayers( IP, 25 )
                                                     .Where( playerFromSameIP => playerFromSameIP.IsBanned )
                                                     .ToArray();
            if ( bannedPlayerNames.Length > 0 ) {
                canSee.Message( "&WPlayer {0}&W logged in from an IP shared by banned players: {1}",
                                ClassyName, bannedPlayerNames.JoinToClassyString() );
                Logger.Log( LogType.SuspiciousActivity,
                            "Player {0} logged in from an IP shared by banned players: {1}",
                                ClassyName, bannedPlayerNames.JoinToString( info => info.Name ) );
            }

            // check if player is still muted
            if ( Info.MutedUntil > DateTime.UtcNow ) {
                Message( "&WYou were previously muted by {0}&W, {1} left.",
                         Info.MutedByClassy, Info.TimeMutedLeft.ToMiniString() );
                canSee.Message( "&WPlayer {0}&W was previously muted by {1}&W, {2} left.",
                                ClassyName, Info.MutedByClassy, Info.TimeMutedLeft.ToMiniString() );
            }

            // check if player is still frozen
            if ( Info.IsFrozen ) {
                if ( Info.FrozenOn != DateTime.MinValue ) {
                    Message( "&WYou were previously frozen {0} ago by {1}",
                             Info.TimeSinceFrozen.ToMiniString(),
                             Info.FrozenByClassy );
                    canSee.Message( "&WPlayer {0}&W was previously frozen {1} ago by {2}",
                                    ClassyName,
                                    Info.TimeSinceFrozen.ToMiniString(),
                                    Info.FrozenByClassy );
                } else {
                    Message( "&WYou were previously frozen by {0}",
                             Info.FrozenByClassy );
                    canSee.Message( "&WPlayer {0}&W was previously frozen by {1}",
                                    ClassyName, Info.FrozenByClassy );
                }
            }

            // Welcome message
            if ( File.Exists( Paths.GreetingFileName ) ) {
                string[] greetingText = File.ReadAllLines( Paths.GreetingFileName );
                foreach ( string greetingLine in greetingText ) {
                    MessageNow( Server.ReplaceTextKeywords( this, greetingLine ) );
                }
            } else {
                if ( firstTime ) {
                    MessageNow( "Welcome to {0}", ConfigKey.ServerName.GetString() );
                } else {
                    MessageNow( "Welcome back to {0}", ConfigKey.ServerName.GetString() );
                }

                MessageNow( "Your rank is {0}&S. Type &H/Help&S for help.",
                            Info.Rank.ClassyName );
            }

            // A reminder for first-time users
            if ( PlayerDB.Size == 1 && Info.Rank != RankManager.HighestRank ) {
                Message( "Type &H/Rank {0} {1}&S in console to promote yourself",
                         Name, RankManager.HighestRank.Name );
            }

            InitCopySlots();

            HasFullyConnected = true;
            State = SessionState.Online;
            Server.UpdatePlayerList();
            RaisePlayerReadyEvent( this );

            return true;
        }