Slight reconstruction/abstraction. Getting close to connecting to other sessions etc.
This commit is contained in:
87
src/instance/game_instance.c
Normal file
87
src/instance/game_instance.c
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#include "game_instance.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define INPUT_QUEUE_CAPACITY 8
|
||||||
|
|
||||||
|
void game_instance_init(Game_Instance *instance) {
|
||||||
|
instance->local_player_index = 0xFFFF;
|
||||||
|
// TODO: SS - Init input-queue.
|
||||||
|
instance->simulation_accumulator = 0.0f; // TODO: SS - Should probably be moved to the Ingame context.
|
||||||
|
instance->game_session = NULL;
|
||||||
|
memset(&instance->game_networking, 0, sizeof(Game_Networking));
|
||||||
|
|
||||||
|
squeue_init(&instance->local_input_queue, 4, sizeof(Simulation_Game_Input));
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_instance_dispose(Game_Instance *instance) {
|
||||||
|
squeue_free(&instance->local_input_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_instance_host_session(Game_Instance *instance, const Game_Session_Settings settings) {
|
||||||
|
assert(instance != NULL);
|
||||||
|
|
||||||
|
printf("Trying to host an %s session ...\n", settings.online ? "online" : "offline");
|
||||||
|
|
||||||
|
if(instance->game_session != NULL) {
|
||||||
|
printf("Failed. A session is already active.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance->game_session = (Game_Session *)calloc(1, sizeof(Game_Session));
|
||||||
|
|
||||||
|
if(settings.online) {
|
||||||
|
if(instance->game_networking.api != NULL) {
|
||||||
|
printf("Failed. Network is already active.\n");
|
||||||
|
free(instance->game_session);
|
||||||
|
instance->game_session = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!networking_try_host(&instance->game_networking, instance->game_session, settings)) {
|
||||||
|
printf("Failed to host session.\n");
|
||||||
|
free(instance->game_session);
|
||||||
|
instance->game_session = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
game_session_init(instance->game_session, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(game_session_create_player(instance->game_session, &instance->local_player_index)); // Create the host (you!).
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_instance_join_session(Game_Instance *instance, const char *session_id) {
|
||||||
|
assert(instance != NULL);
|
||||||
|
|
||||||
|
printf("Trying to join a session ...\n");
|
||||||
|
|
||||||
|
if(instance->game_session != NULL) {
|
||||||
|
printf("Failed. A session is already active.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(instance->game_networking.api != NULL) {
|
||||||
|
printf("Failed. Network is already active.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_instance_push_local_input(Game_Instance *instance, Simulation_Game_Input input) {
|
||||||
|
return squeue_push(&instance->local_input_queue, (void *)&input);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game_instance_pop_local_input(Game_Instance *instance, Simulation_Game_Input *out_input) {
|
||||||
|
Simulation_Game_Input input = {0};
|
||||||
|
if(!squeue_pop(&instance->local_input_queue, (void *)&input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_input = input;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
32
src/instance/game_instance.h
Normal file
32
src/instance/game_instance.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef GAME_INSTANCE_H
|
||||||
|
#define GAME_INSTANCE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "session/networking.h"
|
||||||
|
#include "session/game_session.h"
|
||||||
|
|
||||||
|
#include "shared/squeue.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t local_player_index;
|
||||||
|
SQueue local_input_queue;
|
||||||
|
|
||||||
|
double simulation_accumulator;
|
||||||
|
|
||||||
|
Game_Networking game_networking;
|
||||||
|
Game_Session *game_session;
|
||||||
|
} Game_Instance;
|
||||||
|
|
||||||
|
void game_instance_init(Game_Instance *instance);
|
||||||
|
void game_instance_dispose(Game_Instance *instance);
|
||||||
|
|
||||||
|
bool game_instance_host_session(Game_Instance *instance, const Game_Session_Settings settings);
|
||||||
|
bool game_instance_join_session(Game_Instance *instance, const char *session_id);
|
||||||
|
|
||||||
|
// TODO: SS - stop_session()
|
||||||
|
|
||||||
|
bool game_instance_push_local_input(Game_Instance *instance, Simulation_Game_Input input);
|
||||||
|
bool game_instance_pop_local_input(Game_Instance *instance, Simulation_Game_Input *out_input);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -205,7 +205,7 @@ static int compare_keys(const void *key1, const void *key2)
|
|||||||
|
|
||||||
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
|
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
|
||||||
{
|
{
|
||||||
snprintf(key, key_size, "%p", json);
|
snprintf(key, key_size, "%p", (void *)json);
|
||||||
if (hashtable_get(parents, key))
|
if (hashtable_get(parents, key))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|||||||
21
src/main.c
21
src/main.c
@@ -6,6 +6,9 @@
|
|||||||
#include "presentation/states/state_main_menu.h"
|
#include "presentation/states/state_main_menu.h"
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
#include "session/networking.h"
|
||||||
|
|
||||||
|
#include "instance/game_instance.h"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
InitWindow(1280, 720, "snejk");
|
InitWindow(1280, 720, "snejk");
|
||||||
@@ -14,10 +17,18 @@ int main() {
|
|||||||
|
|
||||||
bool should_quit_game = false;
|
bool should_quit_game = false;
|
||||||
|
|
||||||
Presentation_State_Ingame_Context presentation_state_ingame_ctx = (Presentation_State_Ingame_Context) {
|
Game_Instance local_game_instance;
|
||||||
};
|
memset(&local_game_instance, 0, sizeof(Game_Instance));
|
||||||
|
|
||||||
|
game_instance_init(&local_game_instance);
|
||||||
|
|
||||||
|
Presentation_State_Ingame_Context presentation_state_ingame_ctx;
|
||||||
|
memset(&presentation_state_ingame_ctx, 0, sizeof(Presentation_State_Ingame_Context));
|
||||||
|
|
||||||
Presentation_State_Main_Menu_Context presentation_state_main_menu_ctx = {
|
Presentation_State_Main_Menu_Context presentation_state_main_menu_ctx = {
|
||||||
.should_quit_game = &should_quit_game,
|
.should_quit_game = &should_quit_game,
|
||||||
|
.game_instance = &local_game_instance,
|
||||||
|
.ingame_ctx = &presentation_state_ingame_ctx
|
||||||
};
|
};
|
||||||
|
|
||||||
presentation_state_ingame_init(&presentation_state_ingame_ctx);
|
presentation_state_ingame_init(&presentation_state_ingame_ctx);
|
||||||
@@ -35,6 +46,10 @@ int main() {
|
|||||||
presentation_state_machine.current->tick(presentation_state_machine.current);
|
presentation_state_machine.current->tick(presentation_state_machine.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(IsKeyPressed(KEY_F)) {
|
||||||
|
ToggleFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
// Render.
|
// Render.
|
||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
{
|
{
|
||||||
@@ -48,5 +63,7 @@ int main() {
|
|||||||
assert(presentation_state_machine_go_to(NULL));
|
assert(presentation_state_machine_go_to(NULL));
|
||||||
CloseWindow();
|
CloseWindow();
|
||||||
|
|
||||||
|
game_instance_dispose(&local_game_instance);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -21,24 +21,6 @@ static void state_enter(Presentation_State *state) {
|
|||||||
|
|
||||||
printf("Entered ingame.\n");
|
printf("Entered ingame.\n");
|
||||||
|
|
||||||
printf("Setting up session ...\n");
|
|
||||||
|
|
||||||
assert(g_current_session != NULL);
|
|
||||||
Game_Session_Settings *settings = &g_current_session->settings;
|
|
||||||
printf(
|
|
||||||
"Singleplayer? %i.\n"
|
|
||||||
"Settings:\n"
|
|
||||||
"- Seed: %u\n"
|
|
||||||
"- Level width: %u, height: %u\n"
|
|
||||||
"- Max players: %u\n"
|
|
||||||
,
|
|
||||||
g_current_session->is_singleplayer,
|
|
||||||
settings->seed,
|
|
||||||
settings->level_width, settings->level_height,
|
|
||||||
settings->max_players
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
printf("Setting up context ...\n");
|
printf("Setting up context ...\n");
|
||||||
ctx->main_camera = (Camera2D) {
|
ctx->main_camera = (Camera2D) {
|
||||||
.offset = (Vector2) { 0, 0 },
|
.offset = (Vector2) { 0, 0 },
|
||||||
@@ -65,23 +47,46 @@ static void state_tick(Presentation_State *state) {
|
|||||||
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
|
||||||
Game_Session *session = g_current_session;
|
Game_Instance *instance = ctx->game_instance;
|
||||||
|
assert(instance != NULL);
|
||||||
|
Game_Session *session = instance->game_session;
|
||||||
|
assert(session != NULL);
|
||||||
|
|
||||||
{ // Push local input to queue.
|
{ // Push local input to queue.
|
||||||
game_session_enqueue_player_input(session, session->local_player_index, (Simulation_Game_Input) {
|
Simulation_Game_Input input = (Simulation_Game_Input) { // NOTE: SS - This needs to be slightly modified to support N local players.
|
||||||
.up = IsKeyDown(KEY_UP) || IsKeyDown(KEY_W),
|
.up = IsKeyDown(KEY_UP) || IsKeyDown(KEY_W),
|
||||||
.down = IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S),
|
.down = IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S),
|
||||||
.right = IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D),
|
.right = IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D),
|
||||||
.left = IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)
|
.left = IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if(!simulation_input_equal(input, ctx->prev_local_input)) {
|
||||||
|
game_instance_push_local_input(instance, input);
|
||||||
|
ctx->prev_local_input = input;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double delta_time = GetFrameTime();
|
const double delta_time = GetFrameTime();
|
||||||
game_session_update(session, delta_time);
|
ctx->simulation_accumulator += delta_time;
|
||||||
|
|
||||||
|
const double sim_dt = 1.0f / session->settings.tickrate;
|
||||||
|
|
||||||
|
while (ctx->simulation_accumulator >= sim_dt) {
|
||||||
|
// Pop input from instance's local_input_queue and set the local player's input to it, if we have one.
|
||||||
|
Simulation_Game_Input input = {0};
|
||||||
|
if(game_instance_pop_local_input(instance, &input)) {
|
||||||
|
// TODO: SS - We should probably check if this input is invalid for the snake. (pressing "go right" when going left, for example.)
|
||||||
|
// If it is invalid, the next input in the queue should be tried.
|
||||||
|
game_session_set_player_input(session, instance->local_player_index, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
game_session_tick(session);
|
||||||
|
ctx->simulation_accumulator -= sim_dt;
|
||||||
|
}
|
||||||
|
|
||||||
{ // TEMP: SS
|
{ // TEMP: SS
|
||||||
if(IsKeyPressed(KEY_TAB)) {
|
if(IsKeyPressed(KEY_TAB)) {
|
||||||
ctx->debug_render_match_state = !ctx->debug_render_match_state;
|
ctx->debug_draw_session_details = !ctx->debug_draw_session_details;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsKeyPressed(KEY_ESCAPE)) {
|
if(IsKeyPressed(KEY_ESCAPE)) {
|
||||||
@@ -102,7 +107,9 @@ static void state_render(Presentation_State *state) {
|
|||||||
|
|
||||||
ClearBackground((Color) { 240, 236, 226, 255 });
|
ClearBackground((Color) { 240, 236, 226, 255 });
|
||||||
|
|
||||||
Simulation_World *sim_world = &g_current_session->simulation_world;
|
Game_Instance *instance = ctx->game_instance;
|
||||||
|
Game_Session *session = instance->game_session;
|
||||||
|
Simulation_World *sim_world = &session->simulation_world;
|
||||||
assert(sim_world != NULL);
|
assert(sim_world != NULL);
|
||||||
|
|
||||||
Game_World *world = sim_world->game_world;
|
Game_World *world = sim_world->game_world;
|
||||||
@@ -222,7 +229,7 @@ static void state_render(Presentation_State *state) {
|
|||||||
// TODO: SS - Don't draw player-name if playing by yourself.
|
// TODO: SS - Don't draw player-name if playing by yourself.
|
||||||
// TODO: SS - Don't draw your own player-name, only others.
|
// TODO: SS - Don't draw your own player-name, only others.
|
||||||
{ // Draw player-name.
|
{ // Draw player-name.
|
||||||
const char *player_name = "mirakel"; // NOTE: SS - Hardcoded.
|
const char *player_name = "player"; // NOTE: SS - Hardcoded.
|
||||||
const uint32_t font_size = 8;
|
const uint32_t font_size = 8;
|
||||||
int text_width = MeasureText(player_name, font_size);
|
int text_width = MeasureText(player_name, font_size);
|
||||||
DrawText(player_name, pres_x - (float)text_width/2.0f + 4, pres_y - 16, font_size, (Color) { 255, 255, 255, 128 });
|
DrawText(player_name, pres_x - (float)text_width/2.0f + 4, pres_y - 16, font_size, (Color) { 255, 255, 255, 128 });
|
||||||
@@ -255,7 +262,7 @@ static void state_render(Presentation_State *state) {
|
|||||||
},
|
},
|
||||||
(Rectangle) { // Destination.
|
(Rectangle) { // Destination.
|
||||||
pres_x + origin.x,
|
pres_x + origin.x,
|
||||||
pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - ((sin(GetTime() * 12) + 1)/2) * 1,
|
pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - ((sin((GetTime() + (x + y)) * 12) + 1)/2) * 1,
|
||||||
GRID_CELL_SIZE,
|
GRID_CELL_SIZE,
|
||||||
GRID_CELL_SIZE
|
GRID_CELL_SIZE
|
||||||
},
|
},
|
||||||
@@ -271,11 +278,33 @@ static void state_render(Presentation_State *state) {
|
|||||||
|
|
||||||
// TODO: SS - Switch on 'sim_world->match_state'.
|
// TODO: SS - Switch on 'sim_world->match_state'.
|
||||||
|
|
||||||
if(ctx->debug_render_match_state) {
|
if(ctx->debug_draw_session_details) {
|
||||||
char buf[512];
|
char buf[1024];
|
||||||
snprintf(&buf[0], sizeof(buf), "Match-state: %i. Tick: %lu.", sim_world->match_state, sim_world->tick);
|
snprintf(&buf[0], sizeof(buf),
|
||||||
|
"Match-state: %i\n"
|
||||||
|
"Tick: %lu\n"
|
||||||
|
"\n"
|
||||||
|
"Seed: %u\n"
|
||||||
|
"Level: %ux%u\n"
|
||||||
|
"Tickrate: %.2f\n"
|
||||||
|
"\n"
|
||||||
|
|
||||||
DrawText(buf, 16, 16, 8, RED);
|
// TODO: SS - This should only be shown when playing online. Other things should be shown in the cases below;
|
||||||
|
// When playing offline/singleplayer you can only be one player.
|
||||||
|
// When playing local-multiplayer, you control multiple players from the same computer/client/instance.
|
||||||
|
"Local player: %u (host? %s)\n"
|
||||||
|
"Player count: %u / %u\n"
|
||||||
|
,
|
||||||
|
sim_world->match_state, sim_world->tick,
|
||||||
|
sim_world->game_world->seed,
|
||||||
|
sim_world->game_world->grid.width, sim_world->game_world->grid.height,
|
||||||
|
session->settings.tickrate,
|
||||||
|
instance->local_player_index, instance->local_player_index == 0 ? "true" : "false", // NOTE: SS - You're the host if you're player 0.
|
||||||
|
game_session_get_amount_of_active_players(session), sim_world->max_players
|
||||||
|
);
|
||||||
|
|
||||||
|
DrawText(buf, 17, 17, 8, BLACK);
|
||||||
|
DrawText(buf, 16, 16, 8, WHITE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EndMode2D();
|
EndMode2D();
|
||||||
@@ -292,7 +321,7 @@ static void state_exit(Presentation_State *state) {
|
|||||||
UnloadTexture(ctx->texture_snake_head);
|
UnloadTexture(ctx->texture_snake_head);
|
||||||
UnloadTexture(ctx->texture_snake_body);
|
UnloadTexture(ctx->texture_snake_body);
|
||||||
|
|
||||||
game_session_destroy();
|
ctx->game_instance = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Presentation_State presentation_state_ingame;
|
Presentation_State presentation_state_ingame;
|
||||||
|
|||||||
@@ -5,8 +5,13 @@
|
|||||||
#include "simulation/simulation_world.h"
|
#include "simulation/simulation_world.h"
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
|
#include "instance/game_instance.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
Game_Instance *game_instance;
|
||||||
|
double simulation_accumulator;
|
||||||
|
Simulation_Game_Input prev_local_input;
|
||||||
|
|
||||||
Camera2D main_camera;
|
Camera2D main_camera;
|
||||||
|
|
||||||
// Textures.
|
// Textures.
|
||||||
@@ -17,7 +22,7 @@ typedef struct {
|
|||||||
Texture2D texture_snake_body;
|
Texture2D texture_snake_body;
|
||||||
|
|
||||||
// Debug.
|
// Debug.
|
||||||
bool debug_render_match_state;
|
bool debug_draw_session_details;
|
||||||
|
|
||||||
} Presentation_State_Ingame_Context;
|
} Presentation_State_Ingame_Context;
|
||||||
|
|
||||||
|
|||||||
@@ -2,39 +2,58 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include "raygui.h"
|
#include "raygui.h"
|
||||||
|
|
||||||
|
#include "session/networking.h"
|
||||||
|
|
||||||
static void state_enter(Presentation_State *state) {
|
static void state_enter(Presentation_State *state) {
|
||||||
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
|
||||||
ctx->mode = Main_Menu_Mode_Home;
|
ctx->mode = Main_Menu_Mode_Home;
|
||||||
|
|
||||||
|
// if(ctx->game_session != NULL) {
|
||||||
|
// if(ctx->game_session->is_singleplayer) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// if(ctx->game_session->is_host) {
|
||||||
|
// networking_stop_hosting(ctx->game_networking);
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// game_session_dispose(ctx->game_session);
|
||||||
|
// ctx->game_session = NULL;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
static void state_tick(Presentation_State *state) {
|
static void state_tick(Presentation_State *state) {
|
||||||
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
||||||
|
|
||||||
{ // DEBUG
|
// { // DEBUG
|
||||||
if(IsKeyPressed(KEY_P)) {
|
// if(IsKeyPressed(KEY_P)) {
|
||||||
ctx->is_singleplayer = true;
|
// game_session_init_default_settings(true, &ctx->session_settings);
|
||||||
ctx->mode = Main_Menu_Mode_Game_Setup;
|
|
||||||
game_session_init_default_settings(ctx->is_singleplayer, &ctx->session_settings);
|
|
||||||
|
|
||||||
game_session_create(
|
// assert(ctx->game_session == NULL);
|
||||||
ctx->is_singleplayer,
|
// ctx->game_session = (Game_Session *)calloc(1, sizeof(Game_Session));
|
||||||
!ctx->is_singleplayer,
|
|
||||||
ctx->session_settings
|
|
||||||
);
|
|
||||||
|
|
||||||
presentation_state_machine_go_to(&presentation_state_ingame);
|
// game_session_init(
|
||||||
}
|
// ctx->game_session,
|
||||||
}
|
// ctx->session_settings
|
||||||
|
// );
|
||||||
|
|
||||||
|
// ctx->ingame_ctx->game_instance = ctx->game_instance;
|
||||||
|
// presentation_state_machine_go_to(&presentation_state_ingame);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
bool editing_width = false;
|
|
||||||
|
|
||||||
static void state_render(Presentation_State *state) {
|
static void state_render(Presentation_State *state) {
|
||||||
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
||||||
|
|
||||||
@@ -59,17 +78,15 @@ static void state_render(Presentation_State *state) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Main_Menu_Mode_Singleplayer: {
|
case Main_Menu_Mode_Singleplayer: {
|
||||||
ctx->is_singleplayer = true;
|
|
||||||
ctx->mode = Main_Menu_Mode_Game_Setup;
|
ctx->mode = Main_Menu_Mode_Game_Setup;
|
||||||
game_session_init_default_settings(ctx->is_singleplayer, &ctx->session_settings);
|
game_session_init_default_settings(false, &ctx->session_settings);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Main_Menu_Mode_Multiplayer: {
|
case Main_Menu_Mode_Multiplayer: {
|
||||||
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "Host")) {
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "Host")) {
|
||||||
ctx->is_singleplayer = false;
|
|
||||||
ctx->mode = Main_Menu_Mode_Game_Setup;
|
ctx->mode = Main_Menu_Mode_Game_Setup;
|
||||||
game_session_init_default_settings(ctx->is_singleplayer, &ctx->session_settings);
|
game_session_init_default_settings(true, &ctx->session_settings);
|
||||||
}
|
}
|
||||||
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "Join")) {
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "Join")) {
|
||||||
ctx->mode = Main_Menu_Mode_Multiplayer_Join;
|
ctx->mode = Main_Menu_Mode_Multiplayer_Join;
|
||||||
@@ -94,18 +111,16 @@ static void state_render(Presentation_State *state) {
|
|||||||
// Modify 'ctx->session_settings'.
|
// Modify 'ctx->session_settings'.
|
||||||
|
|
||||||
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "Play")) {
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "Play")) {
|
||||||
// Set up the ingame-context and transition to the ingame-state.
|
if(game_instance_host_session(ctx->game_instance, ctx->session_settings)) { // Settings indicate whether or not this is an online or offline game.
|
||||||
// NOTE: SS - We might need to wait before transitioning when playing multiplayer.
|
ctx->ingame_ctx->game_instance = ctx->game_instance;
|
||||||
game_session_create(
|
presentation_state_machine_go_to(&presentation_state_ingame);
|
||||||
ctx->is_singleplayer,
|
}
|
||||||
!ctx->is_singleplayer,
|
else {
|
||||||
ctx->session_settings
|
printf("Failed to play.\n");
|
||||||
);
|
}
|
||||||
|
|
||||||
presentation_state_machine_go_to(&presentation_state_ingame);
|
|
||||||
}
|
}
|
||||||
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "Cancel")) {
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "Cancel")) {
|
||||||
ctx->mode = ctx->is_singleplayer ? Main_Menu_Mode_Home : Main_Menu_Mode_Multiplayer;
|
ctx->mode = ctx->session_settings.max_players == 1 ? Main_Menu_Mode_Home : Main_Menu_Mode_Multiplayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -115,6 +130,8 @@ static void state_render(Presentation_State *state) {
|
|||||||
|
|
||||||
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "Connect")) {
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "Connect")) {
|
||||||
printf("TODO: SS - Connect to session id.\n");
|
printf("TODO: SS - Connect to session id.\n");
|
||||||
|
const char *session_id = "TEMP00"; // TEMP
|
||||||
|
game_instance_join_session(ctx->game_instance, session_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 2), 128, BUTTON_HEIGHT }, "Cancel")) {
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 2), 128, BUTTON_HEIGHT }, "Cancel")) {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
#define PRES_STATE_MAIN_MENU_H
|
#define PRES_STATE_MAIN_MENU_H
|
||||||
|
|
||||||
#include "states.h"
|
#include "states.h"
|
||||||
#include "session/game_session.h"
|
#include "state_ingame.h"
|
||||||
|
#include "instance/game_instance.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
Main_Menu_Mode_Home,
|
Main_Menu_Mode_Home,
|
||||||
@@ -16,10 +17,13 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
Main_Menu_Mode mode;
|
Main_Menu_Mode mode;
|
||||||
|
|
||||||
bool is_singleplayer;
|
|
||||||
Game_Session_Settings session_settings;
|
Game_Session_Settings session_settings;
|
||||||
|
|
||||||
|
Game_Instance *game_instance;
|
||||||
|
|
||||||
bool *should_quit_game;
|
bool *should_quit_game;
|
||||||
|
|
||||||
|
Presentation_State_Ingame_Context *ingame_ctx;
|
||||||
} Presentation_State_Main_Menu_Context;
|
} Presentation_State_Main_Menu_Context;
|
||||||
|
|
||||||
void presentation_state_main_menu_init(Presentation_State_Main_Menu_Context *ctx);
|
void presentation_state_main_menu_init(Presentation_State_Main_Menu_Context *ctx);
|
||||||
|
|||||||
@@ -6,22 +6,14 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "shared/squeue.h"
|
void game_session_init(Game_Session *session, Game_Session_Settings settings) {
|
||||||
|
assert(session != NULL);
|
||||||
|
|
||||||
#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->settings = settings;
|
||||||
|
|
||||||
session->simulation_world = simulation_create_world(
|
session->simulation_world = simulation_create_world(
|
||||||
session->settings.seed,
|
session->settings.seed,
|
||||||
|
session->settings.online,
|
||||||
session->settings.max_players,
|
session->settings.max_players,
|
||||||
session->settings.level_width,
|
session->settings.level_width,
|
||||||
session->settings.level_height
|
session->settings.level_height
|
||||||
@@ -29,63 +21,33 @@ void game_session_create(bool is_singleplayer, bool is_host, Game_Session_Settin
|
|||||||
|
|
||||||
session->players = (Game_Session_Player *)calloc(session->settings.max_players, sizeof(Game_Session_Player));
|
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));
|
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() {
|
void game_session_dispose(Game_Session *session) {
|
||||||
Game_Session *session = g_current_session;
|
|
||||||
|
|
||||||
if(session == NULL) {
|
if(session == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
simulation_destroy_world(&session->simulation_world);
|
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);
|
free(session->players);
|
||||||
session->players = NULL;
|
session->players = NULL;
|
||||||
free(session->players_prev);
|
free(session->players_prev);
|
||||||
session->players_prev = NULL;
|
session->players_prev = NULL;
|
||||||
|
|
||||||
free(session);
|
memset(session, 0, sizeof(Game_Session));
|
||||||
g_current_session = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings) {
|
void game_session_init_default_settings(bool online, Game_Session_Settings *out_settings) {
|
||||||
out_settings->seed = 1337; // TODO: SS - Randomize.
|
out_settings->seed = 1337; // TODO: SS - Randomize.
|
||||||
|
out_settings->online = online;
|
||||||
out_settings->tickrate = 10.0;
|
out_settings->tickrate = 10.0;
|
||||||
out_settings->level_width = 64;
|
out_settings->level_width = 64;
|
||||||
out_settings->level_height = 32;
|
out_settings->level_height = 32;
|
||||||
out_settings->max_players = is_singleplayer ? 1 : 8;
|
out_settings->max_players = online ? 8 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void game_session_tick(Game_Session *session) {
|
void game_session_tick(Game_Session *session) {
|
||||||
// Update input.
|
|
||||||
for(uint16_t i = 0; i < session->settings.max_players; i++) {
|
for(uint16_t i = 0; i < session->settings.max_players; i++) {
|
||||||
Game_Session_Player *session_player = &session->players[i];
|
Game_Session_Player *session_player = &session->players[i];
|
||||||
|
|
||||||
@@ -112,16 +74,12 @@ static void game_session_tick(Game_Session *session) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Simulation_Game_Input input = {0};
|
// Update input.
|
||||||
if(squeue_pop(&session_player->input_queue, &input)) {
|
simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) {
|
||||||
// We got an input.
|
.type = Simulation_Command_Type_Set_Player_Input,
|
||||||
|
.player_id = i,
|
||||||
simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) {
|
.player_input = session_player->input,
|
||||||
.type = Simulation_Command_Type_Set_Player_Input,
|
});
|
||||||
.player_id = i,
|
|
||||||
.player_input = input,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tick.
|
// Tick.
|
||||||
@@ -131,17 +89,6 @@ static void game_session_tick(Game_Session *session) {
|
|||||||
memcpy(session->players_prev, session->players, sizeof(Game_Session_Player) * session->settings.max_players);
|
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) {
|
static Game_Session_Player *game_session_get_player_from_id(Game_Session *session, uint16_t player_index) {
|
||||||
assert(session != NULL);
|
assert(session != NULL);
|
||||||
if(player_index >= session->settings.max_players) {
|
if(player_index >= session->settings.max_players) {
|
||||||
@@ -151,11 +98,19 @@ static Game_Session_Player *game_session_get_player_from_id(Game_Session *sessio
|
|||||||
return &session->players[player_index];
|
return &session->players[player_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
Game_Session_Player *game_session_get_local_player(Game_Session *session) {
|
uint16_t game_session_get_amount_of_active_players(Game_Session *session) {
|
||||||
assert(session != NULL);
|
assert(session != NULL);
|
||||||
return game_session_get_player_from_id(session, session->local_player_index);
|
uint16_t amount = 0;
|
||||||
}
|
|
||||||
|
|
||||||
|
for(uint16_t i = 0; i < session->settings.max_players; i++) {
|
||||||
|
Game_Session_Player *player = &session->players[i];
|
||||||
|
if(player->active) {
|
||||||
|
amount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
bool game_session_create_player(Game_Session *session, uint16_t *out_player_index) {
|
bool game_session_create_player(Game_Session *session, uint16_t *out_player_index) {
|
||||||
assert(session != NULL);
|
assert(session != NULL);
|
||||||
@@ -191,23 +146,10 @@ void game_session_destroy_player(Game_Session *session, uint16_t player_index) {
|
|||||||
memset(player, 0, sizeof(Game_Session_Player));
|
memset(player, 0, sizeof(Game_Session_Player));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool game_session_enqueue_player_input(Game_Session *session, uint16_t player_index, Simulation_Game_Input input) {
|
bool game_session_set_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);
|
Game_Session_Player *player = game_session_get_player_from_id(session, player_index);
|
||||||
assert(player != NULL);
|
assert(player != NULL);
|
||||||
|
|
||||||
if(simulation_input_equal(player->most_recent_input, input)) {
|
player->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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -13,54 +13,46 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
bool active;
|
bool active;
|
||||||
|
|
||||||
SQueue input_queue;
|
Simulation_Game_Input input;
|
||||||
Simulation_Game_Input most_recent_input; // Exclusively used to make sure that the input-queue doesn't get filled with duplicated inputs.
|
|
||||||
} Game_Session_Player;
|
} Game_Session_Player;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t seed;
|
uint32_t seed;
|
||||||
|
|
||||||
double tickrate;
|
bool online;
|
||||||
|
|
||||||
uint16_t level_width;
|
uint16_t level_width;
|
||||||
uint16_t level_height;
|
uint16_t level_height;
|
||||||
|
|
||||||
|
double tickrate;
|
||||||
|
|
||||||
uint8_t max_players;
|
uint8_t max_players;
|
||||||
|
|
||||||
// ..
|
// ..
|
||||||
} Game_Session_Settings;
|
} Game_Session_Settings;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool is_singleplayer;
|
|
||||||
bool is_host;
|
|
||||||
|
|
||||||
Game_Session_Settings settings;
|
Game_Session_Settings settings;
|
||||||
|
|
||||||
Simulation_World simulation_world;
|
|
||||||
double simulation_accumulator;
|
|
||||||
|
|
||||||
Game_Session_Player *players;
|
Game_Session_Player *players;
|
||||||
Game_Session_Player *players_prev;
|
Game_Session_Player *players_prev;
|
||||||
uint16_t local_player_index;
|
|
||||||
|
|
||||||
// TODO: SS - Local + remote input-queue.
|
Simulation_World simulation_world;
|
||||||
} Game_Session;
|
} Game_Session;
|
||||||
|
|
||||||
extern Game_Session *g_current_session;
|
void game_session_init(Game_Session *session, Game_Session_Settings settings);
|
||||||
|
void game_session_dispose(Game_Session *session);
|
||||||
|
|
||||||
void game_session_create(bool is_singleplayer, bool is_host, Game_Session_Settings settings);
|
void game_session_init_default_settings(bool online, Game_Session_Settings *out_settings);
|
||||||
void game_session_destroy();
|
|
||||||
|
|
||||||
void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings);
|
void game_session_tick(Game_Session *session);
|
||||||
|
|
||||||
void game_session_update(Game_Session *session, const double delta_time);
|
uint16_t game_session_get_amount_of_active_players(Game_Session *session);
|
||||||
|
|
||||||
Game_Session_Player *game_session_get_local_player(Game_Session *session);
|
|
||||||
|
|
||||||
bool game_session_create_player(Game_Session *session, uint16_t *out_player_index);
|
bool game_session_create_player(Game_Session *session, uint16_t *out_player_index);
|
||||||
void game_session_destroy_player(Game_Session *session, uint16_t player_index);
|
void game_session_destroy_player(Game_Session *session, uint16_t player_index);
|
||||||
|
|
||||||
bool game_session_enqueue_player_input(Game_Session *session, uint16_t player_index, Simulation_Game_Input input);
|
bool game_session_set_player_input(Game_Session *session, uint16_t player_index, Simulation_Game_Input input);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "MultiplayerApi.h"
|
#include "multiplayer_api.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -15,18 +15,19 @@
|
|||||||
typedef struct ListenerNode {
|
typedef struct ListenerNode {
|
||||||
int id;
|
int id;
|
||||||
MultiplayerListener cb;
|
MultiplayerListener cb;
|
||||||
void *user_data;
|
void *context;
|
||||||
struct ListenerNode *next;
|
struct ListenerNode *next;
|
||||||
} ListenerNode;
|
} ListenerNode;
|
||||||
|
|
||||||
typedef struct ListenerSnapshot {
|
typedef struct ListenerSnapshot {
|
||||||
MultiplayerListener cb;
|
MultiplayerListener cb;
|
||||||
void *user_data;
|
void *context;
|
||||||
} ListenerSnapshot;
|
} ListenerSnapshot;
|
||||||
|
|
||||||
struct MultiplayerApi {
|
struct MultiplayerApi {
|
||||||
char *server_host;
|
char *server_host;
|
||||||
uint16_t server_port;
|
uint16_t server_port;
|
||||||
|
char identifier[37];
|
||||||
int sockfd;
|
int sockfd;
|
||||||
char *session_id;
|
char *session_id;
|
||||||
|
|
||||||
@@ -48,12 +49,19 @@ static void *recv_thread_main(void *arg);
|
|||||||
static void process_line(MultiplayerApi *api, const char *line);
|
static void process_line(MultiplayerApi *api, const char *line);
|
||||||
static int start_recv_thread(MultiplayerApi *api);
|
static int start_recv_thread(MultiplayerApi *api);
|
||||||
|
|
||||||
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port) {
|
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port, const char *identifier)
|
||||||
MultiplayerApi *api = (MultiplayerApi *)calloc(1, sizeof(MultiplayerApi));
|
{
|
||||||
|
int len = strlen(identifier);
|
||||||
|
if (len != 36) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiplayerApi *api = (MultiplayerApi *)calloc(1, sizeof(MultiplayerApi));
|
||||||
if (!api) {
|
if (!api) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (server_host) {
|
if (server_host) {
|
||||||
api->server_host = strdup(server_host);
|
api->server_host = strdup(server_host);
|
||||||
} else {
|
} else {
|
||||||
@@ -66,6 +74,9 @@ MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
api->server_port = server_port;
|
api->server_port = server_port;
|
||||||
|
|
||||||
|
strncpy(api->identifier, identifier, 37);
|
||||||
|
|
||||||
api->sockfd = -1;
|
api->sockfd = -1;
|
||||||
api->session_id = NULL;
|
api->session_id = NULL;
|
||||||
api->recv_thread_started = 0;
|
api->recv_thread_started = 0;
|
||||||
@@ -117,6 +128,7 @@ void mp_api_destroy(MultiplayerApi *api) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int mp_api_host(MultiplayerApi *api,
|
int mp_api_host(MultiplayerApi *api,
|
||||||
|
json_t *data,
|
||||||
char **out_session,
|
char **out_session,
|
||||||
char **out_clientId,
|
char **out_clientId,
|
||||||
json_t **out_data) {
|
json_t **out_data) {
|
||||||
@@ -129,9 +141,16 @@ int mp_api_host(MultiplayerApi *api,
|
|||||||
json_t *root = json_object();
|
json_t *root = json_object();
|
||||||
if (!root) return MP_API_ERR_IO;
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
json_object_set_new(root, "session", json_null());
|
json_object_set_new(root, "identifier", json_string(api->identifier));
|
||||||
json_object_set_new(root, "cmd", json_string("host"));
|
json_object_set_new(root, "cmd", json_string("host"));
|
||||||
json_object_set_new(root, "data", json_object());
|
|
||||||
|
json_t *data_copy;
|
||||||
|
if (data && json_is_object(data)) {
|
||||||
|
data_copy = json_deep_copy(data);
|
||||||
|
} else {
|
||||||
|
data_copy = json_object();
|
||||||
|
}
|
||||||
|
json_object_set_new(root, "data", data_copy);
|
||||||
|
|
||||||
rc = send_json_line(api, root);
|
rc = send_json_line(api, root);
|
||||||
if (rc != MP_API_OK) {
|
if (rc != MP_API_OK) {
|
||||||
@@ -214,6 +233,7 @@ int mp_api_list(MultiplayerApi *api, json_t **out_list)
|
|||||||
json_t *root = json_object();
|
json_t *root = json_object();
|
||||||
if (!root) return MP_API_ERR_IO;
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "identifier", json_string(api->identifier));
|
||||||
json_object_set_new(root, "cmd", json_string("list"));
|
json_object_set_new(root, "cmd", json_string("list"));
|
||||||
|
|
||||||
rc = send_json_line(api, root);
|
rc = send_json_line(api, root);
|
||||||
@@ -277,6 +297,7 @@ int mp_api_join(MultiplayerApi *api,
|
|||||||
json_t *root = json_object();
|
json_t *root = json_object();
|
||||||
if (!root) return MP_API_ERR_IO;
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "identifier", json_string(api->identifier));
|
||||||
json_object_set_new(root, "session", json_string(sessionId));
|
json_object_set_new(root, "session", json_string(sessionId));
|
||||||
json_object_set_new(root, "cmd", json_string("join"));
|
json_object_set_new(root, "cmd", json_string("join"));
|
||||||
|
|
||||||
@@ -371,16 +392,20 @@ int mp_api_join(MultiplayerApi *api,
|
|||||||
return joinAccepted ? MP_API_OK : MP_API_ERR_REJECTED;
|
return joinAccepted ? MP_API_OK : MP_API_ERR_REJECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mp_api_game(MultiplayerApi *api, json_t *data) {
|
int mp_api_game(MultiplayerApi *api, json_t *data, const char* destination) {
|
||||||
if (!api || !data) return MP_API_ERR_ARGUMENT;
|
if (!api || !data) return MP_API_ERR_ARGUMENT;
|
||||||
if (api->sockfd < 0 || !api->session_id) return MP_API_ERR_STATE;
|
if (api->sockfd < 0 || !api->session_id) return MP_API_ERR_STATE;
|
||||||
|
|
||||||
json_t *root = json_object();
|
json_t *root = json_object();
|
||||||
if (!root) return MP_API_ERR_IO;
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "identifier", json_string(api->identifier));
|
||||||
json_object_set_new(root, "session", json_string(api->session_id));
|
json_object_set_new(root, "session", json_string(api->session_id));
|
||||||
json_object_set_new(root, "cmd", json_string("game"));
|
json_object_set_new(root, "cmd", json_string("game"));
|
||||||
|
|
||||||
|
if(destination)
|
||||||
|
json_object_set_new(root, "destination", json_string(destination));
|
||||||
|
|
||||||
json_t *data_copy;
|
json_t *data_copy;
|
||||||
if (json_is_object(data)) {
|
if (json_is_object(data)) {
|
||||||
data_copy = json_deep_copy(data);
|
data_copy = json_deep_copy(data);
|
||||||
@@ -394,14 +419,14 @@ int mp_api_game(MultiplayerApi *api, json_t *data) {
|
|||||||
|
|
||||||
int mp_api_listen(MultiplayerApi *api,
|
int mp_api_listen(MultiplayerApi *api,
|
||||||
MultiplayerListener cb,
|
MultiplayerListener cb,
|
||||||
void *user_data) {
|
void *context) {
|
||||||
if (!api || !cb) return -1;
|
if (!api || !cb) return -1;
|
||||||
|
|
||||||
ListenerNode *node = (ListenerNode *)malloc(sizeof(ListenerNode));
|
ListenerNode *node = (ListenerNode *)malloc(sizeof(ListenerNode));
|
||||||
if (!node) return -1;
|
if (!node) return -1;
|
||||||
|
|
||||||
node->cb = cb;
|
node->cb = cb;
|
||||||
node->user_data = user_data;
|
node->context = context;
|
||||||
|
|
||||||
pthread_mutex_lock(&api->lock);
|
pthread_mutex_lock(&api->lock);
|
||||||
node->id = api->next_listener_id++;
|
node->id = api->next_listener_id++;
|
||||||
@@ -505,6 +530,8 @@ static int send_json_line(MultiplayerApi *api, json_t *obj) {
|
|||||||
return MP_API_ERR_IO;
|
return MP_API_ERR_IO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("Sending JSON: %s\n", text); // Debug print
|
||||||
|
|
||||||
size_t len = strlen(text);
|
size_t len = strlen(text);
|
||||||
int fd = api->sockfd;
|
int fd = api->sockfd;
|
||||||
|
|
||||||
@@ -640,7 +667,7 @@ static void process_line(MultiplayerApi *api, const char *line) {
|
|||||||
while (node) {
|
while (node) {
|
||||||
if (node->cb) {
|
if (node->cb) {
|
||||||
snapshot[idx].cb = node->cb;
|
snapshot[idx].cb = node->cb;
|
||||||
snapshot[idx].user_data = node->user_data;
|
snapshot[idx].context = node->context;
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
node = node->next;
|
node = node->next;
|
||||||
@@ -648,7 +675,7 @@ static void process_line(MultiplayerApi *api, const char *line) {
|
|||||||
pthread_mutex_unlock(&api->lock);
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
snapshot[i].cb(cmd, (int64_t)msgId, clientId, data_obj, snapshot[i].user_data);
|
snapshot[i].cb(cmd, (int64_t)msgId, clientId, data_obj, snapshot[i].context);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(snapshot);
|
free(snapshot);
|
||||||
@@ -4,10 +4,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "jansson/jansson.h"
|
#include "jansson/jansson.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct MultiplayerApi MultiplayerApi;
|
typedef struct MultiplayerApi MultiplayerApi;
|
||||||
|
|
||||||
/* Callback‑typ för inkommande events från servern. */
|
/* Callback‑typ för inkommande events från servern. */
|
||||||
@@ -16,7 +12,7 @@ typedef void (*MultiplayerListener)(
|
|||||||
int64_t messageId, /* sekventiellt meddelande‑ID (från host) */
|
int64_t messageId, /* sekventiellt meddelande‑ID (från host) */
|
||||||
const char *clientId, /* avsändarens klient‑ID (eller NULL) */
|
const char *clientId, /* avsändarens klient‑ID (eller NULL) */
|
||||||
json_t *data, /* JSON‑objekt med godtycklig speldata */
|
json_t *data, /* JSON‑objekt med godtycklig speldata */
|
||||||
void *user_data /* godtycklig pekare som skickas vidare */
|
void *context /* godtycklig pekare som skickas vidare */
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Returkoder */
|
/* Returkoder */
|
||||||
@@ -31,7 +27,7 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Skapar en ny API‑instans. Returnerar NULL vid fel. */
|
/* Skapar en ny API‑instans. Returnerar NULL vid fel. */
|
||||||
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port);
|
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port, const char *identifier);
|
||||||
|
|
||||||
/* Stänger ner anslutning, stoppar mottagartråd och frigör minne. */
|
/* Stänger ner anslutning, stoppar mottagartråd och frigör minne. */
|
||||||
void mp_api_destroy(MultiplayerApi *api);
|
void mp_api_destroy(MultiplayerApi *api);
|
||||||
@@ -41,9 +37,10 @@ void mp_api_destroy(MultiplayerApi *api);
|
|||||||
anroparen ansvarar för att free:a. out_data (om ej NULL) får ett json_t*
|
anroparen ansvarar för att free:a. out_data (om ej NULL) får ett json_t*
|
||||||
med extra data från servern (anroparen ska json_decref när klart). */
|
med extra data från servern (anroparen ska json_decref när klart). */
|
||||||
int mp_api_host(MultiplayerApi *api,
|
int mp_api_host(MultiplayerApi *api,
|
||||||
char **out_session,
|
json_t *data,
|
||||||
char **out_clientId,
|
char **out_session,
|
||||||
json_t **out_data);
|
char **out_clientId,
|
||||||
|
json_t **out_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Hämtar en lista över tillgängliga publika sessioner.
|
Hämtar en lista över tillgängliga publika sessioner.
|
||||||
@@ -70,19 +67,15 @@ int mp_api_join(MultiplayerApi *api,
|
|||||||
json_t **out_data);
|
json_t **out_data);
|
||||||
|
|
||||||
/* Skickar ett "game"‑meddelande med godtycklig JSON‑data till sessionen. */
|
/* Skickar ett "game"‑meddelande med godtycklig JSON‑data till sessionen. */
|
||||||
int mp_api_game(MultiplayerApi *api, json_t *data);
|
int mp_api_game(MultiplayerApi *api, json_t *data, const char* destination);
|
||||||
|
|
||||||
/* Registrerar en lyssnare för inkommande events.
|
/* Registrerar en lyssnare för inkommande events.
|
||||||
Returnerar ett positivt listener‑ID, eller −1 vid fel. */
|
Returnerar ett positivt listener‑ID, eller −1 vid fel. */
|
||||||
int mp_api_listen(MultiplayerApi *api,
|
int mp_api_listen(MultiplayerApi *api,
|
||||||
MultiplayerListener cb,
|
MultiplayerListener cb,
|
||||||
void *user_data);
|
void *context);
|
||||||
|
|
||||||
/* Avregistrerar lyssnare. Listener‑ID är värdet från mp_api_listen. */
|
/* Avregistrerar lyssnare. Listener‑ID är värdet från mp_api_listen. */
|
||||||
void mp_api_unlisten(MultiplayerApi *api, int listener_id);
|
void mp_api_unlisten(MultiplayerApi *api, int listener_id);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* MULTIPLAYER_API_H */
|
#endif /* MULTIPLAYER_API_H */
|
||||||
104
src/session/networking.c
Normal file
104
src/session/networking.c
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#include "networking.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define ONVO_IDENTIFIER "c2438167-831b-4bf7-8bdc-abcdefabcd00"
|
||||||
|
|
||||||
|
static inline bool setup_api(Game_Networking *networking) {
|
||||||
|
assert(networking != NULL);
|
||||||
|
|
||||||
|
if(networking->api != NULL) {
|
||||||
|
printf("Failed to set up API. Already active.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiplayerApi *mp_api = mp_api_create("kontoret.onvo.se", 9001, ONVO_IDENTIFIER);
|
||||||
|
if(mp_api == NULL) {
|
||||||
|
printf("Failed to set up API. Could not initialize.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
networking->api = mp_api;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool networking_try_host(Game_Networking *networking, Game_Session *session, Game_Session_Settings settings) {
|
||||||
|
assert(networking != NULL);
|
||||||
|
|
||||||
|
if(!settings.online) {
|
||||||
|
printf("Failed to host; Game_Session_Settings.online == false, expected it to be true.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!setup_api(networking)) {
|
||||||
|
printf("Failed to host; API is already set up.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *session_id = NULL;
|
||||||
|
char *local_client_id = NULL;
|
||||||
|
json_t *response_data = NULL;
|
||||||
|
int host_result = mp_api_host(
|
||||||
|
networking->api,
|
||||||
|
NULL, // TODO: SS - Send data to server that contains the game-session's settings.
|
||||||
|
&session_id,
|
||||||
|
&local_client_id,
|
||||||
|
&response_data
|
||||||
|
);
|
||||||
|
if(host_result != MP_API_OK) {
|
||||||
|
printf("Failed to host; Got result: %i.\n", host_result);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Started hosting session '%s', local_client_id is '%s'.\n", session_id, local_client_id);
|
||||||
|
networking->session_id = session_id;
|
||||||
|
networking->local_client_id = local_client_id;
|
||||||
|
|
||||||
|
// TODO: SS - Start listening.
|
||||||
|
|
||||||
|
// Set up session.
|
||||||
|
game_session_init(
|
||||||
|
session,
|
||||||
|
settings
|
||||||
|
);
|
||||||
|
|
||||||
|
if(response_data != NULL) {
|
||||||
|
// TODO: SS - Handle JSON-response.
|
||||||
|
json_decref(response_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool networking_try_join(Game_Networking *networking, const char *session_id) {
|
||||||
|
assert(networking != NULL);
|
||||||
|
|
||||||
|
if(!setup_api(networking)) {
|
||||||
|
printf("Failed to join; API is already set up.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Trying to join session with id: '%s' ...\n", session_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool networking_stop_hosting(Game_Networking *networking) {
|
||||||
|
assert(networking != NULL);
|
||||||
|
if(networking->api == NULL) {
|
||||||
|
printf("Failed to stop hosting; API is not set up.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_api_destroy(networking->api);
|
||||||
|
networking->api = NULL;
|
||||||
|
|
||||||
|
if(networking->session_id != NULL) {
|
||||||
|
free(networking->session_id);
|
||||||
|
}
|
||||||
|
if(networking->local_client_id != NULL) {
|
||||||
|
free(networking->local_client_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Stopped hosting.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
19
src/session/networking.h
Normal file
19
src/session/networking.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef NETWORKING_H
|
||||||
|
#define NETWORKING_H
|
||||||
|
|
||||||
|
#include "multiplayer_api.h"
|
||||||
|
#include "game_session.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MultiplayerApi *api;
|
||||||
|
|
||||||
|
char *session_id;
|
||||||
|
char *local_client_id;
|
||||||
|
} Game_Networking;
|
||||||
|
|
||||||
|
bool networking_try_host(Game_Networking *networking, Game_Session *session, Game_Session_Settings settings);
|
||||||
|
bool networking_try_join(Game_Networking *networking, const char *session_id);
|
||||||
|
|
||||||
|
bool networking_stop_hosting(Game_Networking *networking);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -25,6 +25,8 @@ void game_world_destroy(Game_World *world);
|
|||||||
bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, uint16_t y, Entity_ID *out_entity_id);
|
bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, uint16_t y, Entity_ID *out_entity_id);
|
||||||
void game_world_destroy_entity(Game_World *world, Entity_ID entity_id);
|
void game_world_destroy_entity(Game_World *world, Entity_ID entity_id);
|
||||||
|
|
||||||
|
// TODO: SS - "void game_world_spawn_player(Game_World *world, ..)"
|
||||||
|
|
||||||
Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id);
|
Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -6,12 +6,14 @@
|
|||||||
|
|
||||||
#define SIM_COMMANDS_PER_PLAYER 4
|
#define SIM_COMMANDS_PER_PLAYER 4
|
||||||
|
|
||||||
Simulation_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height) {
|
Simulation_World simulation_create_world(uint32_t seed, bool online, uint8_t max_players, uint16_t width, uint16_t height) {
|
||||||
Simulation_World w;
|
Simulation_World w;
|
||||||
memset(&w, 0, sizeof(Simulation_World));
|
memset(&w, 0, sizeof(Simulation_World));
|
||||||
|
|
||||||
w.tick = 0;
|
w.tick = 0;
|
||||||
|
|
||||||
|
w.online = online;
|
||||||
|
|
||||||
w.game_world = game_world_create(seed, width, height);
|
w.game_world = game_world_create(seed, width, height);
|
||||||
assert(w.game_world != NULL);
|
assert(w.game_world != NULL);
|
||||||
|
|
||||||
@@ -97,9 +99,7 @@ void simulation_world_tick(Simulation_World *simulation_world) {
|
|||||||
|
|
||||||
switch(simulation_world->match_state) {
|
switch(simulation_world->match_state) {
|
||||||
case Simulation_Match_State_Waiting_To_Start: {
|
case Simulation_Match_State_Waiting_To_Start: {
|
||||||
bool is_singleplayer = simulation_world->max_players == 1;
|
if(!simulation_world->online) { // If not online, just go to Counting_Down.
|
||||||
|
|
||||||
if(is_singleplayer) {
|
|
||||||
simulation_world->match_state = Simulation_Match_State_Counting_Down;
|
simulation_world->match_state = Simulation_Match_State_Counting_Down;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -230,7 +230,6 @@ void simulation_world_tick(Simulation_World *simulation_world) {
|
|||||||
|
|
||||||
// Figure out what cell we're on and what cell we want to go to.
|
// Figure out what cell we're on and what cell we want to go to.
|
||||||
Grid_Cell *current_cell = grid_get_cell(&gw->grid, entity->x, entity->y);
|
Grid_Cell *current_cell = grid_get_cell(&gw->grid, entity->x, entity->y);
|
||||||
Grid_Cell *start_cell = current_cell;
|
|
||||||
assert(current_cell != NULL);
|
assert(current_cell != NULL);
|
||||||
Grid_Cell *target_cell = grid_get_cell(&gw->grid, entity->x + dx, entity->y + dy);
|
Grid_Cell *target_cell = grid_get_cell(&gw->grid, entity->x + dx, entity->y + dy);
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
#define SIMULATION_WORLD_H
|
#define SIMULATION_WORLD_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "shared/game_world.h"
|
#include "shared/game_world.h"
|
||||||
|
#include "shared/random.h" // Deterministic random.
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
@@ -17,6 +19,8 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t tick;
|
uint64_t tick;
|
||||||
|
|
||||||
|
bool online;
|
||||||
|
|
||||||
Game_World *game_world;
|
Game_World *game_world;
|
||||||
|
|
||||||
Simulation_Match_State match_state;
|
Simulation_Match_State match_state;
|
||||||
@@ -29,7 +33,7 @@ typedef struct {
|
|||||||
uint16_t max_commands;
|
uint16_t max_commands;
|
||||||
} Simulation_World;
|
} Simulation_World;
|
||||||
|
|
||||||
Simulation_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height);
|
Simulation_World simulation_create_world(uint32_t seed, bool online, uint8_t max_players, uint16_t width, uint16_t height);
|
||||||
void simulation_destroy_world(Simulation_World *simulation_world);
|
void simulation_destroy_world(Simulation_World *simulation_world);
|
||||||
|
|
||||||
void simulation_world_tick(Simulation_World *simulation_world);
|
void simulation_world_tick(Simulation_World *simulation_world);
|
||||||
|
|||||||
Reference in New Issue
Block a user