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;
}