#include "game_session.h" #include #include #include #include #include #include "shared/squeue.h" #define INPUT_QUEUE_CAPACITY 8 Game_Session *g_current_session = NULL; void game_session_create(bool is_singleplayer, bool is_host, Game_Session_Settings settings) { assert(g_current_session == NULL); Game_Session *session = (Game_Session *)calloc(1, sizeof(Game_Session)); session->is_singleplayer = is_singleplayer; session->is_host = is_host; session->settings = settings; session->simulation_world = simulation_create_world( session->settings.seed, session->settings.max_players, session->settings.level_width, session->settings.level_height ); session->players = (Game_Session_Player *)calloc(session->settings.max_players, sizeof(Game_Session_Player)); session->players_prev = (Game_Session_Player *)calloc(session->settings.max_players, sizeof(Game_Session_Player)); for(uint16_t i = 0; i < session->settings.max_players; i++) { assert(squeue_init( &session->players[i].input_queue, INPUT_QUEUE_CAPACITY, sizeof(Simulation_Game_Input) )); // Intentionally not initializing players_prev's input_queue. } { // TEMP session->local_player_index = 0; assert(game_session_create_player(session, &session->local_player_index)); } session->simulation_accumulator = 0.0f; g_current_session = session; printf("New Game_Session created.\n"); } void game_session_destroy() { Game_Session *session = g_current_session; if(session == NULL) { return; } simulation_destroy_world(&session->simulation_world); for(uint16_t i = 0; i < session->settings.max_players; i++) { squeue_free(&session->players[i].input_queue); // Intentionally not freeing players_prev's input_queue. } free(session->players); session->players = NULL; free(session->players_prev); session->players_prev = NULL; free(session); g_current_session = NULL; } void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings) { out_settings->seed = 1337; // TODO: SS - Randomize. out_settings->tickrate = 10.0; out_settings->level_width = 32; out_settings->level_height = 32; out_settings->max_players = is_singleplayer ? 1 : 8; } static void game_session_tick(Game_Session *session) { // Update input. for(uint16_t i = 0; i < session->settings.max_players; i++) { Game_Session_Player *session_player = &session->players[i]; { // Check if any players have joined/disconnected by comparing against the previous session_player. Game_Session_Player *session_player_prev = &session->players_prev[i]; bool was_active = session_player_prev->active; bool is_active = session_player->active; if (!was_active && is_active) { simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) { .type = Simulation_Command_Type_Add_Player, .player_id = i }); } else if (was_active && !is_active) { simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) { .type = Simulation_Command_Type_Remove_Player, .player_id = i }); } } if(!session_player->active) { continue; } Simulation_Game_Input input = {0}; if(squeue_pop(&session_player->input_queue, &input)) { // We got an input. simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) { .type = Simulation_Command_Type_Set_Player_Input, .player_id = i, .player_input = input, }); } } // Tick. simulation_world_tick(&session->simulation_world); // Copy 'players' from session to 'players_prev'. memcpy(session->players_prev, session->players, sizeof(Game_Session_Player) * session->settings.max_players); } void game_session_update(Game_Session *session, const double delta_time) { session->simulation_accumulator += delta_time; const double sim_dt = 1.0 / session->settings.tickrate; while (session->simulation_accumulator >= sim_dt) { game_session_tick(session); session->simulation_accumulator -= sim_dt; } } static Game_Session_Player *game_session_get_player_from_id(Game_Session *session, uint16_t player_index) { assert(session != NULL); if(player_index >= session->settings.max_players) { return NULL; } return &session->players[player_index]; } Game_Session_Player *game_session_get_local_player(Game_Session *session) { assert(session != NULL); return game_session_get_player_from_id(session, session->local_player_index); } bool game_session_create_player(Game_Session *session, uint16_t *out_player_index) { assert(session != NULL); int32_t found_index = -1; for(uint16_t i = 0; i < session->settings.max_players; i++) { Game_Session_Player *session_player = &session->players[i]; assert(session_player != NULL); if(!session_player->active) { found_index = i; session_player->active = true; break; } } if(found_index < 0) { return false; } *out_player_index = (uint16_t)found_index; return true; } void game_session_destroy_player(Game_Session *session, uint16_t player_index) { assert(session != NULL); assert(player_index < session->settings.max_players); Game_Session_Player *player = &session->players[player_index]; assert(player != NULL); assert(player->active); memset(player, 0, sizeof(Game_Session_Player)); } bool game_session_enqueue_player_input(Game_Session *session, uint16_t player_index, Simulation_Game_Input input) { Game_Session_Player *player = game_session_get_player_from_id(session, player_index); assert(player != NULL); if(simulation_input_equal(player->most_recent_input, input)) { // Ignore 'input' if it's equal to the most recent input. This way we avoid duplicates. return false; } // TODO: SS - Check if 'input' is a valid "snake-move". // Inputting right when going left will sadly be "okay" here which will make the game // feel less responsive. if(!squeue_push(&player->input_queue, (void *)&input)) { return false; } player->most_recent_input = input; return true; }