diff --git a/03-network/client/src/defs.h b/03-network/client/src/defs.h new file mode 100644 index 0000000..8e52d45 --- /dev/null +++ b/03-network/client/src/defs.h @@ -0,0 +1,31 @@ +#ifndef DEFS_H +#define DEFS_H + +#include + +// Server details +#define SERVER_ADDR "127.0.0.1" +#define SERVER_PORT 9080 + +// SDL2 +#define SCREEN_WIDTH 1000 +#define SCREEN_HEIGHT 480 +#define WINDOW_FLAGS SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL +#define RENDERER_FLAGS SDL_RENDERER_ACCELERATED + +// MCS: Message Communication Standard +#define SERVER_MESSAGE_LEN 10 +#define CLIENT_MESSAGE_LEN 7 + +// MFS: Message Format Standard +#define PLAYER_CONNECT_FORMAT 0 +#define OBJECT_PROPERTIES_FORMAT 1 +#define PLAYER_DISCONNECT_FORMAT 2 + +// MSCA: Message Communication Standard Actions +#define LEFT_MOVEMENT 0b10000000 +#define RIGHT_MOVEMENT 0b01000000 +#define JUMP_MOVEMENT 0b00100000 +#define CROUCH_MOVEMENT 0b00010000 + +#endif diff --git a/03-network/client/src/main.c b/03-network/client/src/main.c index e12e36a..1dc2cf4 100644 --- a/03-network/client/src/main.c +++ b/03-network/client/src/main.c @@ -3,18 +3,11 @@ #include #include #include "structs.h" +#include "defs.h" #define DEBUG 1 -#define SERVER_ADDR "127.0.0.1" -#define SERVER_PORT 9080 -#define SERVER_MESSAGE_LEN 10 // 0-1 BYTE: Action details, 2-9: Other data (not sure we even need this) -#define CLIENT_MESSAGE_LEN 4 // 4 PROPERTIES (each of 4 BYTES) -const int SCREEN_WIDTH = 1000; -const int SCREEN_HEIGHT = 480; -const Uint32 WINDOW_FLAGS = SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL; -const Uint32 RENDERER_FLAGS = SDL_RENDERER_ACCELERATED; -int GAME_RUNNING = 1; +int game_running = 1; struct game game; int movement_speed = 10; double delta_time; @@ -26,6 +19,15 @@ TCPsocket server_socket; // scene struct object *player; +// textures +SDL_Texture *idle_animation; +SDL_Texture *jump_animation; +SDL_Texture *run_animation; + + +int objects_count = 0; +struct object ** objects_map; + void prepare_scene(void) { SDL_SetRenderDrawColor(game.renderer, 96, 128, 255, 255); SDL_RenderClear(game.renderer); @@ -49,7 +51,7 @@ void handle_input(void) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: - GAME_RUNNING = 0; + game_running = 0; break; case SDL_KEYDOWN: case SDL_KEYUP: @@ -58,9 +60,8 @@ void handle_input(void) { default: break; } - if (event.type == SDL_QUIT) { - GAME_RUNNING = 0; - } + if (event.type == SDL_QUIT) + game_running = 0; } } @@ -131,6 +132,25 @@ void present_scene(void) { SDL_RenderPresent(game.renderer); } +int new_key_position(void **map, int slots) { + int slot = 0; + + if (slots <= 0) + return slot; + + if (map == NULL) + return slot; + + while (slot <= slots) { + if (map[slots] == NULL) + break; + + slot++; + } + + return slot; +} + int handle_server(void *data) { for (;;) { int message[CLIENT_MESSAGE_LEN]; @@ -144,10 +164,80 @@ int handle_server(void *data) { if (DEBUG) fprintf(stdout, "Notice: received '%d' bytes from server\n", recv_len); - player->x = message[0]; - player->y = message[1]; - player->colliding = message[2]; - player->force = message[3]; + if (message[0] == PLAYER_CONNECT_FORMAT) { + if (DEBUG) + fprintf(stdout, "DEBUG: PLAYER CONNECT MESSAGE\n"); + + if (message[1] >= objects_count) { + objects_map = (struct object **) realloc(objects_map, sizeof(struct object *)*message[1]); + objects_count = message[1]+1; + } + if (objects_map == NULL) { + fprintf(stderr, "Error: failed reallocating objects hash map\n"); + return -1; + } + + struct object *new_object = create_object(idle_animation, 4, 48); + if (new_object == NULL) { + fprintf(stderr, "Error: failed allocating memory for new object\n"); + return -1; + } + + new_object->id = message[1]; + new_object->x = message[2]; + new_object->y = message[3]; + new_object->colliding = message[4]; + new_object->force = message[5]; + new_object->state = message[6]; + objects_map[message[1]] = new_object; + + continue; + } + + if (message[0] == OBJECT_PROPERTIES_FORMAT) { + if (DEBUG) + fprintf(stdout, "DEBUG: OBJECT PROPERTIES MESSAGE FOR ID %d\n", message[1]); + + if (message[1] >= objects_count) { + objects_map = (struct object **) realloc(objects_map, sizeof(struct object *)*message[1]); + objects_count = message[1]+1; + } + if (objects_map == NULL) { + fprintf(stderr, "Error: failed reallocating objects hash map\n"); + return -1; + } + + if (objects_map[message[1]] == NULL) { + struct object *new_object = create_object(idle_animation, 4, 48); + if (new_object == NULL) { + fprintf(stderr, "Error: failed allocating memory for new object\n"); + return -1; + } + + new_object->id = message[1]; + objects_map[message[1]] = new_object; + } + + objects_map[message[1]]->x = message[2]; + objects_map[message[1]]->y = message[3]; + objects_map[message[1]]->colliding = message[4]; + objects_map[message[1]]->force = message[5]; + + objects_map[message[1]]->state = message[6]; + continue; + } + + if (message[0] == PLAYER_DISCONNECT_FORMAT) { + if (DEBUG) + fprintf(stdout, "DEBUG: PLAYER DISCONNECT MESSAGE\n"); + + int object_id = message[1]; + + free(objects_map[object_id]); + objects_map[object_id] = NULL; + + continue; + } } return 0; @@ -189,7 +279,7 @@ int main(int argc, char *argv[]) { return -1; } - game.renderer = SDL_CreateRenderer(game.window, -1, RENDERER_FLAGS); + game.renderer = SDL_CreateRenderer(game.window, -1, RENDERER_FLAGS); if (game.renderer == NULL) { fprintf(stderr, "Error: could not create renderer\n%s\n", SDL_GetError()); @@ -197,17 +287,15 @@ int main(int argc, char *argv[]) { } // load player animations - SDL_Texture *idle_animation = load_texture("assets/player/idle.png"); - SDL_Texture *jump_animation = load_texture("assets/player/jump.png"); - SDL_Texture *run_animation = load_texture("assets/player/run.png"); - - player = create_object(idle_animation, 4, 48); + idle_animation = load_texture("assets/player/idle.png"); + jump_animation = load_texture("assets/player/jump.png"); + run_animation = load_texture("assets/player/run.png"); if (connect_to_server() != 0) return -1; // game loop - while (GAME_RUNNING) { + while (game_running) { double current_frame = SDL_GetTicks(); delta_time = current_frame - last_frame; last_frame = current_frame; @@ -215,43 +303,40 @@ int main(int argc, char *argv[]) { prepare_scene(); handle_input(); + uint8_t message[SERVER_MESSAGE_LEN]; + message[0] = (game.left << 7) | + (game.right << 6) | + (game.jump << 5); + + SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); + // The reason why I don't do // that much game development // in my free time. Damn youuu ifs - if (player->colliding) { - if (game.left) - switch_animation(player, run_animation); - if (game.right) - switch_animation(player, run_animation); - if (!game.left && !game.right) - switch_animation(player, idle_animation); - if (game.jump) { - switch_animation(player, jump_animation); + for (int iter = 0; iter < objects_count; iter++) { + struct object *obj = objects_map[iter]; + if (obj == NULL) + continue; - uint8_t message[SERVER_MESSAGE_LEN]; - message[0] = 2; - - SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); + if (obj->state & LEFT_MOVEMENT) { + switch_animation(obj, run_animation); + obj->flip = SDL_FLIP_HORIZONTAL; } + + if (obj->state & RIGHT_MOVEMENT) { + switch_animation(obj, run_animation); + obj->flip = SDL_FLIP_NONE; + } + + if (!(obj->state & LEFT_MOVEMENT) && !(obj->state & RIGHT_MOVEMENT)) + switch_animation(obj, idle_animation); + + if (obj->state & JUMP_MOVEMENT) + switch_animation(obj, jump_animation); + + draw_object(obj); } - if (game.left) { - player->flip = SDL_FLIP_HORIZONTAL; - uint8_t message[SERVER_MESSAGE_LEN]; - message[0] = 1; - - SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); - } - if (game.right) { - player->flip = SDL_FLIP_NONE; - uint8_t message[SERVER_MESSAGE_LEN]; - message[0] = 0; - - SDLNet_TCP_Send(server_socket, message, sizeof(uint8_t)*SERVER_MESSAGE_LEN); - } - - draw_object(player); - present_scene(); SDL_Delay(16); diff --git a/03-network/client/src/structs.h b/03-network/client/src/structs.h index dc0af64..9717a3e 100644 --- a/03-network/client/src/structs.h +++ b/03-network/client/src/structs.h @@ -23,6 +23,7 @@ struct object { SDL_Texture *texture; int resolution; // of the texture/every tile + int id; int x; int y; int scale; @@ -32,6 +33,8 @@ struct object { double animation_clock; // everytime it reaches 1, the texture switches to the next slide double animation_speed; // how fast will the clock reach 1 with respect to delta_time int animation_slide; // the current slide of the animation + + uint8_t state; int colliding; int force; diff --git a/03-network/server/src/defs.h b/03-network/server/src/defs.h index cce5681..ab1fcf9 100644 --- a/03-network/server/src/defs.h +++ b/03-network/server/src/defs.h @@ -1,17 +1,24 @@ #ifndef DEFS_H #define DEFS_H -#include +// DEBUG +#define GENERAL_DEBUG 0x0001 +#define PLAYER_DEBUG 0x0010 // MCS: Message Communication Standard #define SERVER_MESSAGE_LEN 10 -#define CLIENT_MESSAGE_LEN 4 +#define CLIENT_MESSAGE_LEN 7 + +// MFS: Message Format Standard +#define PLAYER_CONNECT_FORMAT 0 +#define OBJECT_PROPERTIES_FORMAT 1 +#define PLAYER_DISCONNECT_FORMAT 2 // MCSA: Message Communication Standard Actions -#define RIGHT_MOVEMENT 0 -#define LEFT_MOVEMENT 1 -#define JUMP_MOVEMENT 2 -#define CROUCH_MOVEMENT 3 +#define LEFT_MOVEMENT 0b10000000 +#define RIGHT_MOVEMENT 0b01000000 +#define JUMP_MOVEMENT 0b00100000 +#define CROUCH_MOVEMENT 0b00010000 // OC: Object Constants #define GRAVITY 2 diff --git a/03-network/server/src/main.c b/03-network/server/src/main.c index 7d84991..b0ceb71 100644 --- a/03-network/server/src/main.c +++ b/03-network/server/src/main.c @@ -7,18 +7,130 @@ #include "structs.h" #include "defs.h" +#define DEBUG 0x0010 #define PORT 9080 -#define DEBUG 1 int objects_count = 0; struct object **objects_map; +int connections_count = 0; +struct connection **connections_map; + +void handle_player_input(struct object *obj, char *message) { + uint8_t action = message[0]; + + if (action & RIGHT_MOVEMENT) + obj->x += MOVEMENT_SPEED; + + if (action & LEFT_MOVEMENT) + obj->x -= MOVEMENT_SPEED; + + if (action & JUMP_MOVEMENT) { + obj->force -= MOVEMENT_SPEED; + obj->colliding = 0; + } + + obj->state = action; +} + +void handle_player_physics(struct object *obj) { + if (obj->y < 200) + obj->force += GRAVITY; + else { + obj->colliding = 1; + obj->y = 200; + } + + obj->y += obj->force; +} + +void send_player_positions(TCPsocket socket, int player_id) { + if (objects_map == NULL) + return; + + for (int iter = 0; iter < objects_count; iter++) { + struct object *obj = objects_map[iter]; + if (obj == NULL) + continue; + + // Update the client with every object's information + // Note: the order OBVIOUSLY matters + int reply[] = { + OBJECT_PROPERTIES_FORMAT, + obj->id, + obj->x, + obj->y, + obj->colliding, + obj->force, + obj->state + }; + + if (DEBUG & PLAYER_DEBUG) + fprintf(stdout, "PLAYER_DEBUG: player '%d' properties (%d, %d, %d, %d)\n", + obj->id, obj->x, obj->y, obj->colliding, obj->force); + + SDLNet_TCP_Send(socket, reply, sizeof(int)*CLIENT_MESSAGE_LEN); + } +} + +int send_player_connect_event(int object_id) { + struct object *obj = objects_map[object_id]; + + for (int iter = 0; iter < connections_count; iter++) { + struct connection *con = connections_map[iter]; + if (con == NULL) + continue; + + int message[] = { + PLAYER_CONNECT_FORMAT, + obj->id, + obj->x, + obj->y, + obj->colliding, + obj->force, + obj->state + }; + + if (DEBUG & PLAYER_DEBUG) + fprintf(stdout, "PLAYER_DEBUG: new player '%d' has connected to the server\n", obj->id); + + SDLNet_TCP_Send(con->socket, message, sizeof(int)*CLIENT_MESSAGE_LEN); + } + + return 0; +} + +int send_player_disconnect_event(int object_id) { + struct object *obj = objects_map[object_id]; + + for (int iter = 0; iter < connections_count; iter++) { + struct connection *con = connections_map[iter]; + if (con == NULL) + continue; + + int message[] = { + PLAYER_DISCONNECT_FORMAT, + obj->id, + obj->x, + obj->y, + obj->colliding, + obj->force, + obj->state + }; + + if (DEBUG & PLAYER_DEBUG) + fprintf(stdout, "PLAYER_DEBUG: player '%d' has disconnected from the server\n", obj->id); + + SDLNet_TCP_Send(con->socket, message, sizeof(int)*CLIENT_MESSAGE_LEN); + } + + return 0; +} + int handle_player(void *data) { struct connection *connection_data = (struct connection *) data; struct object *obj = objects_map[connection_data->obj_id]; - memset(obj, 0, sizeof(struct object)); - SDLNet_SocketSet set = SDLNet_AllocSocketSet(1); if (set == NULL) { fprintf(stderr, "Error: cannot allocate memory for a new socket set\n"); @@ -31,10 +143,11 @@ int handle_player(void *data) { return -1; } + send_player_connect_event(connection_data->obj_id); + for (;;) { char message[SERVER_MESSAGE_LEN]; - - int ready_sockets = SDLNet_CheckSockets(set, 10); + int ready_sockets = SDLNet_CheckSockets(set, 100); if (ready_sockets == -1) { fprintf(stderr, "Error: cannot call select() system call with SDLNet_CheckSockets()\n"); continue; @@ -44,65 +157,29 @@ int handle_player(void *data) { goto update_client; int recv_len = SDLNet_TCP_Recv(connection_data->socket, message, SERVER_MESSAGE_LEN); - if (!recv_len) { // player disconnected fprintf(stderr, "Error: failed receiving message\n%s\n", SDLNet_GetError()); break; } - if (DEBUG) + if (DEBUG & GENERAL_DEBUG) fprintf(stdout, "Notice: received '%s' and '%d' bytes\n", message, recv_len); - // parsing the message - uint8_t action = message[0]; - - switch (action) { - case RIGHT_MOVEMENT: - obj->x += MOVEMENT_SPEED; - break; - case LEFT_MOVEMENT: - obj->x -= MOVEMENT_SPEED; - break; - case JUMP_MOVEMENT: - obj->force -= MOVEMENT_SPEED * 2.5; - obj->colliding = 0; - break; - case CROUCH_MOVEMENT: // TODO - break; - default: - break; - } + handle_player_input(obj, message); update_client: - if (obj->y < 200) { - obj->force += GRAVITY; - } else { - obj->colliding = 1; - // obj->force = 0; - obj->y = 200; - } - - // gravity - obj->y += obj->force; - - // Update the client with information - // Note: the order OBVIOUSLY matters - int reply[] = { - obj->x, - obj->y, - obj->colliding, - obj->force - }; - - if (DEBUG) - fprintf(stdout, "Notice: new player properties (%d, %d, %d, %d)\n", reply[0], reply[1], reply[2], reply[3]); - - SDLNet_TCP_Send(connection_data->socket, reply, sizeof(int)*CLIENT_MESSAGE_LEN); + handle_player_physics(obj); + send_player_positions(connection_data->socket, connection_data->obj_id); } + // Communicate client disconnect + send_player_disconnect_event(connection_data->obj_id); + SDLNet_FreeSocketSet(set); SDLNet_TCP_Close(connection_data->socket); + objects_map[connection_data->obj_id] = NULL; + connections_map[connection_data->id] = NULL; free(obj); free(data); @@ -114,6 +191,25 @@ void catch_alarm(int sig) { exit(EXIT_SUCCESS); } +int new_key_position(void **map, int slots) { + int slot = 0; + + if (slots <= 0) + return slot; + + if (map == NULL) + return slot; + + while (slot <= slots) { + if (map[slot] == NULL) + break; + + slot++; + } + + return slot; +} + int main(int argc, char *argv[]) { signal(SIGINT, catch_alarm); @@ -148,7 +244,11 @@ int main(int argc, char *argv[]) { fprintf(stdout, "Notice: accepted a connection from client!\n"); - objects_map = (struct object **) realloc(objects_map, sizeof(struct object *)*(objects_count+1)); + int new_object_position = new_key_position((void **) objects_map, objects_count); + if (new_object_position == objects_count) { + objects_map = (struct object **) realloc(objects_map, sizeof(struct object *)*objects_count); + objects_count++; + } if (objects_map == NULL) { fprintf(stderr, "Error: failed reallocating objects hash map\n"); return -1; @@ -160,7 +260,15 @@ int main(int argc, char *argv[]) { return -1; } - objects_map[objects_count] = new_object; + int new_connection_position = new_key_position((void **) connections_map, connections_count); + if (new_connection_position == connections_count) { + connections_map = (struct connection **) realloc(connections_map, sizeof(struct connection *)*connections_count); + connections_count++; + } + if (connections_map == NULL) { + fprintf(stderr, "Error: failed reallocating connections hash map\n"); + return -1; + } struct connection *new_connection = (struct connection *) calloc(1, sizeof(struct connection)); if (new_connection == NULL) { @@ -168,11 +276,15 @@ int main(int argc, char *argv[]) { return -1; } - new_object->id = objects_count; + objects_map[new_object_position] = new_object; + connections_map[new_connection_position] = new_connection; + new_object->id = new_object_position; + new_connection->id = new_connection_position; new_connection->socket = client; - new_connection->obj_id = objects_count; + new_connection->obj_id = new_object_position; - objects_count++; + if (DEBUG & PLAYER_DEBUG) + fprintf(stdout, "Notice: new player object id: %d\n", new_object_position); SDL_CreateThread(handle_player, "client", new_connection); } diff --git a/03-network/server/src/structs.h b/03-network/server/src/structs.h index e68dc55..aeb32d0 100644 --- a/03-network/server/src/structs.h +++ b/03-network/server/src/structs.h @@ -4,8 +4,9 @@ #include struct connection { - TCPsocket socket; + int id; int obj_id; + TCPsocket socket; }; struct object { @@ -15,6 +16,8 @@ struct object { int colliding; int force; + + uint8_t state; }; #endif