213 lines
6.9 KiB
C
213 lines
6.9 KiB
C
#include "game_session.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
} |