Outils pour utilisateurs

Outils du site


misc:minecraft_client

===== Un client MineCraft ===== Un client Minecraft non fonctionnel car il lis mal les packets dans le buffer, néamoins il est capable de se connecter et peut servir de base à ceux qui veulent développer un client en C. Pour plus d'infos sur le developpement MineCraft : http://wiki.vg/ Ça peut aussi servir à ceux qui veulent savoir comment programmer un socket TCP en C. ==== Changelog ==== * 21/12/11 MàJ pour MC 1.0.0 <file c mcclient.c> /***********************************************\ * MCCLIENT .C * * Minecraft client exemple in C by Mr-Hide * * * * 1 set the username, hostname and * * port at the end of this file * * * * 2 gcc -o mcclient mcclient.c * * * * 3 ... * * * * 4 PROFITS $$$ * \***********************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <errno.h> #include <signal.h> /* To avoid SIGPIPE on write on broken socket */ /* HEADERS : */ #define KEEPALIVE 0x00 /* This packet may be sent to keep the connection alive */ #define LOGINREQUEST 0x01 /* Sent by the client after the handshake to finish logging in */ #define HANDSHAKE 0x02 /* This is the first packet sent when the client connects and is used for Authentication. */ #define CHATMESSAGE 0x03 /* A message from the client to the server, or the server to the client */ #define TIMEUPDATE 0x04 /* Time is based on ticks, where 20 ticks happen every second. There are 24000 ticks in a day, making Minecraft days exactly 20 minutes long. The time of day is based on the timestamp modulo 24000. 0 is sunrise, 6000 is noon, 12000 is sunset, and 18000 is midnight. The default SMP server increments the time by 20 every second. */ #define DISCONNECT 0xFF /* Disconnection or Kick */ #define BUFSIZE 1024 /* Hex int of Minecraft's current protocol (currently 22) */ #define PROTOC_V 0x00000016 /* Java Char is 2bytes and Strings in packets are prefixed with a short equals strlen of the string */ typedef struct str_String16 { char* bytes; int size; }* String16; String16 toString16(char *msg) { int i; String16 res = malloc(sizeof(struct str_String16)); if (res != NULL) { res->size = strlen(msg)*2+2; res->bytes = calloc(res->size, sizeof(char)); res->bytes[1] = strlen(msg); /*FIXME cast 1int -> 2char */ for (i=0 ; i<strlen(msg) ; i++) { res->bytes[2*i+2] = 0; res->bytes[2*i+3] = msg[i]; } } return res; } char* toString(char* string16Bytes) { int i, size; char *res = NULL; size = string16Bytes[1]; if (size != 0) { res = malloc((size+1)*sizeof(char)); if (res != NULL) { for (i=0; i<size ; i++) { res[i] = string16Bytes[2*i+3]; } res[size] = 0; } } return res; } /* used to send an int */ void intToByteArray(unsigned int n, unsigned char *array) { /* int in mc are 4 bytes */ int i; for (i=3 ; i>=0 ; i--) { array[i] = n % 0x0100; n = n / 0x100; } } int mcclient(char *hostname, int portno, char *username) { int sockfd, n; struct sockaddr_in serveraddr; struct hostent *server; unsigned char buf[BUFSIZE]; /* Datas exchange */ char* msg; String16 string16; /* vars for the select routine */ fd_set rfds; struct timeval tv; int retval; /* Disables SIGPIPE */ struct sigaction act; act.sa_handler=SIG_IGN; sigemptyset(&act.sa_mask); act.sa_flags=0; sigaction(SIGPIPE, &act, NULL); /* (int) socket = socket(typeInternet, tcp, protocol=0) */ sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("ERROR opening socket"); return errno; } /* gethostbyname: get the server's DNS entry */ server = gethostbyname(hostname); if (server == NULL) { fprintf(stderr,"ERROR, no such host as %s\n", hostname); retval = -1; } else { /* build the server's Internet address */ bzero((char *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serveraddr.sin_addr.s_addr, server->h_length); serveraddr.sin_port = htons(portno); /* connect: create a connection with the server */ if (connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) { perror("ERROR connecting"); retval = errno; } else { printf("Connected to %s:%d\n", hostname, portno); /* sending handshake */ bzero(buf, BUFSIZE); string16 = toString16(username); buf[0] = HANDSHAKE; for (n=0 ; n<string16->size ; n++) { buf[n+1] = string16->bytes[n]; } /* send the message line to the server */ n = write(sockfd, buf, string16->size+1); free(string16); printf("Handshake send ...\n"); if (n < 0) { perror("ERROR writing to socket"); retval = errno; } else { FD_ZERO(&rfds); FD_SET(sockfd, &rfds); for(;;) { /* Wait up to 5 seconds. */ tv.tv_sec = 5; /* in the loop cuz select modify this var, use pselect instead ? */ tv.tv_usec = 0; retval = select(sockfd+1, &rfds, NULL, NULL, &tv); if (retval == -1) { perror("select()"); retval = errno; break; } else if (retval) { bzero(buf, BUFSIZE); /* print the server's reply */ n = read(sockfd, buf, BUFSIZE); if (n < 0) { perror("ERROR reading from socket"); retval = errno; break; } else { printf("packet rcv type 0x%.2x\n", buf[0]); if (buf[0] == KEEPALIVE) { bzero(buf, BUFSIZE); printf("\t rcv ping : sending Pong !\n"); n = write(sockfd, buf, 1); /* we must reply 0x00 to keep the connection alive */ if (n < 0) { perror("ERROR writing to socket"); retval = errno; break; } } else if (buf[0] == LOGINREQUEST) { /* Connection allowed ! \o/ TODO : do some cool stuff here */ } else if (buf[0] == HANDSHAKE) { msg = toString((char *)buf+1); /* reading the hash */ printf("\tserver reply hash : %s\n", msg); if (strlen(msg) == 1) { bzero(buf, BUFSIZE); buf[0] = LOGINREQUEST; if (msg[0] == '-') { printf("\nSending login request\n"); intToByteArray(PROTOC_V, buf+1); string16 = toString16(username); for (n=0 ; n<string16->size ; n++) { buf[n+5] = string16->bytes[n]; } n = write(sockfd, buf, string16->size+21); if (n < 0) { perror("ERROR writing to socket"); retval = errno; return; } } else if (msg[0] == '+') { /* TODO login with minecraft.net */ } } free (msg); } else if (buf[0] == DISCONNECT) { msg = toString((char *)buf+1); /* reading the reason */ printf("\tDisconnected, reason : %s\n", msg); free (msg); break; } else if (buf[0] == CHATMESSAGE) { msg = toString((char *)buf+1); printf("\t%s\n", msg); free (msg); } } } else { printf("Read Timeout\n"); break; } } } } } close(sockfd); return retval; } int main(){ return mcclient("mrhide.eu", 25565, "Morphix"); } </file>

misc/minecraft_client.txt · Dernière modification: 2011/12/21 15:56 par mrhide