2013-07-18 09:27:19 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.Net ;
using System.Net.Sockets ;
namespace MinecraftClient
{
/// <summary>
/// The class containing all the core functions needed to communicate with a Minecraft server.
/// </summary>
2013-08-06 12:25:09 +02:00
public class MinecraftCom : IAutoComplete
2013-07-18 09:27:19 +02:00
{
#region Login to Minecraft . net , Obtaining a session ID
2013-08-13 12:20:16 +02:00
public enum LoginResult { Error , Success , WrongPassword , Blocked , AccountMigrated , NotPremium , BadRequest } ;
2013-07-20 11:01:49 +10:00
2013-07-18 09:27:19 +02:00
/// <summary>
/// Allows to login to a premium Minecraft account, and retrieve the session ID.
/// </summary>
/// <param name="user">Login</param>
/// <param name="pass">Password</param>
/// <param name="outdata">Will contain the data returned by Minecraft.net, if the login is successful : Version:UpdateTicket:Username:SessionID</param>
/// <returns>Returns the status of the login (Success, Failure, etc.)</returns>
public static LoginResult GetLogin ( string user , string pass , ref string outdata )
{
try
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
WebClient wClient = new WebClient ( ) ;
2013-07-20 13:02:54 +02:00
Console . WriteLine ( "https://login.minecraft.net/?user=" + user + "&password=<******>&version=13" ) ;
2013-08-13 12:20:16 +02:00
string result = Encoding . ASCII . GetString ( wClient . UploadValues ( "https://login.minecraft.net/" , new System . Collections . Specialized . NameValueCollection ( ) { { "user" , user } , { "password" , pass } , { "version" , "13" } } ) ) ;
2013-07-18 09:27:19 +02:00
outdata = result ;
Console . WriteLine ( result ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
if ( result = = "Bad login" ) { return LoginResult . WrongPassword ; }
2013-08-13 12:20:16 +02:00
if ( result = = "Bad request" ) { return LoginResult . BadRequest ; }
2013-07-18 09:27:19 +02:00
if ( result = = "User not premium" ) { return LoginResult . NotPremium ; }
if ( result = = "Too many failed logins" ) { return LoginResult . Blocked ; }
if ( result = = "Account migrated, use e-mail as username." ) { return LoginResult . AccountMigrated ; }
else return LoginResult . Success ;
}
catch ( WebException ) { return LoginResult . Error ; }
}
#endregion
#region Keep - Alive for a Minecraft . net session , should be called every 5 minutes ( currently unused )
/// <summary>
/// The session ID will expire within 5 minutes unless this function is called every 5 minutes
/// </summary>
/// <param name="user">Username</param>
/// <param name="sessionID">Session ID to keep alive</param>
public static void SessionKeepAlive ( string user , string sessionID )
{
new WebClient ( ) . DownloadString ( "https://login.minecraft.net/session?name=" + user + "&session=" + sessionID ) ;
}
#endregion
#region Session checking when joining a server in online mode
/// <summary>
/// This method allows to join an online-mode server.
/// It Should be called between the handshake and the login attempt.
/// </summary>
/// <param name="user">Username</param>
/// <param name="sessionID">A valid session ID for this username</param>
/// <param name="hash">Hash returned by the server during the handshake</param>
/// <returns>Returns true if the check was successful</returns>
public static bool SessionCheck ( string user , string sessionID , string hash )
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
WebClient client = new WebClient ( ) ;
Console . Write ( "http://session.minecraft.net/game/joinserver.jsp?user=" + user + "&sessionId=" + sessionID + "&serverId=" + hash + " ... " ) ;
string result = client . DownloadString ( "http://session.minecraft.net/game/joinserver.jsp?user=" + user + "&sessionId=" + sessionID + "&serverId=" + hash ) ;
Console . WriteLine ( result ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
return ( result = = "OK" ) ;
}
#endregion
#region Server - side session checking ( programmed for testing purposes )
/// <summary>
/// Reproduces the username checking done by an online-mode server during the login process.
/// </summary>
/// <param name="user">Username</param>
/// <param name="hash">Hash sent by the server during the handshake</param>
/// <returns>Returns true if the user is allowed to join the server</returns>
public static bool ServerSessionCheck ( string user , string hash )
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
WebClient client = new WebClient ( ) ;
ConsoleIO . WriteLine ( "http://session.minecraft.net/game/checkserver.jsp?user=" + user + "&serverId=" + hash ) ;
string result = client . DownloadString ( "http://session.minecraft.net/game/checkserver.jsp?user=" + user + "&serverId=" + hash ) ;
ConsoleIO . WriteLine ( result ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
return ( result = = "YES" ) ;
}
#endregion
TcpClient c = new TcpClient ( ) ;
Crypto . AesStream s ;
public bool HasBeenKicked { get { return connectionlost ; } }
bool connectionlost = false ;
bool encrypted = false ;
byte protocolversion ;
public bool Update ( )
{
for ( int i = 0 ; i < bots . Count ; i + + ) { bots [ i ] . Update ( ) ; }
if ( c . Client = = null | | ! c . Connected ) { return false ; }
byte id = 0 ;
try
{
while ( c . Client . Available > 0 )
{
id = readNextByte ( ) ;
ProcessResult result = processPacket ( id ) ;
//Debug : Print packet IDs that are beign processed. Green = OK, Red = Unknown packet
//If the client gets out of sync, check the last green packet processing code.
//if (result == ProcessResult.OK) { printstring("§a0x" + id.ToString("X"), false); }
//else { printstring("§c0x" + id.ToString("X"), false); }
2013-07-20 11:01:49 +10:00
2013-07-18 09:27:19 +02:00
if ( result = = ProcessResult . ConnectionLost )
{
return false ;
}
}
}
catch ( SocketException ) { return false ; }
return true ;
}
public void DebugDump ( )
{
byte [ ] cache = new byte [ 128000 ] ;
Receive ( cache , 0 , 128000 , SocketFlags . None ) ;
string dump = BitConverter . ToString ( cache ) ;
System . IO . File . WriteAllText ( "debug.txt" , dump ) ;
System . Diagnostics . Process . Start ( "debug.txt" ) ;
}
public bool OnConnectionLost ( )
{
if ( ! connectionlost )
{
connectionlost = true ;
for ( int i = 0 ; i < bots . Count ; i + + )
{
if ( bots [ i ] . OnDisconnect ( ChatBot . DisconnectReason . ConnectionLost , "Connection has been lost." ) )
{
return true ; //The client is about to restart
}
}
}
return false ;
}
private enum ProcessResult { OK , ConnectionLost , UnknownPacket }
private ProcessResult processPacket ( int id )
{
int nbr = 0 ;
switch ( id )
{
case 0x00 : byte [ ] keepalive = new byte [ 5 ] { 0 , 0 , 0 , 0 , 0 } ;
Receive ( keepalive , 1 , 4 , SocketFlags . None ) ;
Send ( keepalive ) ; break ;
case 0x01 : readData ( 4 ) ; readNextString ( ) ; readData ( 5 ) ; break ;
case 0x02 : readData ( 1 ) ; readNextString ( ) ; readNextString ( ) ; readData ( 4 ) ; break ;
case 0x03 :
string message = readNextString ( ) ;
if ( protocolversion > = 72 )
{
2013-08-08 10:51:59 +02:00
//printstring("§8" + message, false); //Debug : Show the RAW JSON data
2013-07-18 09:27:19 +02:00
message = ChatParser . ParseText ( message ) ;
printstring ( message , false ) ;
}
else printstring ( message , false ) ;
for ( int i = 0 ; i < bots . Count ; i + + ) { bots [ i ] . GetText ( message ) ; } break ;
case 0x04 : readData ( 16 ) ; break ;
case 0x05 : readData ( 6 ) ; readNextItemSlot ( ) ; break ;
case 0x06 : readData ( 12 ) ; break ;
case 0x07 : readData ( 9 ) ; break ;
case 0x08 : if ( protocolversion > = 72 ) { readData ( 10 ) ; } else readData ( 8 ) ; break ;
case 0x09 : readData ( 8 ) ; readNextString ( ) ; break ;
case 0x0A : readData ( 1 ) ; break ;
case 0x0B : readData ( 33 ) ; break ;
case 0x0C : readData ( 9 ) ; break ;
case 0x0D : readData ( 41 ) ; break ;
case 0x0E : readData ( 11 ) ; break ;
case 0x0F : readData ( 10 ) ; readNextItemSlot ( ) ; readData ( 3 ) ; break ;
case 0x10 : readData ( 2 ) ; break ;
case 0x11 : readData ( 14 ) ; break ;
case 0x12 : readData ( 5 ) ; break ;
case 0x13 : if ( protocolversion > = 72 ) { readData ( 9 ) ; } else readData ( 5 ) ; break ;
case 0x14 : readData ( 4 ) ; readNextString ( ) ; readData ( 16 ) ; readNextEntityMetaData ( ) ; break ;
case 0x16 : readData ( 8 ) ; break ;
case 0x17 : readData ( 19 ) ; readNextObjectData ( ) ; break ;
case 0x18 : readData ( 26 ) ; readNextEntityMetaData ( ) ; break ;
case 0x19 : readData ( 4 ) ; readNextString ( ) ; readData ( 16 ) ; break ;
case 0x1A : readData ( 18 ) ; break ;
case 0x1B : if ( protocolversion > = 72 ) { readData ( 10 ) ; } break ;
case 0x1C : readData ( 10 ) ; break ;
case 0x1D : nbr = ( int ) readNextByte ( ) ; readData ( nbr * 4 ) ; break ;
case 0x1E : readData ( 4 ) ; break ;
case 0x1F : readData ( 7 ) ; break ;
case 0x20 : readData ( 6 ) ; break ;
case 0x21 : readData ( 9 ) ; break ;
case 0x22 : readData ( 18 ) ; break ;
case 0x23 : readData ( 5 ) ; break ;
case 0x26 : readData ( 5 ) ; break ;
case 0x27 : if ( protocolversion > = 72 ) { readData ( 9 ) ; } else readData ( 8 ) ; break ;
case 0x28 : readData ( 4 ) ; readNextEntityMetaData ( ) ; break ;
case 0x29 : readData ( 8 ) ; break ;
case 0x2A : readData ( 5 ) ; break ;
case 0x2B : readData ( 8 ) ; break ;
case 0x2C : if ( protocolversion > = 72 ) { readNextEntityProperties ( protocolversion ) ; } break ;
case 0x33 : readData ( 13 ) ; nbr = readNextInt ( ) ; readData ( nbr ) ; break ;
case 0x34 : readData ( 10 ) ; nbr = readNextInt ( ) ; readData ( nbr ) ; break ;
case 0x35 : readData ( 12 ) ; break ;
case 0x36 : readData ( 14 ) ; break ;
case 0x37 : readData ( 17 ) ; break ;
case 0x38 : readNextChunkBulkData ( ) ; break ;
case 0x3C : readData ( 28 ) ; nbr = readNextInt ( ) ; readData ( 3 * nbr ) ; readData ( 12 ) ; break ;
case 0x3D : readData ( 18 ) ; break ;
case 0x3E : readNextString ( ) ; readData ( 17 ) ; break ;
case 0x3F : if ( protocolversion > 51 ) { readNextString ( ) ; readData ( 32 ) ; } break ;
case 0x46 : readData ( 2 ) ; break ;
case 0x47 : readData ( 17 ) ; break ;
case 0x64 : readNextWindowData ( protocolversion ) ; break ;
case 0x65 : readData ( 1 ) ; break ;
case 0x66 : readData ( 7 ) ; readNextItemSlot ( ) ; break ;
case 0x67 : readData ( 3 ) ; readNextItemSlot ( ) ; break ;
case 0x68 : readData ( 1 ) ; for ( nbr = readNextShort ( ) ; nbr > 0 ; nbr - - ) { readNextItemSlot ( ) ; } break ;
case 0x69 : readData ( 5 ) ; break ;
case 0x6A : readData ( 4 ) ; break ;
case 0x6B : readData ( 2 ) ; readNextItemSlot ( ) ; break ;
case 0x6C : readData ( 2 ) ; break ;
case 0x82 : readData ( 10 ) ; readNextString ( ) ; readNextString ( ) ; readNextString ( ) ; readNextString ( ) ; break ;
case 0x83 : readData ( 4 ) ; nbr = readNextShort ( ) ; readData ( nbr ) ; break ;
case 0x84 : readData ( 11 ) ; nbr = readNextShort ( ) ; if ( nbr > 0 ) { readData ( nbr ) ; } break ;
case 0x85 : if ( protocolversion > = 74 ) { readData ( 13 ) ; } break ;
2013-08-06 11:28:27 +02:00
case 0xC8 :
if ( readNextInt ( ) = = 2022 ) { printstring ( "You are dead. Type /reco to respawn & reconnect." , false ) ; }
if ( protocolversion > = 72 ) { readData ( 4 ) ; } else readData ( 1 ) ;
break ;
2013-07-18 09:27:19 +02:00
case 0xC9 : readNextString ( ) ; readData ( 3 ) ; break ;
case 0xCA : if ( protocolversion > = 72 ) { readData ( 9 ) ; } else readData ( 3 ) ; break ;
2013-08-06 12:25:09 +02:00
case 0xCB : autocomplete_result = readNextString ( ) ; autocomplete_received = true ; break ;
2013-07-18 09:27:19 +02:00
case 0xCC : readNextString ( ) ; readData ( 4 ) ; break ;
case 0xCD : readData ( 1 ) ; break ;
case 0xCE : if ( protocolversion > 51 ) { readNextString ( ) ; readNextString ( ) ; readData ( 1 ) ; } break ;
case 0xCF : if ( protocolversion > 51 ) { readNextString ( ) ; readData ( 1 ) ; readNextString ( ) ; } readData ( 4 ) ; break ;
case 0xD0 : if ( protocolversion > 51 ) { readData ( 1 ) ; readNextString ( ) ; } break ;
case 0xD1 : if ( protocolversion > 51 ) { readNextTeamData ( ) ; } break ;
case 0xFA : readNextString ( ) ; nbr = readNextShort ( ) ; readData ( nbr ) ; break ;
case 0xFF : string reason = readNextString ( ) ;
ConsoleIO . WriteLine ( "Disconnected by Server :" ) ; printstring ( reason , true ) ; connectionlost = true ;
for ( int i = 0 ; i < bots . Count ; i + + ) { bots [ i ] . OnDisconnect ( ChatBot . DisconnectReason . InGameKick , reason ) ; } return ProcessResult . ConnectionLost ;
default : return ProcessResult . UnknownPacket ; //unknown packet!
}
return ProcessResult . OK ; //packet has been successfully skipped
}
private void readData ( int offset )
{
if ( offset > 0 )
{
try
{
byte [ ] cache = new byte [ offset ] ;
Receive ( cache , 0 , offset , SocketFlags . None ) ;
}
catch ( OutOfMemoryException ) { }
}
}
private string readNextString ( )
{
2013-07-20 21:44:19 +02:00
ushort length = ( ushort ) readNextShort ( ) ;
2013-07-19 21:45:01 +10:00
if ( length > 0 )
2013-07-18 09:27:19 +02:00
{
2013-07-19 21:45:01 +10:00
byte [ ] cache = new byte [ length * 2 ] ;
Receive ( cache , 0 , length * 2 , SocketFlags . None ) ;
2013-07-20 21:44:19 +02:00
string result = Encoding . BigEndianUnicode . GetString ( cache ) ;
2013-07-18 09:27:19 +02:00
return result ;
}
else return "" ;
}
private byte [ ] readNextByteArray ( )
{
short len = readNextShort ( ) ;
byte [ ] data = new byte [ len ] ;
Receive ( data , 0 , len , SocketFlags . None ) ;
return data ;
}
private short readNextShort ( )
{
byte [ ] tmp = new byte [ 2 ] ;
Receive ( tmp , 0 , 2 , SocketFlags . None ) ;
Array . Reverse ( tmp ) ;
return BitConverter . ToInt16 ( tmp , 0 ) ;
}
private int readNextInt ( )
{
byte [ ] tmp = new byte [ 4 ] ;
Receive ( tmp , 0 , 4 , SocketFlags . None ) ;
Array . Reverse ( tmp ) ;
return BitConverter . ToInt32 ( tmp , 0 ) ;
}
private byte readNextByte ( )
{
byte [ ] result = new byte [ 1 ] ;
Receive ( result , 0 , 1 , SocketFlags . None ) ;
return result [ 0 ] ;
}
private void readNextItemSlot ( )
{
short itemid = readNextShort ( ) ;
//If slot not empty (item ID != -1)
if ( itemid ! = - 1 )
{
readData ( 1 ) ; //Item count
readData ( 2 ) ; //Item damage
short length = readNextShort ( ) ;
2013-07-19 21:45:01 +10:00
//If length of optional NBT data > 0, read it
2013-07-18 09:27:19 +02:00
if ( length > 0 ) { readData ( length ) ; }
}
}
private void readNextEntityMetaData ( )
{
do
{
byte [ ] id = new byte [ 1 ] ;
Receive ( id , 0 , 1 , SocketFlags . None ) ;
if ( id [ 0 ] = = 0x7F ) { break ; }
int index = id [ 0 ] & 0x1F ;
int type = id [ 0 ] > > 5 ;
switch ( type )
{
case 0 : readData ( 1 ) ; break ; //Byte
case 1 : readData ( 2 ) ; break ; //Short
case 2 : readData ( 4 ) ; break ; //Int
case 3 : readData ( 4 ) ; break ; //Float
case 4 : readNextString ( ) ; break ; //String
case 5 : readNextItemSlot ( ) ; break ; //Slot
case 6 : readData ( 12 ) ; break ; //Vector (3 Int)
}
} while ( true ) ;
}
private void readNextObjectData ( )
{
int id = readNextInt ( ) ;
if ( id ! = 0 ) { readData ( 6 ) ; }
}
private void readNextTeamData ( )
{
readNextString ( ) ; //Internal Name
byte mode = readNextByte ( ) ;
if ( mode = = 0 | | mode = = 2 )
{
readNextString ( ) ; //Display Name
readNextString ( ) ; //Prefix
readNextString ( ) ; //Suffix
readData ( 1 ) ; //Friendly Fire
}
if ( mode = = 0 | | mode = = 3 | | mode = = 4 )
{
short count = readNextShort ( ) ;
for ( int i = 0 ; i < count ; i + + )
{
readNextString ( ) ; //Players
}
}
}
private void readNextEntityProperties ( int protocolversion )
{
if ( protocolversion > = 72 )
{
if ( protocolversion > = 74 )
{
//Minecraft 1.6.2
readNextInt ( ) ; //Entity ID
int count = readNextInt ( ) ;
for ( int i = 0 ; i < count ; i + + )
{
readNextString ( ) ; //Property name
readData ( 8 ) ; //Property value (Double)
short othercount = readNextShort ( ) ;
readData ( 25 * othercount ) ;
}
}
else
{
//Minecraft 1.6.0 / 1.6.1
readNextInt ( ) ; //Entity ID
int count = readNextInt ( ) ;
for ( int i = 0 ; i < count ; i + + )
{
readNextString ( ) ; //Property name
readData ( 8 ) ; //Property value (Double)
}
}
}
}
private void readNextWindowData ( int protocolversion )
{
readData ( 1 ) ;
byte windowtype = readNextByte ( ) ;
readNextString ( ) ;
readData ( 1 ) ;
if ( protocolversion > 51 )
{
readData ( 1 ) ;
if ( protocolversion > = 72 & & windowtype = = 0xb )
{
readNextInt ( ) ;
}
}
}
private void readNextChunkBulkData ( )
{
short chunkcount = readNextShort ( ) ;
int datalen = readNextInt ( ) ;
readData ( 1 ) ;
readData ( datalen ) ;
readData ( 12 * ( chunkcount ) ) ;
}
2013-07-27 21:31:03 +02:00
private static void setcolor ( char c )
2013-07-18 09:27:19 +02:00
{
switch ( c )
{
case '0' : Console . ForegroundColor = ConsoleColor . Gray ; break ; //Should be Black but Black is non-readable on a black background
case '1' : Console . ForegroundColor = ConsoleColor . DarkBlue ; break ;
case '2' : Console . ForegroundColor = ConsoleColor . DarkGreen ; break ;
case '3' : Console . ForegroundColor = ConsoleColor . DarkCyan ; break ;
case '4' : Console . ForegroundColor = ConsoleColor . DarkRed ; break ;
case '5' : Console . ForegroundColor = ConsoleColor . DarkMagenta ; break ;
case '6' : Console . ForegroundColor = ConsoleColor . DarkYellow ; break ;
case '7' : Console . ForegroundColor = ConsoleColor . Gray ; break ;
case '8' : Console . ForegroundColor = ConsoleColor . DarkGray ; break ;
case '9' : Console . ForegroundColor = ConsoleColor . Blue ; break ;
case 'a' : Console . ForegroundColor = ConsoleColor . Green ; break ;
case 'b' : Console . ForegroundColor = ConsoleColor . Cyan ; break ;
case 'c' : Console . ForegroundColor = ConsoleColor . Red ; break ;
case 'd' : Console . ForegroundColor = ConsoleColor . Magenta ; break ;
case 'e' : Console . ForegroundColor = ConsoleColor . Yellow ; break ;
case 'f' : Console . ForegroundColor = ConsoleColor . White ; break ;
case 'r' : Console . ForegroundColor = ConsoleColor . White ; break ;
}
}
2013-07-27 21:31:03 +02:00
private static void printstring ( string str , bool acceptnewlines )
2013-07-18 09:27:19 +02:00
{
2013-07-27 18:44:54 +02:00
if ( ! String . IsNullOrEmpty ( str ) )
2013-07-18 09:27:19 +02:00
{
2013-07-27 18:44:54 +02:00
if ( ! acceptnewlines ) { str = str . Replace ( '\n' , ' ' ) ; }
2013-08-15 18:04:29 +02:00
if ( ConsoleIO . basicIO ) { ConsoleIO . WriteLine ( str ) ; return ; }
2013-07-27 18:44:54 +02:00
string [ ] subs = str . Split ( new char [ ] { '§' } ) ;
if ( subs [ 0 ] . Length > 0 ) { ConsoleIO . Write ( subs [ 0 ] ) ; }
for ( int i = 1 ; i < subs . Length ; i + + )
2013-07-18 09:27:19 +02:00
{
2013-07-27 18:44:54 +02:00
if ( subs [ i ] . Length > 0 )
2013-07-18 09:27:19 +02:00
{
2013-07-27 18:44:54 +02:00
setcolor ( subs [ i ] [ 0 ] ) ;
if ( subs [ i ] . Length > 1 )
{
ConsoleIO . Write ( subs [ i ] . Substring ( 1 , subs [ i ] . Length - 1 ) ) ;
}
2013-07-18 09:27:19 +02:00
}
}
ConsoleIO . Write ( '\n' ) ;
}
Console . ForegroundColor = ConsoleColor . Gray ;
}
2013-08-06 12:25:09 +02:00
private bool autocomplete_received = false ;
private string autocomplete_result = "" ;
public string AutoComplete ( string behindcursor )
{
if ( String . IsNullOrEmpty ( behindcursor ) )
return "" ;
byte [ ] autocomplete = new byte [ 3 + ( behindcursor . Length * 2 ) ] ;
autocomplete [ 0 ] = 0xCB ;
byte [ ] msglen = BitConverter . GetBytes ( ( short ) behindcursor . Length ) ;
Array . Reverse ( msglen ) ; msglen . CopyTo ( autocomplete , 1 ) ;
byte [ ] msg = Encoding . BigEndianUnicode . GetBytes ( behindcursor ) ;
msg . CopyTo ( autocomplete , 3 ) ;
autocomplete_received = false ;
autocomplete_result = behindcursor ;
Send ( autocomplete ) ;
int wait_left = 50 ; //do not wait more than 5 seconds (50 * 100 ms)
while ( wait_left > 0 & & ! autocomplete_received ) { System . Threading . Thread . Sleep ( 100 ) ; wait_left - - ; }
string [ ] results = autocomplete_result . Split ( ( char ) 0x00 ) ;
return results [ 0 ] ;
}
2013-07-18 09:27:19 +02:00
public void setVersion ( byte ver ) { protocolversion = ver ; }
public void setClient ( TcpClient n ) { c = n ; }
private void setEncryptedClient ( Crypto . AesStream n ) { s = n ; encrypted = true ; }
private void Receive ( byte [ ] buffer , int start , int offset , SocketFlags f )
{
while ( c . Client . Available < start + offset ) { }
if ( encrypted )
{
s . Read ( buffer , start , offset ) ;
}
else c . Client . Receive ( buffer , start , offset , f ) ;
}
private void Send ( byte [ ] buffer )
{
if ( encrypted )
{
s . Write ( buffer , 0 , buffer . Length ) ;
}
else c . Client . Send ( buffer ) ;
}
public static bool GetServerInfo ( string serverIP , ref byte protocolversion , ref string version )
{
try
{
string host ; int port ;
string [ ] sip = serverIP . Split ( ':' ) ;
host = sip [ 0 ] ;
if ( sip . Length = = 1 )
{
port = 25565 ;
}
else
{
try
{
port = Convert . ToInt32 ( sip [ 1 ] ) ;
}
catch ( FormatException ) { port = 25565 ; }
}
TcpClient tcp = new TcpClient ( host , port ) ;
byte [ ] ping = new byte [ 2 ] { 0xfe , 0x01 } ;
tcp . Client . Send ( ping , SocketFlags . None ) ;
tcp . Client . Receive ( ping , 0 , 1 , SocketFlags . None ) ;
if ( ping [ 0 ] = = 0xff )
{
MinecraftCom ComTmp = new MinecraftCom ( ) ;
ComTmp . setClient ( tcp ) ;
string result = ComTmp . readNextString ( ) ;
Console . ForegroundColor = ConsoleColor . DarkGray ;
//Console.WriteLine(result.Replace((char)0x00, ' '));
if ( result . Length > 2 & & result [ 0 ] = = '§' & & result [ 1 ] = = '1' )
{
string [ ] tmp = result . Split ( ( char ) 0x00 ) ;
protocolversion = ( byte ) Int16 . Parse ( tmp [ 1 ] ) ;
version = tmp [ 2 ] ;
}
else
{
protocolversion = ( byte ) 39 ;
version = "B1.8.1 - 1.3.2" ;
}
Console . WriteLine ( "Server version : MC " + version + " (protocol v" + protocolversion + ")." ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
return true ;
}
else
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
Console . WriteLine ( "Unexpected answer from the server (is that a Minecraft server ?)" ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
return false ;
}
}
catch
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
Console . WriteLine ( "An error occured while attempting to connect to this IP." ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
return false ;
}
}
public bool Handshake ( string username , string sessionID , ref string serverID , ref byte [ ] token , string host , int port )
{
//array
2013-07-20 21:44:19 +02:00
byte [ ] data = new byte [ 10 + ( username . Length + host . Length ) * 2 ] ;
2013-07-18 09:27:19 +02:00
//packet id
data [ 0 ] = ( byte ) 2 ;
2013-07-20 21:44:19 +02:00
//Protocol Version
2013-07-18 09:27:19 +02:00
data [ 1 ] = protocolversion ;
//short len
2013-07-18 08:02:05 -07:00
byte [ ] sh = BitConverter . GetBytes ( ( short ) username . Length ) ;
2013-07-18 09:27:19 +02:00
Array . Reverse ( sh ) ;
sh . CopyTo ( data , 2 ) ;
//username
2013-07-20 21:44:19 +02:00
byte [ ] bname = Encoding . BigEndianUnicode . GetBytes ( username ) ;
bname . CopyTo ( data , 4 ) ;
2013-07-18 09:27:19 +02:00
//short len
sh = BitConverter . GetBytes ( ( short ) host . Length ) ;
Array . Reverse ( sh ) ;
sh . CopyTo ( data , 4 + ( username . Length * 2 ) ) ;
//host
2013-07-20 21:44:19 +02:00
byte [ ] bhost = Encoding . BigEndianUnicode . GetBytes ( host ) ;
2013-07-18 09:27:19 +02:00
bhost . CopyTo ( data , 6 + ( username . Length * 2 ) ) ;
//port
sh = BitConverter . GetBytes ( port ) ;
Array . Reverse ( sh ) ;
sh . CopyTo ( data , 6 + ( username . Length * 2 ) + ( host . Length * 2 ) ) ;
Send ( data ) ;
byte [ ] pid = new byte [ 1 ] ;
Receive ( pid , 0 , 1 , SocketFlags . None ) ;
while ( pid [ 0 ] = = 0xFA ) //Skip some early plugin messages
{
processPacket ( pid [ 0 ] ) ;
Receive ( pid , 0 , 1 , SocketFlags . None ) ;
}
if ( pid [ 0 ] = = 0xFD )
{
serverID = readNextString ( ) ;
byte [ ] Serverkey_RAW = readNextByteArray ( ) ;
token = readNextByteArray ( ) ;
if ( serverID = = "-" )
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
Console . WriteLine ( "Server is in offline mode." ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
return true ; //No need to check session or start encryption
}
else
{
var PublicServerkey = Crypto . GenerateRSAPublicKey ( Serverkey_RAW ) ;
var SecretKey = Crypto . GenerateAESPrivateKey ( ) ;
Console . ForegroundColor = ConsoleColor . DarkGray ;
2013-08-23 10:48:26 +02:00
Console . WriteLine ( "Handshake successful. (Server ID: " + serverID + ')' ) ;
2013-07-18 09:27:19 +02:00
Console . ForegroundColor = ConsoleColor . Gray ;
2013-07-20 21:44:19 +02:00
return StartEncryption ( username , sessionID , token , serverID , PublicServerkey , SecretKey ) ;
2013-07-18 09:27:19 +02:00
}
}
else return false ;
}
public bool StartEncryption ( string username , string sessionID , byte [ ] token , string serverIDhash , java . security . PublicKey serverKey , javax . crypto . SecretKey secretKey )
{
Console . ForegroundColor = ConsoleColor . DarkGray ;
ConsoleIO . WriteLine ( "Crypto keys & hash generated." ) ;
Console . ForegroundColor = ConsoleColor . Gray ;
if ( serverIDhash ! = "-" )
{
Console . WriteLine ( "Checking Session..." ) ;
if ( ! SessionCheck ( username , sessionID , new java . math . BigInteger ( Crypto . getServerHash ( serverIDhash , serverKey , secretKey ) ) . toString ( 16 ) ) )
{
return false ;
}
}
//Encrypt the data
byte [ ] key_enc = Crypto . Encrypt ( serverKey , secretKey . getEncoded ( ) ) ;
byte [ ] token_enc = Crypto . Encrypt ( serverKey , token ) ;
byte [ ] keylen = BitConverter . GetBytes ( ( short ) key_enc . Length ) ;
byte [ ] tokenlen = BitConverter . GetBytes ( ( short ) token_enc . Length ) ;
Array . Reverse ( keylen ) ;
Array . Reverse ( tokenlen ) ;
//Building the packet
byte [ ] data = new byte [ 5 + ( short ) key_enc . Length + ( short ) token_enc . Length ] ;
data [ 0 ] = 0xFC ;
keylen . CopyTo ( data , 1 ) ;
key_enc . CopyTo ( data , 3 ) ;
tokenlen . CopyTo ( data , 3 + ( short ) key_enc . Length ) ;
token_enc . CopyTo ( data , 5 + ( short ) key_enc . Length ) ;
//Send it back
Send ( data ) ;
//Getting the next packet
byte [ ] pid = new byte [ 1 ] ;
Receive ( pid , 0 , 1 , SocketFlags . None ) ;
if ( pid [ 0 ] = = 0xFC )
{
readData ( 4 ) ;
setEncryptedClient ( Crypto . SwitchToAesMode ( c . GetStream ( ) , secretKey ) ) ;
return true ;
}
else return false ;
}
public bool FinalizeLogin ( )
{
2013-07-27 21:31:03 +02:00
Send ( new byte [ ] { 0xCD , 0 } ) ;
2013-07-18 09:27:19 +02:00
try
{
byte [ ] pid = new byte [ 1 ] ;
try
{
if ( c . Connected )
{
Receive ( pid , 0 , 1 , SocketFlags . None ) ;
while ( pid [ 0 ] > = 0xC0 & & pid [ 0 ] ! = 0xFF ) //Skip some early packets or plugin messages
{
processPacket ( pid [ 0 ] ) ;
Receive ( pid , 0 , 1 , SocketFlags . None ) ;
}
if ( pid [ 0 ] = = ( byte ) 1 )
{
readData ( 4 ) ; readNextString ( ) ; readData ( 5 ) ;
return true ; //The Server accepted the request
}
else if ( pid [ 0 ] = = ( byte ) 0xFF )
{
string reason = readNextString ( ) ;
Console . WriteLine ( "Login rejected by Server :" ) ; printstring ( reason , true ) ;
for ( int i = 0 ; i < bots . Count ; i + + ) { bots [ i ] . OnDisconnect ( ChatBot . DisconnectReason . LoginRejected , reason ) ; }
return false ;
}
}
}
catch
{
//Connection failed
return false ;
}
}
catch
{
//Network error
Console . WriteLine ( "Connection Lost." ) ;
return false ;
}
return false ; //Login was unsuccessful (received a kick...)
}
public bool SendChatMessage ( string message )
{
2013-07-20 21:44:19 +02:00
if ( String . IsNullOrEmpty ( message ) )
return true ;
2013-07-18 09:27:19 +02:00
try
{
byte [ ] chat = new byte [ 3 + ( message . Length * 2 ) ] ;
chat [ 0 ] = ( byte ) 3 ;
2013-07-18 07:24:52 -07:00
byte [ ] msglen ;
2013-07-18 09:27:19 +02:00
msglen = BitConverter . GetBytes ( ( short ) message . Length ) ;
Array . Reverse ( msglen ) ;
msglen . CopyTo ( chat , 1 ) ;
2013-07-18 07:24:52 -07:00
byte [ ] msg ;
2013-07-20 21:44:19 +02:00
msg = Encoding . BigEndianUnicode . GetBytes ( message ) ;
2013-07-18 09:27:19 +02:00
msg . CopyTo ( chat , 3 ) ;
Send ( chat ) ;
return true ;
}
catch ( SocketException ) { return false ; }
}
2013-08-06 11:28:27 +02:00
public bool SendRespawnPacket ( )
{
try
{
Send ( new byte [ ] { 0xCD , 1 } ) ;
return true ;
}
catch ( SocketException ) { return false ; }
}
2013-07-18 09:27:19 +02:00
public void Disconnect ( string message )
{
2013-07-20 21:44:19 +02:00
if ( message = = null )
message = "" ;
2013-07-18 09:27:19 +02:00
try
{
byte [ ] reason = new byte [ 3 + ( message . Length * 2 ) ] ;
reason [ 0 ] = ( byte ) 0xff ;
2013-07-18 07:24:52 -07:00
byte [ ] msglen ;
2013-07-18 09:27:19 +02:00
msglen = BitConverter . GetBytes ( ( short ) message . Length ) ;
Array . Reverse ( msglen ) ;
msglen . CopyTo ( reason , 1 ) ;
2013-07-27 21:31:03 +02:00
if ( message . Length > 0 )
{
byte [ ] msg ;
msg = Encoding . BigEndianUnicode . GetBytes ( message ) ;
msg . CopyTo ( reason , 3 ) ;
}
2013-07-18 09:27:19 +02:00
Send ( reason ) ;
}
catch ( SocketException ) { }
catch ( System . IO . IOException ) { }
}
private List < ChatBot > bots = new List < ChatBot > ( ) ;
2013-08-06 16:11:46 +02:00
public void BotLoad ( ChatBot b ) { b . SetHandler ( this ) ; bots . Add ( b ) ; b . Initialize ( ) ; Settings . SingleCommand = "" ; }
2013-07-18 09:27:19 +02:00
public void BotUnLoad ( ChatBot b ) { bots . RemoveAll ( item = > object . ReferenceEquals ( item , b ) ) ; }
public void BotClear ( ) { bots . Clear ( ) ; }
}
}