bool LoginSequence() {
byte opcode = reader.ReadByte();
if( opcode != (byte)OpCode.Handshake ) {
if( opcode == 2 ) {
// This may be someone connecting with an SMP client
int strLen = IPAddress.NetworkToHostOrder( reader.ReadInt16() );
if( strLen >= 2 && strLen <= 16 ) {
string smpPlayerName = Encoding.UTF8.GetString( reader.ReadBytes( strLen ) );
Logger.Log( "Session.LoginSequence: Player \"{0}\" tried connecting with SMP/Beta client from {1}. " +
"fCraft does not support SMP/Beta.", LogType.Warning,
smpPlayerName, IP );
// send SMP KICK packet
writer.Write( (byte)255 );
byte[] stringData = Encoding.UTF8.GetBytes( NoSmpMessage );
writer.Write( (short)stringData.Length );
writer.Write( stringData );
bytesSent += (1 + stringData.Length);
writer.Flush();
} else {
// Not SMP client (invalid player name length)
Logger.Log( "Session.LoginSequence: Unexpected opcode in the first packet from {0}: {1}.", LogType.Error,
IP, opcode );
KickNow( "Unexpected handshake message - possible protocol mismatch!", LeaveReason.ProtocolViolation );
}
return false;
} else {
Logger.Log( "Session.LoginSequence: Unexpected opcode in the first packet from {0}: {1}.", LogType.Error,
IP, opcode );
KickNow( "Unexpected handshake message - possible protocol mismatch!", LeaveReason.ProtocolViolation );
return false;
}
}
// Check protocol version
int clientProtocolVersion = reader.ReadByte();
if( clientProtocolVersion != Config.ProtocolVersion ) {
Logger.Log( "Session.LoginSequence: Wrong protocol version: {0}.", LogType.Error,
clientProtocolVersion );
KickNow( "Incompatible protocol version!", LeaveReason.ProtocolViolation );
return false;
}
string playerName = ReadString();
string verificationCode = ReadString();
reader.ReadByte(); // unused
bytesReceived += 131;
// Check name for nonstandard characters
if( !Player.IsValidName( playerName ) ) {
Logger.Log( "Session.LoginSequence: Unacceptible player name: {0} ({1})", LogType.SuspiciousActivity,
playerName, IP );
KickNow( "Invalid characters in player name!", LeaveReason.ProtocolViolation );
return false;
}
// Verify name
Player = new Player( null, playerName, this, WorldManager.MainWorld.Map.Spawn );
bool showVerifyNamesWarning = false;
if( !Server.VerifyName( Player.Name, verificationCode, Server.Salt ) ) {
NameVerificationMode nameVerificationMode = ConfigKey.VerifyNames.GetEnum<NameVerificationMode>();
string standardMessage = String.Format( "Session.LoginSequence: Could not verify player name for {0} ({1}).",
Player.Name, IP );
if( IP.Equals( IPAddress.Loopback ) && nameVerificationMode == NameVerificationMode.Always ) {
Logger.Log( "{0} Player was identified as connecting from localhost and allowed in.", LogType.SuspiciousActivity,
standardMessage );
} else if( IP.IsLAN() && ConfigKey.AllowUnverifiedLAN.GetBool() ) {
Logger.Log( "{0} Player was identified as connecting from LAN and allowed in.", LogType.SuspiciousActivity,
standardMessage );
} else if( Player.Info.TimesVisited > 1 && Player.Info.LastIP.Equals( IP ) ) {
switch( nameVerificationMode ) {
case NameVerificationMode.Always:
Player.Info.ProcessFailedLogin( this );
Logger.Log( "{0} IP matched previous records for that name. " +
"Player was kicked anyway because VerifyNames is set to Always.", LogType.SuspiciousActivity,
standardMessage );
KickNow( "Could not verify player name!", LeaveReason.UnverifiedName );
return false;
case NameVerificationMode.Balanced:
case NameVerificationMode.Never:
Logger.Log( "{0} IP matched previous records for that name. Player was allowed in.", LogType.SuspiciousActivity,
standardMessage );
break;
}
} else {
switch( nameVerificationMode ) {
case NameVerificationMode.Always:
case NameVerificationMode.Balanced:
Player.Info.ProcessFailedLogin( this );
Logger.Log( "{0} IP did not match. Player was kicked.", LogType.SuspiciousActivity,
standardMessage );
KickNow( "Could not verify player name!", LeaveReason.UnverifiedName );
return false;
case NameVerificationMode.Never:
Logger.Log( "{0} IP did not match. " +
"Player was allowed in anyway because VerifyNames is set to Never.", LogType.SuspiciousActivity,
standardMessage );
Player.Message( "&WYour name could not be verified." );
showVerifyNamesWarning = true;
break;
}
}
}
// Check if player is banned
if( Player.Info.Banned ) {
Player.Info.ProcessFailedLogin( this );
Logger.Log( "Banned player {0} tried to log in from {1}", LogType.SuspiciousActivity,
Player.Name, IP );
if( ConfigKey.ShowBannedConnectionMessages.GetBool() ) {
Server.SendToAllWhoCan( "&SBanned player {0}&S tried to log in from {1}", null, Permission.ViewPlayerIPs,
Player.GetClassyName(), IP );
Server.SendToAllWhoCant( "&SBanned player {0}&S tried to log in.", null, Permission.ViewPlayerIPs,
Player.GetClassyName() );
}
string bannedMessage = String.Format( "Banned {0} ago by {1}: {2}",
Player.Info.TimeSinceBan.ToMiniString(),
Player.Info.BannedBy,
Player.Info.BanReason );
KickNow( bannedMessage, LeaveReason.LoginFailed );
return false;
}
// Check if player's IP is banned
IPBanInfo ipBanInfo = IPBanList.Get( IP );
if( ipBanInfo != null ) {
Player.Info.ProcessFailedLogin( this );
ipBanInfo.ProcessAttempt( Player );
if( ConfigKey.ShowBannedConnectionMessages.GetBool() ) {
Server.SendToAll( "{0}&S tried to log in from a banned IP.", Player.GetClassyName() );
}
Logger.Log( "{0} tried to log in from a banned IP.", LogType.SuspiciousActivity,
Player.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.RegisterSessionAndCheckConnectionCount( this ) ) {
Player.Info.ProcessFailedLogin( this );
Logger.Log( "Session.LoginSequence: Denied player {0}: maximum number of connections was reached for {1}", LogType.SuspiciousActivity,
playerName, IP );
KickNow( String.Format( "Max connection count reached for {0}", IP ), LeaveReason.LoginFailed );
return false;
}
// Check if player is paid (if required)
if( ConfigKey.PaidPlayersOnly.GetBool() ) {
SendNow( PacketWriter.MakeHandshake( Player,
ConfigKey.ServerName.GetString(),
"Please wait; Checking paid status..." ) );
writer.Flush();
if( !Player.CheckPaidStatus( Player.Name ) ) {
Logger.Log( "Player {0} was kicked because their account is not paid, and PaidOnly setting is enabled.", LogType.SystemActivity,
Player.Name );
KickNow( "Paid players allowed only.", LeaveReason.LoginFailed );
return false;
}
}
// Any additional security checks should be done right here
if( Server.RaisePlayerConnectingEvent( Player ) ) return false;
// ----==== beyond this point, player is considered connecting (allowed to join) ====----
// Register player for future block updates
if( !Server.RegisterPlayerAndCheckIfFull( this ) ) {
Logger.Log( "Player {0} was kicked because server is full.", LogType.SystemActivity,
Player.Name );
string kickMessage = String.Format( "Sorry, server is full ({0}/{1})",
Server.PlayerList.Length, ConfigKey.MaxPlayers.GetInt() );
KickNow( kickMessage, LeaveReason.ServerFull );
return false;
}
Player.Info.ProcessLogin( Player );
// ----==== Beyond this point, player is considered connected (authenticated and registered) ====----
World startingWorld = Server.RaisePlayerConnectedEvent( Player, WorldManager.MainWorld );
// Send server information
SendNow( PacketWriter.MakeHandshake( Player, ConfigKey.ServerName.GetString(), ConfigKey.MOTD.GetString() ) );
// AutoRank
if( ConfigKey.AutoRankEnabled.GetBool() ) {
Rank newRank = AutoRankManager.Check( Player.Info );
if( newRank != null ) {
ModerationCommands.DoChangeRank( Player.Console, Player.Info, newRank, "~AutoRank", false, true );
}
}
bool firstTime = (Player.Info.TimesVisited == 1);
if( !JoinWorldNow( startingWorld, true, true ) ) {
Logger.Log( "Failed to load main world ({0}) for connecting player {1} (from {2})", LogType.Error,
startingWorld.Name, Player.Name, IP );
KickNow( "Unable to join the main world.", LeaveReason.WorldFull );
return false;
}
// ==== Beyond this point, player is considered ready (has a world) ====
if( showVerifyNamesWarning ) {
Server.SendToAllExcept( "&WName and IP of {0}&W are unverified!", Player,
Player.GetClassyName() );
}
// Check if other banned players logged in from this IP
PlayerInfo[] bannedPlayerNames = PlayerDB.FindPlayers( IP, 25 ).Where( playerFromSameIP => playerFromSameIP.Banned ).ToArray();
if( bannedPlayerNames.Length > 0 ) {
string logString = String.Format( "&WPlayer {0}&W logged in from an IP previously used by banned players: {1}",
Player.GetClassyName(),
bannedPlayerNames.JoinToClassyString() );
Server.SendToAll( logString );
Logger.Log( logString, LogType.SuspiciousActivity );
}
// Announce join
if( ConfigKey.ShowConnectionMessages.GetBool() ) {
Server.SendToAllExcept( Server.MakePlayerConnectedMessage( Player, firstTime, Player.World ), Player );
}
// check if player is still muted
if( Player.Info.MutedUntil > DateTime.UtcNow ) {
int secondsLeft = (int)Player.Info.MutedUntil.Subtract( DateTime.UtcNow ).TotalSeconds;
Player.Message( "&WYou were previously muted by {0}, {1} seconds left.",
Player.Info.MutedBy, secondsLeft );
Server.SendToAllExcept( "&WPlayer {0}&W was previously muted by {1}&W, {2} seconds left.", Player,
Player.GetClassyName(), Player.Info.MutedBy, secondsLeft );
}
// check if player is still frozen
if( Player.Info.IsFrozen ) {
if( Player.Info.FrozenOn != DateTime.MinValue ) {
Player.Message( "&WYou were previously frozen {0} ago by {1}",
Player.Info.TimeSinceFrozen.ToMiniString(),
Player.Info.FrozenBy );
Server.SendToAllExcept( "&WPlayer {0}&W was previously frozen {1} ago by {2}.", Player,
Player.GetClassyName(),
Player.Info.TimeSinceFrozen.ToMiniString(),
Player.Info.FrozenBy );
} else {
Player.Message( "&WYou were previously frozen by {0}",
Player.Info.FrozenBy );
Server.SendToAllExcept( "&WPlayer {0}&W was previously frozen by {1}.", Player,
Player.GetClassyName(),
Player.Info.FrozenBy );
}
}
// Welcome message
if( File.Exists( Paths.GreetingFileName ) ) {
string[] greetingText = File.ReadAllLines( Paths.GreetingFileName );
foreach( string greetingLine in greetingText ) {
StringBuilder sb = new StringBuilder( greetingLine );
sb.Replace( "{SERVER_NAME}", ConfigKey.ServerName.GetString() );
sb.Replace( "{RANK}", Player.Info.Rank.GetClassyName() );
sb.Replace( "{PLAYER_NAME}", Player.GetClassyName() );
sb.Replace( "{TIME}", DateTime.Now.ToShortTimeString() ); // localized
sb.Replace( "{WORLD}", Player.World.GetClassyName() );
sb.Replace( "{PLAYERS}", Server.CountVisiblePlayers( Player ).ToString() );
sb.Replace( "{WORLDS}", WorldManager.WorldList.Length.ToString() );
sb.Replace( "{MOTD}", ConfigKey.MOTD.GetString() );
Player.Message( sb.ToString() );
}
} else {
if( firstTime ) {
Player.Message( "Welcome to {0}", ConfigKey.ServerName.GetString() );
} else {
Player.Message( "Welcome back to {0}", ConfigKey.ServerName.GetString() );
}
Player.Message( "Your rank is {0}&S. Type &H/help&S for help.",
Player.Info.Rank.GetClassyName() );
}
// A reminder for first-time users
if( PlayerDB.CountTotalPlayers() == 1 && Player.Info.Rank != RankManager.HighestRank ) {
Player.Message( "Type &H/rank {0} {1}&S in console to promote yourself",
Player.Name, RankManager.HighestRank.Name );
}
Server.RaisePlayerReadyEvent( Player );
IsReady = true;
return true;
}