Moved code and assets for the game to a seperate folder. Moved jansson and raylib to third_party.
This commit is contained in:
155
src/game/session/game_session.c
Normal file
155
src/game/session/game_session.c
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "game_session.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void game_session_init(Game_Session *session, Game_Session_Settings settings) {
|
||||
assert(session != NULL);
|
||||
|
||||
session->settings = settings;
|
||||
|
||||
session->simulation_world = simulation_create_world(
|
||||
session->settings.seed,
|
||||
session->settings.online,
|
||||
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));
|
||||
}
|
||||
|
||||
void game_session_dispose(Game_Session *session) {
|
||||
if(session == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
simulation_destroy_world(&session->simulation_world);
|
||||
|
||||
free(session->players);
|
||||
session->players = NULL;
|
||||
free(session->players_prev);
|
||||
session->players_prev = NULL;
|
||||
|
||||
memset(session, 0, sizeof(Game_Session));
|
||||
}
|
||||
|
||||
void game_session_init_default_settings(bool online, Game_Session_Settings *out_settings) {
|
||||
out_settings->seed = 1337; // TODO: SS - Randomize.
|
||||
out_settings->online = online;
|
||||
out_settings->tickrate = 10.0;
|
||||
out_settings->level_width = 64;
|
||||
out_settings->level_height = 32;
|
||||
out_settings->max_players = online ? 8 : 2;
|
||||
}
|
||||
|
||||
void game_session_tick(Game_Session *session) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Update input.
|
||||
simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) {
|
||||
.type = Simulation_Command_Type_Set_Player_Input,
|
||||
.player_id = i,
|
||||
.player_input = session_player->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);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
uint16_t game_session_get_amount_of_active_players(Game_Session *session) {
|
||||
assert(session != NULL);
|
||||
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) {
|
||||
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_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);
|
||||
assert(player != NULL);
|
||||
|
||||
player->input = input;
|
||||
return true;
|
||||
}
|
||||
58
src/game/session/game_session.h
Normal file
58
src/game/session/game_session.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef GAME_SESSION_H
|
||||
#define GAME_SESSION_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "shared/squeue.h"
|
||||
|
||||
#include "simulation/simulation_world.h"
|
||||
#include "simulation/input.h"
|
||||
#include "simulation/player.h"
|
||||
|
||||
typedef struct {
|
||||
bool active;
|
||||
|
||||
Simulation_Game_Input input;
|
||||
} Game_Session_Player;
|
||||
|
||||
typedef struct {
|
||||
uint32_t seed;
|
||||
|
||||
bool online;
|
||||
|
||||
uint16_t level_width;
|
||||
uint16_t level_height;
|
||||
|
||||
double tickrate;
|
||||
|
||||
uint8_t max_players;
|
||||
|
||||
// ..
|
||||
} Game_Session_Settings;
|
||||
|
||||
typedef struct {
|
||||
Game_Session_Settings settings;
|
||||
|
||||
Game_Session_Player *players;
|
||||
Game_Session_Player *players_prev;
|
||||
|
||||
Simulation_World simulation_world;
|
||||
} Game_Session;
|
||||
|
||||
void game_session_init(Game_Session *session, Game_Session_Settings settings);
|
||||
void game_session_dispose(Game_Session *session);
|
||||
|
||||
void game_session_init_default_settings(bool online, Game_Session_Settings *out_settings);
|
||||
|
||||
void game_session_tick(Game_Session *session);
|
||||
|
||||
uint16_t game_session_get_amount_of_active_players(Game_Session *session);
|
||||
|
||||
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);
|
||||
|
||||
bool game_session_set_player_input(Game_Session *session, uint16_t player_index, Simulation_Game_Input input);
|
||||
|
||||
|
||||
#endif
|
||||
758
src/game/session/multiplayer_api.c
Normal file
758
src/game/session/multiplayer_api.c
Normal file
@@ -0,0 +1,758 @@
|
||||
#include "multiplayer_api.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
typedef struct ListenerNode {
|
||||
int id;
|
||||
MultiplayerListener cb;
|
||||
void *context;
|
||||
struct ListenerNode *next;
|
||||
} ListenerNode;
|
||||
|
||||
typedef struct ListenerSnapshot {
|
||||
MultiplayerListener cb;
|
||||
void *context;
|
||||
} ListenerSnapshot;
|
||||
|
||||
struct MultiplayerApi {
|
||||
char *server_host;
|
||||
uint16_t server_port;
|
||||
char identifier[37];
|
||||
int sockfd;
|
||||
char *session_id;
|
||||
|
||||
pthread_t recv_thread;
|
||||
int recv_thread_started;
|
||||
int running;
|
||||
|
||||
pthread_mutex_t lock;
|
||||
ListenerNode *listeners;
|
||||
int next_listener_id;
|
||||
};
|
||||
|
||||
static int connect_to_server(const char *host, uint16_t port);
|
||||
static int ensure_connected(MultiplayerApi *api);
|
||||
static int send_all(int fd, const char *buf, size_t len);
|
||||
static int send_json_line(MultiplayerApi *api, json_t *obj); /* tar över ägarskap */
|
||||
static int read_line(int fd, char **out_line);
|
||||
static void *recv_thread_main(void *arg);
|
||||
static void process_line(MultiplayerApi *api, const char *line);
|
||||
static int start_recv_thread(MultiplayerApi *api);
|
||||
|
||||
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port, const char *identifier)
|
||||
{
|
||||
int len = strlen(identifier);
|
||||
if (len != 36) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MultiplayerApi *api = (MultiplayerApi *)calloc(1, sizeof(MultiplayerApi));
|
||||
if (!api) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if (server_host) {
|
||||
api->server_host = strdup(server_host);
|
||||
} else {
|
||||
api->server_host = strdup("127.0.0.1");
|
||||
}
|
||||
|
||||
if (!api->server_host) {
|
||||
free(api);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
api->server_port = server_port;
|
||||
|
||||
strncpy(api->identifier, identifier, 37);
|
||||
|
||||
api->sockfd = -1;
|
||||
api->session_id = NULL;
|
||||
api->recv_thread_started = 0;
|
||||
api->running = 0;
|
||||
api->listeners = NULL;
|
||||
api->next_listener_id = 1;
|
||||
|
||||
if (pthread_mutex_init(&api->lock, NULL) != 0) {
|
||||
free(api->server_host);
|
||||
free(api);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
void mp_api_destroy(MultiplayerApi *api) {
|
||||
if (!api) return;
|
||||
|
||||
if (api->recv_thread_started && api->sockfd >= 0) {
|
||||
shutdown(api->sockfd, SHUT_RDWR);
|
||||
pthread_join(api->recv_thread, NULL);
|
||||
}
|
||||
|
||||
if (api->sockfd >= 0) {
|
||||
close(api->sockfd);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&api->lock);
|
||||
ListenerNode *node = api->listeners;
|
||||
api->listeners = NULL;
|
||||
pthread_mutex_unlock(&api->lock);
|
||||
|
||||
while (node) {
|
||||
ListenerNode *next = node->next;
|
||||
free(node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
if (api->session_id) {
|
||||
free(api->session_id);
|
||||
}
|
||||
if (api->server_host) {
|
||||
free(api->server_host);
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&api->lock);
|
||||
free(api);
|
||||
}
|
||||
|
||||
int mp_api_host(MultiplayerApi *api,
|
||||
json_t *data,
|
||||
char **out_session,
|
||||
char **out_clientId,
|
||||
json_t **out_data) {
|
||||
if (!api) return MP_API_ERR_ARGUMENT;
|
||||
if (api->session_id) return MP_API_ERR_STATE;
|
||||
|
||||
int rc = ensure_connected(api);
|
||||
if (rc != MP_API_OK) return rc;
|
||||
|
||||
json_t *root = json_object();
|
||||
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("host"));
|
||||
|
||||
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);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *line = NULL;
|
||||
rc = read_line(api->sockfd, &line);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
json_error_t jerr;
|
||||
json_t *resp = json_loads(line, 0, &jerr);
|
||||
free(line);
|
||||
if (!resp || !json_is_object(resp)) {
|
||||
if (resp) json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *cmd_val = json_object_get(resp, "cmd");
|
||||
if (!json_is_string(cmd_val) || strcmp(json_string_value(cmd_val), "host") != 0) {
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *sess_val = json_object_get(resp, "session");
|
||||
if (!json_is_string(sess_val)) {
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
const char *session = json_string_value(sess_val);
|
||||
|
||||
json_t *cid_val = json_object_get(resp, "clientId");
|
||||
const char *clientId = json_is_string(cid_val) ? json_string_value(cid_val) : NULL;
|
||||
|
||||
json_t *data_val = json_object_get(resp, "data");
|
||||
json_t *data_obj = NULL;
|
||||
if (json_is_object(data_val)) {
|
||||
data_obj = data_val;
|
||||
json_incref(data_obj);
|
||||
}
|
||||
|
||||
api->session_id = strdup(session);
|
||||
if (!api->session_id) {
|
||||
if (data_obj) json_decref(data_obj);
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
|
||||
if (out_session) {
|
||||
*out_session = strdup(session);
|
||||
}
|
||||
if (out_clientId && clientId) {
|
||||
*out_clientId = strdup(clientId);
|
||||
}
|
||||
if (out_data) {
|
||||
*out_data = data_obj;
|
||||
} else if (data_obj) {
|
||||
json_decref(data_obj);
|
||||
}
|
||||
|
||||
json_decref(resp);
|
||||
|
||||
rc = start_recv_thread(api);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return MP_API_OK;
|
||||
}
|
||||
|
||||
int mp_api_list(MultiplayerApi *api, json_t **out_list)
|
||||
{
|
||||
if (!api || !out_list) return MP_API_ERR_ARGUMENT;
|
||||
|
||||
int rc = ensure_connected(api);
|
||||
if (rc != MP_API_OK) return rc;
|
||||
|
||||
json_t *root = json_object();
|
||||
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"));
|
||||
|
||||
rc = send_json_line(api, root);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *line = NULL;
|
||||
rc = read_line(api->sockfd, &line);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
printf("Received line: %s\n", line); // Debug print
|
||||
|
||||
json_error_t jerr;
|
||||
json_t *resp = json_loads(line, 0, &jerr);
|
||||
free(line);
|
||||
if (!resp || !json_is_object(resp)) {
|
||||
if (resp) json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *cmd_val = json_object_get(resp, "cmd");
|
||||
if (!json_is_string(cmd_val) || strcmp(json_string_value(cmd_val), "list") != 0) {
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *list_val = json_object_get(resp, "data");
|
||||
if (!json_is_object(list_val)) {
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *list_obj = json_object_get(list_val, "list");
|
||||
if (!json_is_array(list_obj)) {
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
*out_list = list_obj;
|
||||
json_incref(*out_list);
|
||||
|
||||
json_decref(resp);
|
||||
return MP_API_OK;
|
||||
}
|
||||
|
||||
int mp_api_join(MultiplayerApi *api,
|
||||
const char *sessionId,
|
||||
json_t *data,
|
||||
char **out_session,
|
||||
char **out_clientId,
|
||||
json_t **out_data) {
|
||||
if (!api || !sessionId) return MP_API_ERR_ARGUMENT;
|
||||
if (api->session_id) return MP_API_ERR_STATE;
|
||||
|
||||
int rc = ensure_connected(api);
|
||||
if (rc != MP_API_OK) return rc;
|
||||
|
||||
json_t *root = json_object();
|
||||
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, "cmd", json_string("join"));
|
||||
|
||||
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);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *line = NULL;
|
||||
rc = read_line(api->sockfd, &line);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
json_error_t jerr;
|
||||
json_t *resp = json_loads(line, 0, &jerr);
|
||||
free(line);
|
||||
if (!resp || !json_is_object(resp)) {
|
||||
if (resp) json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *cmd_val = json_object_get(resp, "cmd");
|
||||
if (!json_is_string(cmd_val) || strcmp(json_string_value(cmd_val), "join") != 0) {
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_PROTOCOL;
|
||||
}
|
||||
|
||||
json_t *sess_val = json_object_get(resp, "session");
|
||||
const char *session = NULL;
|
||||
if (json_is_string(sess_val)) {
|
||||
session = json_string_value(sess_val);
|
||||
}
|
||||
|
||||
json_t *cid_val = json_object_get(resp, "clientId");
|
||||
const char *clientId = json_is_string(cid_val) ? json_string_value(cid_val) : NULL;
|
||||
|
||||
json_t *data_val = json_object_get(resp, "data");
|
||||
json_t *data_obj = NULL;
|
||||
if (json_is_object(data_val)) {
|
||||
data_obj = data_val;
|
||||
json_incref(data_obj);
|
||||
}
|
||||
|
||||
int joinAccepted = 1;
|
||||
if (data_obj) {
|
||||
json_t *status_val = json_object_get(data_obj, "status");
|
||||
if (json_is_string(status_val) &&
|
||||
strcmp(json_string_value(status_val), "error") == 0) {
|
||||
joinAccepted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (joinAccepted && session) {
|
||||
api->session_id = strdup(session);
|
||||
if (!api->session_id) {
|
||||
if (data_obj) json_decref(data_obj);
|
||||
json_decref(resp);
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_session && session) {
|
||||
*out_session = strdup(session);
|
||||
}
|
||||
if (out_clientId && clientId) {
|
||||
*out_clientId = strdup(clientId);
|
||||
}
|
||||
if (out_data) {
|
||||
*out_data = data_obj;
|
||||
} else if (data_obj) {
|
||||
json_decref(data_obj);
|
||||
}
|
||||
|
||||
json_decref(resp);
|
||||
|
||||
if (joinAccepted && api->session_id) {
|
||||
rc = start_recv_thread(api);
|
||||
if (rc != MP_API_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return joinAccepted ? MP_API_OK : MP_API_ERR_REJECTED;
|
||||
}
|
||||
|
||||
int mp_api_game(MultiplayerApi *api, json_t *data, const char* destination) {
|
||||
if (!api || !data) return MP_API_ERR_ARGUMENT;
|
||||
if (api->sockfd < 0 || !api->session_id) return MP_API_ERR_STATE;
|
||||
|
||||
json_t *root = json_object();
|
||||
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, "cmd", json_string("game"));
|
||||
|
||||
if(destination)
|
||||
json_object_set_new(root, "destination", json_string(destination));
|
||||
|
||||
json_t *data_copy;
|
||||
if (json_is_object(data)) {
|
||||
data_copy = json_deep_copy(data);
|
||||
} else {
|
||||
data_copy = json_object();
|
||||
}
|
||||
json_object_set_new(root, "data", data_copy);
|
||||
|
||||
return send_json_line(api, root);
|
||||
}
|
||||
|
||||
int mp_api_listen(MultiplayerApi *api,
|
||||
MultiplayerListener cb,
|
||||
void *context) {
|
||||
if (!api || !cb) return -1;
|
||||
|
||||
ListenerNode *node = (ListenerNode *)malloc(sizeof(ListenerNode));
|
||||
if (!node) return -1;
|
||||
|
||||
node->cb = cb;
|
||||
node->context = context;
|
||||
|
||||
pthread_mutex_lock(&api->lock);
|
||||
node->id = api->next_listener_id++;
|
||||
node->next = api->listeners;
|
||||
api->listeners = node;
|
||||
pthread_mutex_unlock(&api->lock);
|
||||
|
||||
return node->id;
|
||||
}
|
||||
|
||||
void mp_api_unlisten(MultiplayerApi *api, int listener_id) {
|
||||
if (!api || listener_id <= 0) return;
|
||||
|
||||
pthread_mutex_lock(&api->lock);
|
||||
ListenerNode *prev = NULL;
|
||||
ListenerNode *cur = api->listeners;
|
||||
while (cur) {
|
||||
if (cur->id == listener_id) {
|
||||
if (prev) {
|
||||
prev->next = cur->next;
|
||||
} else {
|
||||
api->listeners = cur->next;
|
||||
}
|
||||
free(cur);
|
||||
break;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
pthread_mutex_unlock(&api->lock);
|
||||
}
|
||||
|
||||
/* --- Interna hjälpfunktioner --- */
|
||||
|
||||
static int connect_to_server(const char *host, uint16_t port) {
|
||||
if (!host) host = "127.0.0.1";
|
||||
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%u", (unsigned int)port);
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC; /* IPv4 eller IPv6 */
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
struct addrinfo *res = NULL;
|
||||
int err = getaddrinfo(host, port_str, &hints, &res);
|
||||
if (err != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
for (struct addrinfo *rp = res; rp != NULL; rp = rp->ai_next) {
|
||||
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (fd == -1) continue;
|
||||
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) {
|
||||
break;
|
||||
}
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int ensure_connected(MultiplayerApi *api) {
|
||||
if (!api) return MP_API_ERR_ARGUMENT;
|
||||
if (api->sockfd >= 0) return MP_API_OK;
|
||||
|
||||
int fd = connect_to_server(api->server_host, api->server_port);
|
||||
if (fd < 0) {
|
||||
return MP_API_ERR_CONNECT;
|
||||
}
|
||||
api->sockfd = fd;
|
||||
return MP_API_OK;
|
||||
}
|
||||
|
||||
static int send_all(int fd, const char *buf, size_t len) {
|
||||
size_t sent = 0;
|
||||
while (sent < len) {
|
||||
ssize_t n = send(fd, buf + sent, len - sent, 0);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
return -1;
|
||||
}
|
||||
sent += (size_t)n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_json_line(MultiplayerApi *api, json_t *obj) {
|
||||
if (!api || api->sockfd < 0 || !obj) return MP_API_ERR_ARGUMENT;
|
||||
|
||||
char *text = json_dumps(obj, JSON_COMPACT);
|
||||
if (!text) {
|
||||
json_decref(obj);
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
|
||||
printf("Sending JSON: %s\n", text); // Debug print
|
||||
|
||||
size_t len = strlen(text);
|
||||
int fd = api->sockfd;
|
||||
|
||||
int rc = 0;
|
||||
if (send_all(fd, text, len) != 0 || send_all(fd, "\n", 1) != 0) {
|
||||
rc = MP_API_ERR_IO;
|
||||
}
|
||||
|
||||
free(text);
|
||||
json_decref(obj);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int read_line(int fd, char **out_line) {
|
||||
if (!out_line) return MP_API_ERR_ARGUMENT;
|
||||
|
||||
size_t cap = 256;
|
||||
size_t len = 0;
|
||||
char *buf = (char *)malloc(cap);
|
||||
if (!buf) return MP_API_ERR_IO;
|
||||
|
||||
for (;;) {
|
||||
char c;
|
||||
ssize_t n = recv(fd, &c, 1, 0);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
free(buf);
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
if (n == 0) {
|
||||
free(buf);
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len + 1 >= cap) {
|
||||
cap *= 2;
|
||||
char *tmp = (char *)realloc(buf, cap);
|
||||
if (!tmp) {
|
||||
free(buf);
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
buf = tmp;
|
||||
}
|
||||
|
||||
buf[len++] = c;
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
*out_line = buf;
|
||||
return MP_API_OK;
|
||||
}
|
||||
|
||||
static void process_line(MultiplayerApi *api, const char *line) {
|
||||
if (!api || !line || !*line) return;
|
||||
|
||||
json_error_t jerr;
|
||||
json_t *root = json_loads(line, 0, &jerr);
|
||||
if (!root || !json_is_object(root)) {
|
||||
if (root) json_decref(root);
|
||||
return;
|
||||
}
|
||||
|
||||
json_t *cmd_val = json_object_get(root, "cmd");
|
||||
if (!json_is_string(cmd_val)) {
|
||||
json_decref(root);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *cmd = json_string_value(cmd_val);
|
||||
if (!cmd) {
|
||||
json_decref(root);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(cmd, "joined") != 0 &&
|
||||
strcmp(cmd, "leaved") != 0 &&
|
||||
strcmp(cmd, "game") != 0) {
|
||||
json_decref(root);
|
||||
return;
|
||||
}
|
||||
|
||||
json_int_t msgId = 0;
|
||||
json_t *mid_val = json_object_get(root, "messageId");
|
||||
if (json_is_integer(mid_val)) {
|
||||
msgId = json_integer_value(mid_val);
|
||||
}
|
||||
|
||||
const char *clientId = NULL;
|
||||
json_t *cid_val = json_object_get(root, "clientId");
|
||||
if (json_is_string(cid_val)) {
|
||||
clientId = json_string_value(cid_val);
|
||||
}
|
||||
|
||||
json_t *data_val = json_object_get(root, "data");
|
||||
json_t *data_obj;
|
||||
if (json_is_object(data_val)) {
|
||||
data_obj = data_val;
|
||||
json_incref(data_obj);
|
||||
} else {
|
||||
data_obj = json_object();
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&api->lock);
|
||||
int count = 0;
|
||||
ListenerNode *node = api->listeners;
|
||||
while (node) {
|
||||
if (node->cb) count++;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
pthread_mutex_unlock(&api->lock);
|
||||
json_decref(data_obj);
|
||||
json_decref(root);
|
||||
return;
|
||||
}
|
||||
|
||||
ListenerSnapshot *snapshot = (ListenerSnapshot *)malloc(sizeof(ListenerSnapshot) * count);
|
||||
if (!snapshot) {
|
||||
pthread_mutex_unlock(&api->lock);
|
||||
json_decref(data_obj);
|
||||
json_decref(root);
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
node = api->listeners;
|
||||
while (node) {
|
||||
if (node->cb) {
|
||||
snapshot[idx].cb = node->cb;
|
||||
snapshot[idx].context = node->context;
|
||||
idx++;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
pthread_mutex_unlock(&api->lock);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
snapshot[i].cb(cmd, (int64_t)msgId, clientId, data_obj, snapshot[i].context);
|
||||
}
|
||||
|
||||
free(snapshot);
|
||||
json_decref(data_obj);
|
||||
json_decref(root);
|
||||
}
|
||||
|
||||
static void *recv_thread_main(void *arg) {
|
||||
MultiplayerApi *api = (MultiplayerApi *)arg;
|
||||
char buffer[1024];
|
||||
char *acc = NULL;
|
||||
size_t acc_len = 0;
|
||||
size_t acc_cap = 0;
|
||||
|
||||
while (1) {
|
||||
ssize_t n = recv(api->sockfd, buffer, sizeof(buffer), 0);
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < n; ++i) {
|
||||
char ch = buffer[i];
|
||||
if (ch == '\n') {
|
||||
if (acc_len > 0) {
|
||||
char *line = (char *)malloc(acc_len + 1);
|
||||
if (!line) {
|
||||
acc_len = 0;
|
||||
continue;
|
||||
}
|
||||
memcpy(line, acc, acc_len);
|
||||
line[acc_len] = '\0';
|
||||
|
||||
acc_len = 0;
|
||||
process_line(api, line);
|
||||
free(line);
|
||||
} else {
|
||||
acc_len = 0;
|
||||
}
|
||||
} else {
|
||||
if (acc_len + 1 >= acc_cap) {
|
||||
size_t new_cap = acc_cap == 0 ? 256 : acc_cap * 2;
|
||||
char *tmp = (char *)realloc(acc, new_cap);
|
||||
if (!tmp) {
|
||||
free(acc);
|
||||
acc = NULL;
|
||||
acc_len = 0;
|
||||
acc_cap = 0;
|
||||
break;
|
||||
}
|
||||
acc = tmp;
|
||||
acc_cap = new_cap;
|
||||
}
|
||||
acc[acc_len++] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (acc) {
|
||||
free(acc);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int start_recv_thread(MultiplayerApi *api) {
|
||||
if (!api) return MP_API_ERR_ARGUMENT;
|
||||
if (api->recv_thread_started) {
|
||||
return MP_API_OK;
|
||||
}
|
||||
|
||||
api->running = 1;
|
||||
int rc = pthread_create(&api->recv_thread, NULL, recv_thread_main, api);
|
||||
if (rc != 0) {
|
||||
api->running = 0;
|
||||
return MP_API_ERR_IO;
|
||||
}
|
||||
|
||||
api->recv_thread_started = 1;
|
||||
return MP_API_OK;
|
||||
}
|
||||
81
src/game/session/multiplayer_api.h
Normal file
81
src/game/session/multiplayer_api.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#ifndef MULTIPLAYER_API_H
|
||||
#define MULTIPLAYER_API_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "third_party/jansson/jansson.h"
|
||||
|
||||
typedef struct MultiplayerApi MultiplayerApi;
|
||||
|
||||
/* Callback‑typ för inkommande events från servern. */
|
||||
typedef void (*MultiplayerListener)(
|
||||
const char *event, /* "joined", "leaved", "game" */
|
||||
int64_t messageId, /* sekventiellt meddelande‑ID (från host) */
|
||||
const char *clientId, /* avsändarens klient‑ID (eller NULL) */
|
||||
json_t *data, /* JSON‑objekt med godtycklig speldata */
|
||||
void *context /* godtycklig pekare som skickas vidare */
|
||||
);
|
||||
|
||||
/* Returkoder */
|
||||
enum {
|
||||
MP_API_OK = 0,
|
||||
MP_API_ERR_ARGUMENT = 1,
|
||||
MP_API_ERR_STATE = 2,
|
||||
MP_API_ERR_CONNECT = 3,
|
||||
MP_API_ERR_PROTOCOL = 4,
|
||||
MP_API_ERR_IO = 5,
|
||||
MP_API_ERR_REJECTED = 6 /* t.ex. ogiltigt sessions‑ID vid join */
|
||||
};
|
||||
|
||||
/* Skapar en ny API‑instans. Returnerar NULL vid fel. */
|
||||
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. */
|
||||
void mp_api_destroy(MultiplayerApi *api);
|
||||
|
||||
/* Hostar en ny session. Blockerar tills svar erhållits eller fel uppstår.
|
||||
out_session / out_clientId pekar på nyallokerade strängar (malloc) som
|
||||
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). */
|
||||
int mp_api_host(MultiplayerApi *api,
|
||||
json_t *data,
|
||||
char **out_session,
|
||||
char **out_clientId,
|
||||
json_t **out_data);
|
||||
|
||||
/*
|
||||
Hämtar en lista över tillgängliga publika sessioner.
|
||||
Returnerar MP_API_OK vid framgång, annan felkod vid fel.
|
||||
Anroparen ansvarar för att json_decref:a out_list när klar. */
|
||||
int mp_api_list(MultiplayerApi *api,
|
||||
json_t **out_list);
|
||||
|
||||
/* Går med i befintlig session.
|
||||
sessionId: sessionskod (t.ex. "ABC123").
|
||||
data: valfri JSON‑payload med spelarinformation (kan vara NULL).
|
||||
out_* fungerar som i mp_api_host.
|
||||
|
||||
Returnerar:
|
||||
- MP_API_OK vid lyckad join
|
||||
- MP_API_ERR_REJECTED om servern svarar med status:error (t.ex. ogiltigt ID)
|
||||
- annan felkod vid nätverks/protokoll‑fel.
|
||||
*/
|
||||
int mp_api_join(MultiplayerApi *api,
|
||||
const char *sessionId,
|
||||
json_t *data,
|
||||
char **out_session,
|
||||
char **out_clientId,
|
||||
json_t **out_data);
|
||||
|
||||
/* Skickar ett "game"‑meddelande med godtycklig JSON‑data till sessionen. */
|
||||
int mp_api_game(MultiplayerApi *api, json_t *data, const char* destination);
|
||||
|
||||
/* Registrerar en lyssnare för inkommande events.
|
||||
Returnerar ett positivt listener‑ID, eller −1 vid fel. */
|
||||
int mp_api_listen(MultiplayerApi *api,
|
||||
MultiplayerListener cb,
|
||||
void *context);
|
||||
|
||||
/* Avregistrerar lyssnare. Listener‑ID är värdet från mp_api_listen. */
|
||||
void mp_api_unlisten(MultiplayerApi *api, int listener_id);
|
||||
|
||||
#endif /* MULTIPLAYER_API_H */
|
||||
257
src/game/session/networking.c
Normal file
257
src/game/session/networking.c
Normal file
@@ -0,0 +1,257 @@
|
||||
#include "networking.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define ONVO_IDENTIFIER "c2438167-831b-4bf7-8bdc-abcdefabcd00"
|
||||
|
||||
static void listen_callback(
|
||||
const char *event, /* "joined", "leaved", "game" */
|
||||
int64_t messsage_id, /* sekventiellt meddelande‑ID (från host) */
|
||||
const char *client_id, /* avsändarens klient‑ID (eller NULL) */
|
||||
json_t *data, /* JSON‑objekt med godtycklig speldata */
|
||||
void *context /* godtycklig pekare som skickas vidare */
|
||||
) {
|
||||
Game_Networking *networking = (Game_Networking *)context;
|
||||
assert(networking != NULL);
|
||||
assert(networking->game_session != NULL);
|
||||
|
||||
(void)data;
|
||||
|
||||
printf("#%li :: Event: '%s' from '%s'.\n", messsage_id, event, client_id);
|
||||
|
||||
assert(networking != NULL);
|
||||
|
||||
if(strcmp(event, "joined") == 0) {
|
||||
// Create a new player in the session, get the id and store it as the value in the
|
||||
// 'client_id_to_player_index' hashmap using 'client_id' as the key.
|
||||
|
||||
uint32_t *existing_id = map_get(&networking->client_id_to_player_index, client_id);
|
||||
assert(existing_id == NULL); // Something has gone terribly wrong if we get a 'joined' event for the same client-id with them still being in the map.
|
||||
|
||||
uint16_t player_index = 0xFFFF;
|
||||
assert(game_session_create_player(networking->game_session, &player_index));
|
||||
assert(map_set(&networking->client_id_to_player_index, client_id, player_index) == 0); // 0 = OK.
|
||||
|
||||
// TODO: SS - Send a 'game' message to the joinee with information about the session?
|
||||
|
||||
printf("Added client-id '%s' (player-index: %u) to the map.\n", client_id, player_index);
|
||||
}
|
||||
else if(strcmp(event, "leaved") == 0) {
|
||||
// TODO: SS - Index the hashmap using 'client_id' as the key and attempt to get the
|
||||
// player-index for that client. Run 'game_session_destroy_player()'.
|
||||
|
||||
uint32_t *existing_id = map_get(&networking->client_id_to_player_index, client_id);
|
||||
assert(existing_id != NULL); // Something has gone terribly wrong if we get a 'leaved' event for the a client-id that isn't in the map.
|
||||
|
||||
uint16_t player_id_to_remove = *existing_id;
|
||||
|
||||
game_session_destroy_player(networking->game_session, player_id_to_remove); // Returns void but crashes internally if id isn't OK.
|
||||
map_remove(&networking->client_id_to_player_index, client_id);
|
||||
|
||||
printf("Removed client-id '%s' (player-index: %u) from the map.\n", client_id, player_id_to_remove);
|
||||
}
|
||||
else if(strcmp(event, "game") == 0) {
|
||||
// TODO: SS - Check the 'data' to see what type of game-event it is.
|
||||
// Possible game-events I can think of right now:
|
||||
// - Player-input.
|
||||
// - Match-state changed.
|
||||
// - Chat-message?
|
||||
// - Session-state?
|
||||
// - Maybe Player-Joined/Left. (seems like mp_api only sends 'joined'/'leaved' events to the host.)
|
||||
}
|
||||
else {
|
||||
printf("UNKNOWN EVENT-TYPE: '%s'.\n", event);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool start_listening(Game_Networking *networking) {
|
||||
assert(networking != NULL);
|
||||
assert(networking->api != NULL);
|
||||
|
||||
int listener_id = mp_api_listen(networking->api, listen_callback, (void *)networking); // TODO: SS - Change context.
|
||||
if(listener_id < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
networking->listener_id = (uint32_t)listener_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool stop_listening(Game_Networking *networking) {
|
||||
assert(networking != NULL);
|
||||
assert(networking->api != NULL);
|
||||
|
||||
mp_api_unlisten(networking->api, (int)networking->listener_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool networking_try_host(Game_Networking *networking, Game_Session *session, Game_Session_Settings settings, uint16_t *out_host_player_id) {
|
||||
assert(networking != NULL);
|
||||
assert(session != 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);
|
||||
// TODO: SS - Shutdown API.
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Started hosting session '%s', local_client_id is '%s'.\n", session_id, local_client_id);
|
||||
networking->is_host = true;
|
||||
networking->game_session = session;
|
||||
networking->session_id = session_id;
|
||||
networking->local_client_id = local_client_id;
|
||||
|
||||
map_init(&networking->client_id_to_player_index);
|
||||
|
||||
assert(start_listening(networking));
|
||||
|
||||
// Set up session.
|
||||
game_session_init(
|
||||
session,
|
||||
settings
|
||||
);
|
||||
|
||||
// Add the host to the game.
|
||||
uint16_t host_player_index = 0xFFFF;
|
||||
assert(game_session_create_player(networking->game_session, &host_player_index));
|
||||
map_set(&networking->client_id_to_player_index, local_client_id, host_player_index);
|
||||
*out_host_player_id = host_player_index;
|
||||
printf("Host-player created (client-id: '%s'). Player-id: %u.\n", local_client_id, host_player_index);
|
||||
|
||||
if(response_data != NULL) {
|
||||
// TODO: SS - Handle JSON-response.
|
||||
json_decref(response_data);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool networking_try_join(Game_Networking *networking, Game_Session *session, const char *session_id) {
|
||||
assert(networking != NULL);
|
||||
assert(session != NULL);
|
||||
|
||||
if(strlen(session_id) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!setup_api(networking)) {
|
||||
printf("Failed to join; API is already set up.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Trying to join session using code: '%s' ...\n", session_id);
|
||||
|
||||
char *result_session_id;
|
||||
char *local_client_id;
|
||||
json_t *received_json;
|
||||
int join_result = mp_api_join(
|
||||
networking->api, // API.
|
||||
session_id, // Session-code.
|
||||
NULL, // JSON-data to send. // TODO: SS - Send information about you as a player (username, ..).
|
||||
&result_session_id, // Received Session-id. I assume it should be the same as 'session_id'. // TODO: SS - Ask Robin why mp_api_join() returns a session_id. Is it to support 'abcdef' => 'ABCDEF' and/or proxies?
|
||||
&local_client_id, // Received client-id.
|
||||
&received_json // Received JSON-data.
|
||||
);
|
||||
if(join_result != MP_API_OK) {
|
||||
// TODO: SS - Shutdown API.
|
||||
printf("Failed to join; Got result: %i.\n", join_result);
|
||||
return false;
|
||||
}
|
||||
assert(strcmp(result_session_id, session_id) == 0);
|
||||
|
||||
printf("Joined session '%s', local_client_id is '%s'.\n", result_session_id, local_client_id);
|
||||
networking->is_host = true;
|
||||
networking->game_session = session;
|
||||
networking->session_id = result_session_id;
|
||||
networking->local_client_id = local_client_id;
|
||||
|
||||
map_init(&networking->client_id_to_player_index);
|
||||
|
||||
// TODO: SS - Decide how to network this..
|
||||
// Should we just receive the session's settings as well as a long list of commands of what has happened throughout the match (so far) - at least one per tick?
|
||||
// Or, should this client receive the current state of the session?
|
||||
|
||||
if(received_json != NULL) {
|
||||
// TODO: SS - Read this json because it might contain valuable information.
|
||||
json_decref(received_json);
|
||||
}
|
||||
|
||||
// TODO: SS - Stop listening.
|
||||
assert(start_listening(networking));
|
||||
|
||||
// TODO: SS - Set up session based on what we got in the json.
|
||||
// game_session_init(
|
||||
// session,
|
||||
// settings
|
||||
// );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
map_deinit(&networking->client_id_to_player_index);
|
||||
|
||||
assert(stop_listening(networking));
|
||||
|
||||
mp_api_destroy(networking->api);
|
||||
networking->api = NULL;
|
||||
networking->game_session = NULL;
|
||||
networking->is_host = false;
|
||||
|
||||
if(networking->session_id != NULL) {
|
||||
free(networking->session_id);
|
||||
networking->session_id = NULL;
|
||||
}
|
||||
if(networking->local_client_id != NULL) {
|
||||
free(networking->local_client_id);
|
||||
networking->local_client_id = NULL;
|
||||
}
|
||||
|
||||
printf("Stopped hosting.\n");
|
||||
return true;
|
||||
}
|
||||
28
src/game/session/networking.h
Normal file
28
src/game/session/networking.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef NETWORKING_H
|
||||
#define NETWORKING_H
|
||||
|
||||
#include "multiplayer_api.h"
|
||||
#include "game_session.h"
|
||||
|
||||
#include "shared/map.h"
|
||||
|
||||
typedef struct {
|
||||
MultiplayerApi *api;
|
||||
|
||||
bool is_host;
|
||||
|
||||
char *session_id;
|
||||
char *local_client_id;
|
||||
|
||||
uint32_t listener_id;
|
||||
|
||||
Game_Session *game_session; // Comes from Game_Instance.
|
||||
map_uint_t client_id_to_player_index;
|
||||
} Game_Networking;
|
||||
|
||||
bool networking_try_host(Game_Networking *networking, Game_Session *session, Game_Session_Settings settings, uint16_t *out_host_player_id);
|
||||
bool networking_try_join(Game_Networking *networking, Game_Session *session, const char *session_id);
|
||||
|
||||
bool networking_stop_hosting(Game_Networking *networking);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user