#include #include #include #include #include #include #include #include #include #include #include "scufeng/log.h" #include "networking.h" #include "MultiPlayer.h" #include "game.h" #include "game_common.h" #include "GameState_net.h" static bool handle_game_state_msg(MultiPlayer *const mp) { GameStateMsg msg; if (!recv_msg(mp->conn_s, NET_MSG_GAME_STATE, (uint8_t *) &msg)) { return false; } GameStateMsg_to_GameState(&msg, &mp->state); return true; } bool MultiPlayer_alloc( MultiPlayer *const mp, const unsigned int screen_w, const unsigned int screen_h, const char *server_host, const uint16_t server_port ) { struct protoent *proto = getprotobyname("tcp"); mp->sock = socket(AF_INET, SOCK_STREAM, proto->p_proto); if (mp->sock == -1) { SE_LOG_FATALF("socket failed: %d %s", errno, strerror(errno)); } struct in_addr server_addr; int res = inet_aton(server_host, &server_addr); if (res == 0) { close(mp->sock); SE_LOG_FATAL("inet_aton failed"); } struct sockaddr_in server_sockaddr = { .sin_family = AF_INET, .sin_addr = server_addr, .sin_port = htons(server_port) }; res = connect( mp->sock, (struct sockaddr *) &server_sockaddr, sizeof(server_sockaddr) ); if (res == -1) { close(mp->sock); SE_LOG_ERRORF("connect failed: %d %s", errno, strerror(errno)); return false; } SE_LOG("Connected to server"); mp->conn_s = fdopen(mp->sock, "r+"); if (mp->conn_s == nullptr) { close(mp->sock); SE_LOG_ERRORF("fdopen failed: (%d) %s", errno, strerror(errno)); return false; } NetMsgType msg_type; if (recv_msg_type(mp->conn_s, &msg_type) == ECONNRESET) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERROR("Connection reset"); return false; } if (msg_type != NET_MSG_IS_PLAYER_1) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERRORF("Did not get IS_PLAYER_1 msg, got: %u", msg_type); return false; } IsPlayer1Msg p1_msg = { 0 }; if (!recv_msg(mp->conn_s, NET_MSG_IS_PLAYER_1, (uint8_t *) &p1_msg)) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERROR("Connection reset"); return false; } mp->is_player_1 = (bool) p1_msg.player1; if (mp->is_player_1) { SE_LOG("I am player 1"); } else { SE_LOG("I am player 2"); } SE_LOG("Waiting for game start"); mp->state = GameState_new(game_default_params, false); if (recv_msg_type(mp->conn_s, &msg_type) == ECONNRESET) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERROR("Connection reset"); return false; } if (msg_type != NET_MSG_GAME_STATE) { fclose(mp->conn_s); close(mp->sock); SE_LOG_FATALF("[MultiPlayer_alloc] Did not get GAME_STATE msg, got: %u", msg_type); } if (!handle_game_state_msg(mp)) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERROR("Connection reset"); return false; } if (recv_msg_type(mp->conn_s, &msg_type) == ECONNRESET) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERROR("Connection reset"); return false; } if (msg_type != NET_MSG_GAME_START) { fclose(mp->conn_s); close(mp->sock); SE_LOG_FATALF("[MultiPlayer_alloc] Did not get GAME_START msg, got: %u", msg_type); } if (!send_msg(mp->conn_s, NET_MSG_GAME_START, nullptr)) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERROR("Connection reset"); return false; } if (fcntl(mp->sock, F_SETFL, O_NONBLOCK) != 0) { fclose(mp->conn_s); close(mp->sock); SE_LOG_ERRORF("fcntl failed on mp->sock: (%d) %s", errno, strerror(errno)); return false; } SE_LOG("Game started"); mp->screen_w = screen_w; mp->screen_h = screen_h; mp->shapes = GameShapes_alloc(screen_w, screen_h, game_default_params); return mp; } void MultiPlayer_free(MultiPlayer *const mp) { fclose(mp->conn_s); mp->conn_s = nullptr; // close(mp->conn); // mp->conn = 0; close(mp->sock); mp->sock = 0; GameShapes_free(&mp->shapes); } static void handle_paddle_pos_msg(MultiPlayer *const mp) { PaddleUpdateMsg msg; recv_msg(mp->conn_s, NET_MSG_PADDLE_POS, (uint8_t *) &msg); mp->state.paddle_l.pos[1] = msg.l_pos_y; mp->state.paddle_l.dirv[1] = msg.l_dirv_y; mp->state.paddle_r.pos[1] = msg.r_pos_y; mp->state.paddle_r.dirv[1] = msg.r_dirv_y; } static void handle_ball_update_msg(MultiPlayer *const mp) { BallUpdateMsg msg; recv_msg(mp->conn_s, NET_MSG_BALL_UPDATE, (uint8_t *) &msg); mp->state.ball.pos[0] = msg.ball_pos_x; mp->state.ball.pos[1] = msg.ball_pos_y; mp->state.ball.dirv[0] = msg.ball_dirv_x; mp->state.ball.dirv[1] = msg.ball_dirv_y; } static void handle_score_update_msg(MultiPlayer *const mp) { ScoreUpdateMsg msg; recv_msg(mp->conn_s, NET_MSG_SCORE_UPDATE, (uint8_t *) &msg); mp->state.score_l = msg.l_score; mp->state.score_r = msg.r_score; SE_LOGF("score_l: %d score_r: %d", mp->state.score_l, mp->state.score_r); } static void handle_input( const MultiPlayer *const mp, bool *dir_changed, SEDirection *new_dir, bool *quit ) { if (se_get_key(SE_KEY_ESCAPE) == SE_KEY_ACTION_PRESS) { *quit = true; return; } *new_dir = SE_NONE; *dir_changed = false; const SEDirection current_dir = GameState_paddle_l_dir_get(&mp->state); if (se_get_key(SE_KEY_W) == SE_KEY_ACTION_PRESS) { if (current_dir != SE_UP) { *new_dir = SE_UP; *dir_changed = true; } } else if (se_get_key(SE_KEY_S) == SE_KEY_ACTION_PRESS) { if (current_dir != SE_DOWN) { *new_dir = SE_DOWN; *dir_changed = true; } } else if (current_dir != SE_NONE) { *new_dir = SE_NONE; *dir_changed = true; } } Screen MultiPlayer_tick(MultiPlayer *const mp, const double delta) { NetMsgType msg_type; while (true) { const int res = recv_msg_type(mp->conn_s, &msg_type); if (res == 0) { switch (msg_type) { case NET_MSG_PADDLE_CHANGE_DIR: break; case NET_MSG_PADDLE_POS: handle_paddle_pos_msg(mp); break; case NET_MSG_GAME_STATE: handle_game_state_msg(mp); break; case NET_MSG_IS_PLAYER_1: case NET_MSG_GAME_START: break; case NET_MSG_BALL_UPDATE: handle_ball_update_msg(mp); break; case NET_MSG_SCORE_UPDATE: handle_score_update_msg(mp); break; } break; } else if (res == ECONNRESET) { SE_LOG_ERROR("Connection reset"); return PONG_MAIN_MENU; } else if (res == EWOULDBLOCK) { break; } else { SE_LOG_FATALF("[MultiPlayer_tick] Unhandled return value: %d", res); } // SE_LOGF("Got message: %s", NetMsgType_to_str(msg_type)); } bool dir_changed = false; SEDirection new_dir; bool quit = false; handle_input(mp, &dir_changed, &new_dir, &quit); if (quit) { return PONG_MAIN_MENU; } if (dir_changed) { GameState_paddle_l_change_dir(&mp->state, new_dir); const PaddleChangeDirMsg msg = { .dir = (uint8_t) new_dir }; if (!send_msg(mp->conn_s, NET_MSG_PADDLE_CHANGE_DIR, (uint8_t *) &msg)) { SE_LOG_ERROR("Connection reset"); return PONG_MAIN_MENU; } } GameState_update(&mp->state, delta); GameShapes_update(&mp->shapes, &mp->state, mp->screen_w, mp->screen_h); GameShapes_render(&mp->shapes, &mp->state, mp->screen_w, mp->screen_h); return PONG_MULTIPLAYER; }