diff --git a/Makefile b/Makefile index c86c0d9..3d7abc1 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ CC = clang SA = scan-build -_LIB_FILES = marshal.c protocol.c bot.c +_LIB_FILES = marshal.c protocol.c bot.c client.c _TEST_FILES = packet_test.c protocol_test.c test_runner.c LIB_DIR = src TEST_DIR = test LIB_FILES= $(patsubst %,$(LIB_DIR)/%,$(_LIB_FILES)) TEST_FILES= $(patsubst %,$(TEST_DIR)/%,$(_TEST_FILES)) -CFLAGS=-Wall --std=gnu99 -Wfatal-errors +CFLAGS=-Wall --std=gnu99 -Wfatal-errors -lpthread -g tests: bin $(CC) -o bin/tests $(LIB_FILES) $(TEST_FILES) -I $(LIB_DIR) $(CFLAGS) ./bin/tests diff --git a/src/bot.c b/src/bot.c index 81b9d46..fabfe0b 100644 --- a/src/bot.c +++ b/src/bot.c @@ -19,15 +19,16 @@ bot_t *init_bot(char *name, void (*bot_main)(void *)){ // set the bot name bot_t *bot = calloc(1, sizeof(bot_t)); bot->packet_threshold = DEFAULT_THRESHOLD; + bot->buf = calloc(1, DEFAULT_THRESHOLD); size_t len = strlen(name); bot->name = calloc(len + 1, sizeof(char)); strncpy(bot->name, name, len + 1); bot->bot_main = bot_main; // initialize the callback data structure - bot->callbacks = calloc(NUM_STATES, sizeof(function **)); - bot->callbacks[HANDSHAKE] = calloc(HANDSHAKE_PACKETS, sizeof(function *)); - bot->callbacks[LOGIN] = calloc(LOGIN_PACKETS, sizeof(function *)); - bot->callbacks[PLAY] = calloc(PLAY_PACKETS, sizeof(function *)); + bot->callbacks = calloc(NUM_STATES, sizeof(function *)); + bot->callbacks[HANDSHAKE] = calloc(HANDSHAKE_PACKETS, sizeof(function)); + bot->callbacks[LOGIN] = calloc(LOGIN_PACKETS, sizeof(function)); + bot->callbacks[PLAY] = calloc(PLAY_PACKETS, sizeof(function)); return bot; } @@ -37,15 +38,15 @@ void free_bot(bot_t *bot){ // unrolled outer loop just cuz int i; for(i = 0; i < HANDSHAKE_PACKETS; i++){ - function *func = bot->callbacks[HANDSHAKE][i]; + function *func = &bot->callbacks[HANDSHAKE][i]; free_list(func); } for(i = 0; i < LOGIN_PACKETS; i++){ - function *func = bot->callbacks[LOGIN][i]; + function *func = &bot->callbacks[LOGIN][i]; free_list(func); } for(i = 0; i < PLAY_PACKETS; i++){ - function *func = bot->callbacks[PLAY][i]; + function *func = &bot->callbacks[PLAY][i]; free_list(func); } free(bot); @@ -59,12 +60,13 @@ void free_list(function *list){ void register_event(bot_t *bot, uint32_t state, uint32_t packet_id, - void (*f)(void *)){ - function *current = bot->callbacks[state][packet_id]; - while(current) - current = current->next; - current = calloc(1, sizeof(function)); - current->f = f; + void (*f)(bot_t *, void *)){ + function *parent = &bot->callbacks[state][packet_id]; + while(parent->next) + parent = parent->next; + function *child = calloc(1, sizeof(function)); + parent->f = f; + parent->next = child; } // initializes a bot structure with a socket. The socket is bound to the local address on @@ -72,37 +74,19 @@ void register_event(bot_t *bot, uint32_t state, uint32_t packet_id, // the socket descriptor is returned by the function. If -1 is returned, then an error // occured, and a message will have been printed out. -int join_server(bot_t *your_bot, char *local_port, char* server_host, - char* server_port){ - int status; +int join_server(bot_t *your_bot, char* server_host, char* server_port){ struct addrinfo hints, *res; int sockfd; - memset(&hints, 0, sizeof(hints)); + // first, load up address structs with getaddrinfo(): + memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - if((status = getaddrinfo(NULL, local_port, &hints, &res))){ - fprintf(stderr, "Your computer is literally haunted: %s\n", - gai_strerror(status)); - return -1; - } - if((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){ - fprintf(stderr, "Could not create socket for unknown reason.\n"); - return -1; - } - freeaddrinfo(res); - // socket bound to local address/port + getaddrinfo(server_host, server_port, &hints, &res); - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - if((status = getaddrinfo(server_host, server_port, &hints, &res))){ - fprintf(stderr, "Server could not be resolved: %s\n", - gai_strerror(status)); - return -1; - } + // make a socket and connect + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); connect(sockfd, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - // connected to server + your_bot->socketfd = sockfd; return sockfd; } @@ -148,7 +132,7 @@ int receive_packet(bot_t *bot) { assert(i != len); packet_size += len; - received = len + 1; + received = i + 1; if (packet_size <= bot->packet_threshold) { while (received < packet_size) { ret = receive_raw(bot, bot->buf + received, packet_size - received); @@ -160,13 +144,17 @@ int receive_packet(bot_t *bot) { ret = peek_packet(bot, bot->buf); return ret; } else { - // read in a huge buffer, but throw it away - while (received < packet_size) { + // read in a huge buffer, packet_threshold at a time + while (received < packet_size - bot->packet_threshold) { ret = receive_raw(bot, bot->buf, bot->packet_threshold); if (ret <= 0) return -1; received += ret; } + // read the last portion of the packet + ret = receive_raw(bot, bot->buf, packet_size - received); + if (ret <= 0) + return -1; return -2; } } diff --git a/src/bot.h b/src/bot.h index b6218ba..18699d8 100644 --- a/src/bot.h +++ b/src/bot.h @@ -6,12 +6,10 @@ typedef enum {HANDSHAKE, LOGIN, STATUS, PLAY, NUM_STATES} state; -typedef struct _function { - void (*f)(void *); - struct _function *next; -} function; +typedef struct bot bot_t; +typedef struct _function function; -typedef struct bot { +struct bot { int socketfd; size_t packet_threshold; char *buf; @@ -19,8 +17,13 @@ typedef struct bot { state current_state; /* registered callbacks */ void (*bot_main)(void *); - function ***callbacks; // triple indirection hooray! -} bot_t; + function **callbacks; +}; + +struct _function { + void (*f)(bot_t *, void *); + struct _function *next; +}; extern struct bot context; @@ -47,15 +50,13 @@ void free_bot(bot_t *); * is recieved. * Note: Currently there is no way to "un-register" a callback. */ -void register_event(bot_t *bot, uint32_t state, uint32_t packet_id, void (*f)(void *)); +void register_event(bot_t *bot, uint32_t state, uint32_t packet_id, void (*f)(bot_t *, void *)); /** \brief Open a socket to the specified server * - * Open a socket connection to a specific server for a particular bot. It is - * possible, but not useful, to choose the local bind port. A local_port value - * of NULL should choose a random, open port. + * Open a socket connection to a specific server for a particular bot. */ -int join_server(bot_t *bot, char *local_port, char* server_host, char* server_port); +int join_server(bot_t *bot, char* server_host, char* server_port); /** \brief Sends a string across the network using a bot's socket * diff --git a/src/client.c b/src/client.c index d85c759..0004a60 100644 --- a/src/client.c +++ b/src/client.c @@ -31,29 +31,41 @@ void *bot_thread(void *bot); void client_run(bot_t *bots, uint32_t num) { // create 1 thread for receiving packets, and 1 for each bot int i; - bot_list = bots; num_bots = num; + bot_list = bots; pthread_key_create(&bot_key, NULL); - + // create & start listener thread pthread_t event_listener; pthread_create(&event_listener, NULL, receiver, NULL); // create all the bot threads + bot_threads = calloc(num_bots, sizeof(pthread_t)); for(i = 0; i < num; i++) { - pthread_create(bot_threads + i, NULL, bot_thread, bot_list + i); + pthread_create(bot_threads + i, NULL, bot_thread, bot_list + i); } - + // wait for all threads to finish // TODO: support for exit codes pthread_join(event_listener, NULL); for(i = 0; i < num; i++) { pthread_join(bot_threads[i], NULL); } + free(bot_threads); } void *bot_thread(void *bot) { pthread_setspecific(bot_key, bot); + + struct sigaction sa; + sa.sa_handler = signal_handler; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGUSR1, &sa, NULL) == -1) { + perror("sigaction"); + exit(1); + } + ((bot_t *)bot)->bot_main(bot); return NULL; } diff --git a/src/client.h b/src/client.h index 1eae61b..7fe6f52 100644 --- a/src/client.h +++ b/src/client.h @@ -1,11 +1,4 @@ #include "bot.h" -#include -#include -#include -#include -#include "protocol.h" -#include -#include void client_run(bot_t *, uint32_t); diff --git a/src/marshal.c b/src/marshal.c index 3a5a997..12235f7 100644 --- a/src/marshal.c +++ b/src/marshal.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "marshal.h" #include "bot.h" #include "protocol.h" @@ -181,7 +182,6 @@ int format_packet(bot_t *bot, void *packet_data, void **packet_raw_ptr){ break; default: ; - size_t size = format_sizeof(*fmt); if(index + size > len) return -1; // TODO: compression memcpy(packet_raw + index, packet_data, size); @@ -208,10 +208,11 @@ int decode_packet(bot_t *bot, void *packet_raw, void *packet_data){ // packet_data = struct containing packet data uint32_t len; vint32_t value; - uint32_t arr_len; + uint32_t arr_len = -1; size_t size; char *fmt = *((char **)packet_data); + assert(fmt != 0); packet_data += sizeof(void *); int32_t packet_size; @@ -239,6 +240,10 @@ int decode_packet(bot_t *bot, void *packet_raw, void *packet_data){ case '*': fmt++; size_t size_elem = format_sizeof(*fmt); + assert(arr_len != -1); + if(arr_len != 0) { + break; + } void *arr = calloc(arr_len, size_elem); for(int i = 0; i < arr_len * size_elem; i += size_elem){ memcpy(arr + i, packet_raw + i, size_elem); diff --git a/src/protocol.c b/src/protocol.c index 74b50e7..c73e069 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -1,4 +1,4 @@ - +#include #include "protocol.h" #include "marshal.h" #include "bot.h" @@ -16,8 +16,8 @@ // Macro to fill structs in the callback switch #define _render_callback(NAME) { \ recv_struct = recv_ ## NAME(bot); \ - while(func) { \ - ((void (*)(NAME ## _t *))func->f)((NAME ## _t *) recv_struct); \ + while(func->next) { \ + (func->f)(bot, recv_struct); \ func = func->next; \ } \ break; \ @@ -629,7 +629,6 @@ _render_recv(play_clientbound_chunk_bulk, "vbvwwh*b", 0x26); _render_recv(play_clientbound_explosion, "vwwwww*wwww", 0x27); _render_recv(play_clientbound_effect, "vwlwb", 0x28); _render_recv(play_clientbound_sound_effect, "vswwwwb", 0x29); -_render_recv(play_clientbound_particle, "vvbwwwwwwww*v", 0x2A); _render_recv(play_clientbound_entity_spawn_global, "vvbwww", 0x2C); _render_recv(play_clientbound_update_sign, "vlssss", 0x33); _render_recv(play_clientbound_plugin_message, "vs*b", 0x3F); @@ -638,8 +637,12 @@ _render_recv(play_clientbound_plugin_difficulty, "vb", 0x41); _render_recv(play_clientbound_set_compression, "vv", 0x46); void callback_decode(bot_t *bot) { - uint32_t pid = receive_packet(bot); - function *func = bot->callbacks[bot->current_state][pid]; + int32_t pid = receive_packet(bot); + if (pid < 0) { + if (pid == -1) exit(123); + return; + } + function *func = &bot->callbacks[bot->current_state][pid]; void *recv_struct; switch (bot->current_state) { case HANDSHAKE: @@ -702,7 +705,6 @@ void callback_decode(bot_t *bot) { case 0x27: _render_callback(play_clientbound_explosion); case 0x28: _render_callback(play_clientbound_effect); case 0x29: _render_callback(play_clientbound_sound_effect); - case 0x2A: _render_callback(play_clientbound_particle); case 0x2C: _render_callback(play_clientbound_entity_spawn_global); case 0x33: _render_callback(play_clientbound_update_sign); case 0x3F: _render_callback(play_clientbound_plugin_message); diff --git a/src/protocol.h b/src/protocol.h index 06b0522..5f04283 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -531,23 +531,6 @@ typedef struct play_clientbound_sound_effect { uint8_t pitch; } play_clientbound_sound_effect_t; -typedef struct play_clientbound_particle { - char* format; - vint32_t packet_id; - - vint32_t particle_id; - bool long_distance; - float x; - float y; - float z; - float dx; - float dy; - float dz; - float particle_data; - int32_t count; - vint32_t* data; -} play_clientbound_particle_t; - typedef struct play_clientbound_entity_spawn_global { char* format; vint32_t packet_id; @@ -1096,9 +1079,6 @@ recv_play_clientbound_effect(bot_t* bot); play_clientbound_sound_effect_t* recv_play_clientbound_sound_effect(bot_t* bot); -play_clientbound_particle_t* -recv_play_clientbound_particle(bot_t* bot); - play_clientbound_entity_spawn_global_t* recv_play_clientbound_entity_spawn_global(bot_t* bot); diff --git a/src/sample_bot.c b/src/sample_bot.c index fa27975..dbb4889 100644 --- a/src/sample_bot.c +++ b/src/sample_bot.c @@ -1,19 +1,61 @@ #include #include #include +#include #include +#include +#include #include "bot.h" #include "client.h" +#include "protocol.h" -void sample_main(void* dummy) { - struct timespec req = {0, 50000000}; +void sample_login_handler(bot_t *bot, void *vp) { + login_clientbound_success_t *p = (login_clientbound_success_t *)vp; + printf("Logged in as: %s\n", p->username); + bot->current_state = PLAY; +} + +void sample_keepalive_handler(bot_t *bot, void *vp) { + play_clientbound_keepalive_t *p = (play_clientbound_keepalive_t *)vp; + printf("keepalive_id: %d\n", p->keepalive_id); + send_play_serverbound_keepalive(bot, p->keepalive_id); +} + +void time_update_checker(bot_t *bot, void *vp) { + play_clientbound_time_update_t *p = (play_clientbound_time_update_t *)vp; + printf("Game time: %lu\n", p->time); +} + +void sample_main(void* vbot) { + bot_t *bot = (bot_t *)vbot; + struct timespec req = {0, 5000000}; struct timespec rem; + + send_handshaking_serverbound_handshake(bot, 47, "localhost", 25565, 2); + bot->current_state = LOGIN; + send_login_serverbound_login(bot, bot->name); + while(1) { + //I'm on the ground, trust me! + //send_play_serverbound_player(bot, true); nanosleep(&req, &rem); } } int main() { - /* bot_t *batman_bot = init_bot("batman", *sample_main); */ - /* client_run(batman_bot, 1); */ + bot_t *bot = init_bot("botrbert", *sample_main); + + register_event(bot, PLAY, 0x00, sample_keepalive_handler); + register_event(bot, LOGIN, 0x02, sample_login_handler); + register_event(bot, PLAY, 0x03, time_update_checker); + + join_server(bot, "localhost", "25565"); + + bot_t* test_bots = calloc(1, sizeof(bot_t)); + test_bots[0] = *bot; + + client_run(test_bots, 1); + + free_bot(bot); + return 0; }