Ostatnio tworzę program do sprawdzania
statusu serwerapewnej gry z użyciem nieoficjalnej
dokumentacji protokołu.
Napisany już przeze mnie
kod źródłowy: (tutejszy parser mi zniekształca kod)
#include <cstring>
#include <iostream>
#include <winsock2.h>
int main( int argc, char * argv[] ) {
char * host = "mc-pl.net";
unsigned short port = 25565;
WSADATA wsaData;
SOCKADDR_IN saddr;
SOCKET sock;
WSAStartup( MAKEWORD( 2, 2 ), & wsaData );
sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
LPHOSTENT hostEntry = gethostbyname( host );
if( !hostEntry ) {
unsigned int addr = inet_addr( host );
hostEntry = gethostbyaddr(( char * ) & addr, 4, AF_INET );
if( !hostEntry ) return 0xDEAD01;
}
saddr.sin_addr.S_un.S_addr
= *(( int * ) * hostEntry->h_addr_list );
saddr.sin_family = AF_INET;
saddr.sin_port = htons( port );
if( connect( sock,( sockaddr * ) & saddr, sizeof( sockaddr ) ) == SOCKET_ERROR )
return 0xDEAD02;
char handshake[] = { 0x14, 0x00, 0xD2, 0x01, 0x0D,
'1', '5', '1', '.',
'8', '0', '.',
'1', '0', '8', '.',
'1', '5',
0x63, 0xDD, 0x01 };
char & hs_lenght = handshake[ 0 ];
char request[] = { 0x01, 0x00 };
char & rq_lenght = request[ 0 ];
char buffer[ 0x10000 ];
short bytes_sent = 0;
short bytes_readen = 0;
bytes_sent += send( sock, handshake, hs_lenght + 1, 0 );
bytes_sent += send( sock, request, rq_lenght + 1, 0 );
bytes_readen += recv( sock, buffer, 0x10000, 0 );
char ping[ 10 ] = { 0x09, 0x01, };
char pong[ 10 ];
send( sock, ping, 10, 0 );
recv( sock, pong, 10, 0 );
char * JSON =( char * ) memchr( buffer, '{', 12 );
if( !memcmp( ping, pong, 10 ) )
std::cout << JSON << '\n' << '\n'
<< "\tping pong is:\tOK!\n"
<< "\tbytes sent:\t" << bytes_sent << '\n'
<< "\tbytes readen:\t" << bytes_readen << '\n';
else return 0xDEAD00;
closesocket( sock );
WSACleanup();
return 0x0;
}
Ogólne informacje co jest wysyłane i otrzymywane:
Ogólny format pakietów ... : (bez kompresji)
... i pola poszczególnych z nich:
Handshake (wysyła klient, ID: 0x00)
Request (wysyła klient, ID: 0x00) // nie posiada pól
Response (wysyła serwer, ID: 0x00)
Ping (wysyła klient, ID:0x00)
Pong (wysyła serwer, ID:0x00) // odpowiedź na Ping
Użyte kodowanie VarInt i VarLong:
Kodowanie jest podobne do tego z
Protocol Buffer,
ale jest zupełnie inne od użytego tutaj.
Najbardziej znaczący bit informuje czy następny bajt również należy do naszej liczby.
W ten sposób otrzymujemy grupki po 7dem bitów w kolejności little endian.
Przy maksymalnym użyciu bajtów (VarInt ma 5, a VarLong 10) przycinane są
najbardziej znaczące bity z najbardziej znaczącej grupki tak
by uzyskać 32 bity przy VarInt'cie i 64 bity przy VarLong'y.
Przykładowe VarInt: // z VarLong'ami jest tak samo.
Przykładowe hostname serwerów do testów:
mc-pl.net play.hivemc.com gommehd.net eu.shotbow.net
Przebieg komunikacji z serwerem:
Hex-dump pakietów jakie powinny zostać wysłane i odebrane od/do
"minecraftpolska.net" [151.80.108.15], aby otrzymać status serwera:
Handshake ( client --> serwer ):
00000000 14 00 d2 01 0d 31 35 31 2e 38 30 2e 31 30 38 2e .....151 .80.108.
00000010 31 35 63 dd 01 15c..
Request ( client --> serwer ):
00000015 01 00
Response ( client <-- serwer ):
// środek pakietu można zignorować, najważniejsze jest jego otrzymanie
00000000 c3 6c 00 c0 6c 7b 22 76 65 72 73 69 6f 6e 22 3a .l..l{"v ersion":
// ----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -----------------
0000363C 69 73 74 22 3a 5b 5d 7d 7d ist":[]} }
Ping ( client --> serwer ):
00000017 09 01 xx xx xx xx xx xx xx xx ........ ..
Pong ( client <-- serwer ):
00003645 09 01 xx xx xx xx xx xx xx xx ........ ..
Tu połączenie jest zamykane.
________________________________________________________
PRE-EDIT: Nie zapominać o linkowaniu
ws2_32.dllz folderu systemowego: "C:\Windows\System32\ws2_32.dll"