host progress
This commit is contained in:
parent
a80eff9e39
commit
627f736345
40
src/app.c
40
src/app.c
@ -23,6 +23,7 @@
|
|||||||
#include "phys.h"
|
#include "phys.h"
|
||||||
#include "rng.h"
|
#include "rng.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
|
#include "host.h"
|
||||||
|
|
||||||
struct exit_callback {
|
struct exit_callback {
|
||||||
app_exit_callback_func *func;
|
app_exit_callback_func *func;
|
||||||
@ -123,45 +124,6 @@ void app_register_exit_callback(app_exit_callback_func *func)
|
|||||||
* Entry point
|
* Entry point
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
INTERNAL SOCK_RECEIVE_CALLBACK_FUNC_DEF(server_recv, address, msg)
|
|
||||||
{
|
|
||||||
(UNUSED)address;
|
|
||||||
(UNUSED)msg;
|
|
||||||
DEBUGBREAKABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
INTERNAL SOCK_RECEIVE_CALLBACK_FUNC_DEF(client_recv, address, msg)
|
|
||||||
{
|
|
||||||
(UNUSED)address;
|
|
||||||
(UNUSED)msg;
|
|
||||||
DEBUGBREAKABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void app_entry_point(void)
|
void app_entry_point(void)
|
||||||
{
|
{
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
|||||||
25
src/client.c
25
src/client.c
@ -1,5 +1,7 @@
|
|||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
|
#include "arena.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#define CHANNEL_LOOKUP_BUCKETS 4096
|
#define CHANNEL_LOOKUP_BUCKETS 4096
|
||||||
|
|
||||||
@ -19,12 +21,12 @@ READONLY struct client_store _g_client_store_nil = { .valid = false };
|
|||||||
|
|
||||||
struct client_store *client_store_alloc(void)
|
struct client_store *client_store_alloc(void)
|
||||||
{
|
{
|
||||||
struct arena arena = ARENA_ALLOC(GIGABYTE(64));
|
struct arena arena = arena_alloc(GIGABYTE(64));
|
||||||
struct client_store *store = arena_push_zero(&arena, struct client_store);
|
struct client_store *store = arena_push_zero(&arena, struct client_store);
|
||||||
store->arena = arena;
|
store->arena = arena;
|
||||||
store->num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS;
|
store->num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS;
|
||||||
store->channel_lookup_buckets = arena_push_array_zero(&arena, struct channel_lookup_bucket, store->num_channel_lookup_buckets);
|
store->channel_lookup_buckets = arena_push_array_zero(&arena, struct channel_lookup_bucket, store->num_channel_lookup_buckets);
|
||||||
store->clients = arena_dry_push(store, struct client);
|
store->clients = arena_dry_push(&arena, struct client);
|
||||||
return store;
|
return store;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +51,11 @@ struct client_store *client_store_from_client(struct client *client)
|
|||||||
* Client
|
* Client
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
|
||||||
|
{
|
||||||
|
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
|
||||||
|
}
|
||||||
|
|
||||||
struct client *client_from_handle(struct client_store *store, struct client_handle handle)
|
struct client *client_from_handle(struct client_store *store, struct client_handle handle)
|
||||||
{
|
{
|
||||||
if (handle.gen != 0 && handle.idx < store->clients_reserved) {
|
if (handle.gen != 0 && handle.idx < store->clients_reserved) {
|
||||||
@ -65,7 +72,7 @@ struct client *client_from_channel_id(struct client_store *store, struct host_ch
|
|||||||
struct client *res = client_nil();
|
struct client *res = client_nil();
|
||||||
u64 channel_hash = hash_from_channel_id(channel_id);
|
u64 channel_hash = hash_from_channel_id(channel_id);
|
||||||
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
||||||
struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[index];
|
struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
||||||
for (struct client *client = bucket->first; client; client = client->next_hash) {
|
for (struct client *client = bucket->first; client; client = client->next_hash) {
|
||||||
if (client->channel_hash == channel_hash) {
|
if (client->channel_hash == channel_hash) {
|
||||||
res = client;
|
res = client;
|
||||||
@ -75,7 +82,7 @@ struct client *client_from_channel_id(struct client_store *store, struct host_ch
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct client client_alloc(struct client_store *store, struct host_channel_id channel_id)
|
struct client *client_alloc(struct client_store *store, struct host_channel_id channel_id)
|
||||||
{
|
{
|
||||||
struct client *client = NULL;
|
struct client *client = NULL;
|
||||||
struct client_handle handle = ZI;
|
struct client_handle handle = ZI;
|
||||||
@ -90,7 +97,7 @@ struct client client_alloc(struct client_store *store, struct host_channel_id ch
|
|||||||
handle.idx = store->clients_reserved;
|
handle.idx = store->clients_reserved;
|
||||||
++store->clients_reserved;
|
++store->clients_reserved;
|
||||||
}
|
}
|
||||||
client = _g_client_nil;
|
*client = _g_client_nil;
|
||||||
client->valid = true;
|
client->valid = true;
|
||||||
client->handle = handle;
|
client->handle = handle;
|
||||||
|
|
||||||
@ -100,7 +107,7 @@ struct client client_alloc(struct client_store *store, struct host_channel_id ch
|
|||||||
|
|
||||||
/* Insert into channel lookup */
|
/* Insert into channel lookup */
|
||||||
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
||||||
struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[index];
|
struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
||||||
if (bucket->last) {
|
if (bucket->last) {
|
||||||
bucket->last->next_hash = client;
|
bucket->last->next_hash = client;
|
||||||
client->prev_hash = bucket->last;
|
client->prev_hash = bucket->last;
|
||||||
@ -108,6 +115,7 @@ struct client client_alloc(struct client_store *store, struct host_channel_id ch
|
|||||||
bucket->first = client;
|
bucket->first = client;
|
||||||
}
|
}
|
||||||
bucket->last = client;
|
bucket->last = client;
|
||||||
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
void client_release(struct client *client)
|
void client_release(struct client *client)
|
||||||
@ -119,9 +127,8 @@ void client_release(struct client *client)
|
|||||||
store->first_free_client = client;
|
store->first_free_client = client;
|
||||||
|
|
||||||
/* Remove from channel lookup */
|
/* Remove from channel lookup */
|
||||||
u64 channel_hash = hash_from_channel_id(channel_id);
|
u64 bucket_index = client->channel_hash % store->num_channel_lookup_buckets;
|
||||||
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
||||||
struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[index];
|
|
||||||
struct client *prev = client->prev_hash;
|
struct client *prev = client->prev_hash;
|
||||||
struct client *next = client->next_hash;
|
struct client *next = client->next_hash;
|
||||||
if (prev) {
|
if (prev) {
|
||||||
|
|||||||
12
src/client.h
12
src/client.h
@ -9,7 +9,11 @@ struct client_handle {
|
|||||||
|
|
||||||
struct client {
|
struct client {
|
||||||
b32 valid;
|
b32 valid;
|
||||||
|
struct client_handle handle;
|
||||||
|
|
||||||
struct host_channel_id channel_id;
|
struct host_channel_id channel_id;
|
||||||
|
u64 channel_hash;
|
||||||
|
|
||||||
struct client *next_free;
|
struct client *next_free;
|
||||||
struct client *next_hash;
|
struct client *next_hash;
|
||||||
struct client *prev_hash;
|
struct client *prev_hash;
|
||||||
@ -23,12 +27,14 @@ struct channel_lookup_bucket {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct client_store {
|
struct client_store {
|
||||||
|
b32 valid;
|
||||||
struct arena arena;
|
struct arena arena;
|
||||||
|
|
||||||
struct channel_lookup_bucket channel_lookup_buckets;
|
struct channel_lookup_bucket *channel_lookup_buckets;
|
||||||
u64 num_channel_lookup_buckets;
|
u64 num_channel_lookup_buckets;
|
||||||
|
|
||||||
struct client *clients;
|
struct client *clients;
|
||||||
|
struct client *first_free_client;
|
||||||
u64 clients_reserved;
|
u64 clients_reserved;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,7 +44,7 @@ INLINE struct client *client_nil(void)
|
|||||||
return &_g_client_nil;
|
return &_g_client_nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE struct client *client_store_nil(void)
|
INLINE struct client_store *client_store_nil(void)
|
||||||
{
|
{
|
||||||
extern READONLY struct client_store _g_client_store_nil;
|
extern READONLY struct client_store _g_client_store_nil;
|
||||||
return &_g_client_store_nil;
|
return &_g_client_store_nil;
|
||||||
@ -49,7 +55,7 @@ void client_store_release(struct client_store *store);
|
|||||||
struct client_store *client_store_from_client(struct client *client);
|
struct client_store *client_store_from_client(struct client *client);
|
||||||
struct client *client_from_handle(struct client_store *store, struct client_handle handle);
|
struct client *client_from_handle(struct client_store *store, struct client_handle handle);
|
||||||
struct client *client_from_channel_id(struct client_store *store, struct host_channel_id channel_id);
|
struct client *client_from_channel_id(struct client_store *store, struct host_channel_id channel_id);
|
||||||
struct client client_alloc(struct client_store *store, struct host_channel_id channel_id);
|
struct client *client_alloc(struct client_store *store, struct host_channel_id channel_id);
|
||||||
void client_release(struct client *client);
|
void client_release(struct client *client);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -506,6 +506,11 @@ struct space_entry_handle {
|
|||||||
u64 gen;
|
u64 gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct host_channel_id {
|
||||||
|
u32 gen;
|
||||||
|
u32 idx;
|
||||||
|
};
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Tag structs
|
* Tag structs
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
#include "phys.h"
|
#include "phys.h"
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
enum entity_prop {
|
enum entity_prop {
|
||||||
ENTITY_PROP_NONE,
|
ENTITY_PROP_NONE,
|
||||||
@ -154,6 +155,12 @@ struct entity {
|
|||||||
/* ENTITY_PROP_MOUSE_JOINT */
|
/* ENTITY_PROP_MOUSE_JOINT */
|
||||||
struct phys_mouse_joint mouse_joint_data;
|
struct phys_mouse_joint mouse_joint_data;
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/* Player */
|
||||||
|
|
||||||
|
/* ENTITY_PROP_PLAYER_CONTROLLED */
|
||||||
|
struct client_handle controlling_client;
|
||||||
|
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/* Control */
|
/* Control */
|
||||||
|
|
||||||
|
|||||||
111
src/game.c
111
src/game.c
@ -16,6 +16,7 @@
|
|||||||
#include "space.h"
|
#include "space.h"
|
||||||
#include "byteio.h"
|
#include "byteio.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "host.h"
|
||||||
|
|
||||||
GLOBAL struct {
|
GLOBAL struct {
|
||||||
struct atomic_i32 game_thread_shutdown;
|
struct atomic_i32 game_thread_shutdown;
|
||||||
@ -68,14 +69,10 @@ struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr,
|
|||||||
(UNUSED)phys_sr;
|
(UNUSED)phys_sr;
|
||||||
(UNUSED)host_sr;
|
(UNUSED)host_sr;
|
||||||
|
|
||||||
/* Initialize game input storage */
|
|
||||||
G.game_input_mutex = sys_mutex_alloc();
|
|
||||||
G.game_input_arena = arena_alloc(GIGABYTE(64));
|
|
||||||
|
|
||||||
G.client_store = client_store_alloc();
|
G.client_store = client_store_alloc();
|
||||||
|
|
||||||
/* Intialize host */
|
/* Intialize host */
|
||||||
struct host_address_desc bind_address = HOST_ADDRESS_ALL_LOCAL_INTERFACES(12345);
|
struct sock_address bind_address = sock_address_from_any_local_interface_with_port(12345);
|
||||||
G.host = host_alloc(bind_address);
|
G.host = host_alloc(bind_address);
|
||||||
|
|
||||||
/* Initialize empty world */
|
/* Initialize empty world */
|
||||||
@ -119,7 +116,6 @@ INTERNAL void reset_world(void)
|
|||||||
|
|
||||||
/* Re-create world */
|
/* Re-create world */
|
||||||
world_alloc(&G.tick);
|
world_alloc(&G.tick);
|
||||||
G.tick.continuity_gen = atomic_u64_eval(&G.prev_tick_continuity_gen) + 1;
|
|
||||||
G.tick.timescale = GAME_TIMESCALE;
|
G.tick.timescale = GAME_TIMESCALE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +433,7 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, array)
|
|||||||
* Update
|
* Update
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL void game_update()
|
INTERNAL void game_update(void)
|
||||||
{
|
{
|
||||||
__prof;
|
__prof;
|
||||||
|
|
||||||
@ -535,7 +531,7 @@ INTERNAL void game_update()
|
|||||||
|
|
||||||
struct game_cmd_list game_cmds = ZI;
|
struct game_cmd_list game_cmds = ZI;
|
||||||
{
|
{
|
||||||
struct host_event_array host_events = host_events_pop(scratch.arena, G.host);
|
struct host_event_array host_events = host_pop_events(scratch.arena, &G.host);
|
||||||
game_cmds_from_host_events(scratch.arena, host_events, &game_cmds);
|
game_cmds_from_host_events(scratch.arena, host_events, &game_cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,10 +541,10 @@ INTERNAL void game_update()
|
|||||||
|
|
||||||
for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) {
|
for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) {
|
||||||
enum game_cmd_kind kind = cmd->kind;
|
enum game_cmd_kind kind = cmd->kind;
|
||||||
struct host_channel_id channel_id = cmd->client_channel_id;
|
struct host_channel_id channel_id = cmd->channel_id;
|
||||||
|
|
||||||
struct client *client = client_from_channel_id(channel_id);
|
struct client *client = client_from_channel_id(G.client_store, channel_id);
|
||||||
if (client->valid || channel_id == HOST_CHANNEL_ID_NIL) {
|
if (client->valid || host_channel_id_is_nil(channel_id)) {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
/* Cursor */
|
/* Cursor */
|
||||||
case GAME_CMD_KIND_CURSOR_MOVE:
|
case GAME_CMD_KIND_CURSOR_MOVE:
|
||||||
@ -573,9 +569,9 @@ INTERNAL void game_update()
|
|||||||
case GAME_CMD_KIND_CLIENT_DISCONNECT:
|
case GAME_CMD_KIND_CLIENT_DISCONNECT:
|
||||||
{
|
{
|
||||||
if (client->valid) {
|
if (client->valid) {
|
||||||
struct entity *client_ent = entity_from_handle(client->ent);
|
struct entity *client_ent = entity_from_handle(entity_store, client->ent);
|
||||||
if (client_ent->valid) {
|
if (client_ent->valid) {
|
||||||
entity_disable_prop(client_ent, ENTITY_PROP_CLIENT_CONTROLLED);
|
entity_disable_prop(client_ent, ENTITY_PROP_PLAYER_CONTROLLED);
|
||||||
entity_enable_prop(client_ent, ENTITY_PROP_RELEASE_NEXT_TICK);
|
entity_enable_prop(client_ent, ENTITY_PROP_RELEASE_NEXT_TICK);
|
||||||
host_queue_disconnect(&G.host, channel_id);
|
host_queue_disconnect(&G.host, channel_id);
|
||||||
}
|
}
|
||||||
@ -585,12 +581,12 @@ INTERNAL void game_update()
|
|||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
};
|
};
|
||||||
} else if (kind == GAME_CMD_KIND_CLIENT_CONNECT && channel_id != HOST_CHANNEL_ID_NIL && !client->valid) {
|
} else if (kind == GAME_CMD_KIND_CLIENT_CONNECT && !host_channel_id_is_nil(channel_id) && !client->valid) {
|
||||||
/* Connect client */
|
/* Connect client */
|
||||||
struct client *client = client_alloc(G.client_store, channel_id);
|
client = client_alloc(G.client_store, channel_id);
|
||||||
struct client_ent *client_ent = entity_alloc(root);
|
struct entity *client_ent = entity_alloc(root);
|
||||||
entity_enable_prop(client_ent, ENTITY_PROP_CLIENT_CONTROLLED);
|
entity_enable_prop(client_ent, ENTITY_PROP_PLAYER_CONTROLLED);
|
||||||
client_ent->client_handle = client->handle;
|
client_ent->controlling_client = client->handle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1322,19 +1318,21 @@ INTERNAL void game_update()
|
|||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
|
|
||||||
/* TODO: Not like this */
|
/* TODO: Not like this */
|
||||||
struct game_event_desc snapshot_ed = ZI;
|
struct game_event snapshot_event = ZI;
|
||||||
snapshot_ed.kind = GAME_EVENT_KIND_SNAPSHOT_FULL;
|
snapshot_event.kind = GAME_EVENT_KIND_SNAPSHOT_FULL;
|
||||||
snapshot_ed.data = game_string_from_tick(&G.tick);
|
snapshot_event.snapshot_data = game_string_from_tick(temp.arena, &G.tick);
|
||||||
|
|
||||||
struct host_msg_desc msg = ZI;
|
struct game_event_list l = ZI;
|
||||||
msg.channel = HOST_CHANNEL_ALL;
|
l.first = &snapshot_event;
|
||||||
msg.data = snapshot;
|
l.last = &snapshot_event;
|
||||||
host_queue_write(G.host, msg);
|
struct string msg = game_string_from_events(temp.arena, l);
|
||||||
|
|
||||||
temp_arena_end(temp);
|
host_queue_write(&G.host, HOST_CHANNEL_ID_ALL, msg);
|
||||||
|
|
||||||
|
arena_temp_end(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
host_update(G.host);
|
host_update(&G.host);
|
||||||
__profframe("Game");
|
__profframe("Game");
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
@ -1354,7 +1352,7 @@ struct string game_string_from_cmds(struct arena *arena, struct game_cmd_list cm
|
|||||||
{
|
{
|
||||||
struct byte_writer bw = bw_from_arena(arena);
|
struct byte_writer bw = bw_from_arena(arena);
|
||||||
|
|
||||||
for (struct game_cmd *cmd = cmds->first; cmd; cmd = cmd->next) {
|
for (struct game_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
|
||||||
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
|
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
|
||||||
u64 start = bw_pos(&bw);
|
u64 start = bw_pos(&bw);
|
||||||
|
|
||||||
@ -1387,11 +1385,11 @@ struct string game_string_from_cmds(struct arena *arena, struct game_cmd_list cm
|
|||||||
return bw_get_written(&bw);
|
return bw_get_written(&bw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_cmd *cmds_out)
|
void game_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_cmd_list *cmds_out)
|
||||||
{
|
{
|
||||||
for (u64 i = 0; i < host_events.count; ++i) {
|
for (u64 i = 0; i < host_events.count; ++i) {
|
||||||
struct game_cmd *cmd = arena_push_zero(arena, struct game_cmd);
|
struct game_cmd *cmd = arena_push_zero(arena, struct game_cmd);
|
||||||
struct host_channel_event host_event = host_events.events[i];
|
struct host_event host_event = host_events.events[i];
|
||||||
enum host_event_kind host_event_kind = host_event.kind;
|
enum host_event_kind host_event_kind = host_event.kind;
|
||||||
cmd->channel_id = host_event.channel_id;
|
cmd->channel_id = host_event.channel_id;
|
||||||
switch (host_event_kind) {
|
switch (host_event_kind) {
|
||||||
@ -1408,7 +1406,7 @@ void game_cmds_from_host_events(struct arena *arena, struct host_event_array hos
|
|||||||
|
|
||||||
case HOST_EVENT_KIND_MSG:
|
case HOST_EVENT_KIND_MSG:
|
||||||
{
|
{
|
||||||
struct byte_reader br = br_from_buffer(event.msg);
|
struct byte_reader br = br_from_buffer(host_event.msg);
|
||||||
while (br_bytes_left(&br) > 0) {
|
while (br_bytes_left(&br) > 0) {
|
||||||
u64 cmd_size = br_read_u64(&br);
|
u64 cmd_size = br_read_u64(&br);
|
||||||
u64 cmd_pos_end = br_pos(&br) + cmd_size;
|
u64 cmd_pos_end = br_pos(&br) + cmd_size;
|
||||||
@ -1459,11 +1457,11 @@ void game_cmds_from_host_events(struct arena *arena, struct host_event_array hos
|
|||||||
struct string game_string_from_events(struct arena *arena, struct game_event_list events)
|
struct string game_string_from_events(struct arena *arena, struct game_event_list events)
|
||||||
{
|
{
|
||||||
struct byte_writer bw = bw_from_arena(arena);
|
struct byte_writer bw = bw_from_arena(arena);
|
||||||
for (struct game_cmd *cmd = cmds->first; cmd; cmd = cmd->next) {
|
for (struct game_event *event = events.first; event; event = event->next) {
|
||||||
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
|
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
|
||||||
u64 start = bw_pos(&bw);
|
u64 start = bw_pos(&bw);
|
||||||
|
|
||||||
switch (cmd->kind) {
|
switch (event->kind) {
|
||||||
case GAME_EVENT_KIND_SNAPSHOT_FULL:
|
case GAME_EVENT_KIND_SNAPSHOT_FULL:
|
||||||
{
|
{
|
||||||
} break;
|
} break;
|
||||||
@ -1477,34 +1475,34 @@ struct string game_string_from_events(struct arena *arena, struct game_event_lis
|
|||||||
return bw_get_written(&bw);
|
return bw_get_written(&bw);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_event *events_out)
|
void game_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_event_list *events_out)
|
||||||
{
|
{
|
||||||
for (u64 i = 0; i < host_events.count; ++i) {
|
for (u64 i = 0; i < host_events.count; ++i) {
|
||||||
struct game_event *event = arena_push_zero(arena, struct game_event);
|
struct game_event *game_event = arena_push_zero(arena, struct game_event);
|
||||||
struct host_channel_event host_event = host_events.events[i];
|
struct host_event host_event = host_events.events[i];
|
||||||
enum host_event_kind host_event_kind = host_event.kind;
|
enum host_event_kind host_event_kind = host_event.kind;
|
||||||
event->channel_id = host_event.channel_id;
|
game_event->channel_id = host_event.channel_id;
|
||||||
switch (host_event_kind) {
|
switch (host_event_kind) {
|
||||||
case HOST_EVENT_KIND_CHANNEL_OPENED:
|
case HOST_EVENT_KIND_CHANNEL_OPENED:
|
||||||
{
|
{
|
||||||
event->kind = GAME_EVENT_KIND_CONNECT;
|
game_event->kind = GAME_EVENT_KIND_CONNECT;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case HOST_EVENT_KIND_CHANNEL_CLOSED:
|
case HOST_EVENT_KIND_CHANNEL_CLOSED:
|
||||||
{
|
{
|
||||||
event->kind = GAME_EVENT_KIND_DISCONNECT;
|
game_event->kind = GAME_EVENT_KIND_DISCONNECT;
|
||||||
event->disconnect_reason = LIT("Connection lost");
|
game_event->disconnect_reason = LIT("Connection lost");
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case HOST_EVENT_KIND_MSG:
|
case HOST_EVENT_KIND_MSG:
|
||||||
{
|
{
|
||||||
struct byte_reader br = br_from_buffer(event.msg);
|
struct byte_reader br = br_from_buffer(host_event.msg);
|
||||||
while (br_bytes_left(&br) > 0) {
|
while (br_bytes_left(&br) > 0) {
|
||||||
u64 event_size = br_read_u64(&br);
|
u64 event_size = br_read_u64(&br);
|
||||||
u64 event_pos_end = br_pos(&br) + event_size;
|
u64 event_pos_end = br_pos(&br) + event_size;
|
||||||
|
|
||||||
event->kind = br_read_i8(&br);
|
game_event->kind = br_read_i8(&br);
|
||||||
switch (event->kind) {
|
switch (game_event->kind) {
|
||||||
case GAME_EVENT_KIND_SNAPSHOT_FULL:
|
case GAME_EVENT_KIND_SNAPSHOT_FULL:
|
||||||
{
|
{
|
||||||
} break;
|
} break;
|
||||||
@ -1521,14 +1519,33 @@ void game_events_from_host_events(struct arena *arena, struct host_event_array h
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (events_out->last) {
|
if (events_out->last) {
|
||||||
events_out->last->next = cmd;
|
events_out->last->next = game_event;
|
||||||
} else {
|
} else {
|
||||||
events_out->first = cmd;
|
events_out->first = game_event;
|
||||||
}
|
}
|
||||||
events_out->last = cmd;
|
events_out->last = game_event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Snapshot
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct string game_string_from_tick(struct arena *arena, struct world *tick)
|
||||||
|
{
|
||||||
|
(UNUSED)arena;
|
||||||
|
(UNUSED)tick;
|
||||||
|
|
||||||
|
struct string res = ZI;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_tick_from_string(struct string str, struct world *tick_out)
|
||||||
|
{
|
||||||
|
(UNUSED)str;
|
||||||
|
(UNUSED)tick_out;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Game thread
|
* Game thread
|
||||||
* ========================== */
|
* ========================== */
|
||||||
@ -1536,8 +1553,6 @@ void game_events_from_host_events(struct arena *arena, struct host_event_array h
|
|||||||
|
|
||||||
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg)
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg)
|
||||||
{
|
{
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
||||||
|
|
||||||
(UNUSED)arg;
|
(UNUSED)arg;
|
||||||
i64 last_frame_ns = 0;
|
i64 last_frame_ns = 0;
|
||||||
i64 target_dt_ns = NS_FROM_SECONDS(GAME_FPS > (0) ? (1.0 / GAME_FPS) : 0);
|
i64 target_dt_ns = NS_FROM_SECONDS(GAME_FPS > (0) ? (1.0 / GAME_FPS) : 0);
|
||||||
|
|||||||
21
src/game.h
21
src/game.h
@ -1,6 +1,8 @@
|
|||||||
#ifndef GAME_H
|
#ifndef GAME_H
|
||||||
#define GAME_H
|
#define GAME_H
|
||||||
|
|
||||||
|
#include "host.h"
|
||||||
|
|
||||||
struct world;
|
struct world;
|
||||||
struct mixer_startup_receipt;
|
struct mixer_startup_receipt;
|
||||||
struct sprite_startup_receipt;
|
struct sprite_startup_receipt;
|
||||||
@ -84,7 +86,7 @@ struct game_cmd_list {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct string game_string_from_cmds(struct arena *arena, struct game_cmd_list cmds);
|
struct string game_string_from_cmds(struct arena *arena, struct game_cmd_list cmds);
|
||||||
void game_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_cmd *cmds_out);
|
void game_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_cmd_list *cmds_out);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Game event
|
* Game event
|
||||||
@ -104,9 +106,13 @@ enum game_event_kind {
|
|||||||
|
|
||||||
struct game_event {
|
struct game_event {
|
||||||
enum game_event_kind kind;
|
enum game_event_kind kind;
|
||||||
|
struct host_channel_id channel_id;
|
||||||
|
|
||||||
struct entity_handle entity;
|
struct string snapshot_data;
|
||||||
struct string update_data;
|
struct string disconnect_reason;
|
||||||
|
|
||||||
|
//struct entity_handle entity;
|
||||||
|
//struct string update_data;
|
||||||
|
|
||||||
struct game_event *next;
|
struct game_event *next;
|
||||||
};
|
};
|
||||||
@ -117,6 +123,13 @@ struct game_event_list {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct string game_string_from_events(struct arena *arena, struct game_event_list events);
|
struct string game_string_from_events(struct arena *arena, struct game_event_list events);
|
||||||
void game_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_event *events_out);
|
void game_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_event_list *events_out);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Snapshot
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct string game_string_from_tick(struct arena *arena, struct world *tick);
|
||||||
|
void game_tick_from_string(struct string str, struct world *tick_out);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
483
src/host.c
483
src/host.c
@ -2,6 +2,7 @@
|
|||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
#include "scratch.h"
|
#include "scratch.h"
|
||||||
#include "byteio.h"
|
#include "byteio.h"
|
||||||
|
#include "sys.h"
|
||||||
|
|
||||||
//#define HOST_NETWORK_ADDRESS_STRING(str)
|
//#define HOST_NETWORK_ADDRESS_STRING(str)
|
||||||
//#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port)
|
//#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port)
|
||||||
@ -10,69 +11,81 @@
|
|||||||
#define PACKET_MAGIC 0xd9e3b8b6
|
#define PACKET_MAGIC 0xd9e3b8b6
|
||||||
#define PACKET_CHUNK_MAX_LEN 256
|
#define PACKET_CHUNK_MAX_LEN 256
|
||||||
|
|
||||||
GLOBAL struct {
|
/* Give enough space for msg chunk + header */
|
||||||
i32 _;
|
#define PACKET_DATA_MAX_LEN (PACKET_CHUNK_MAX_LEN * 2)
|
||||||
} G = ZI, DEBUG_ALIAS(G, G_host);
|
|
||||||
|
|
||||||
|
enum packet_kind {
|
||||||
struct host_address_desc {
|
PACKET_KIND_NONE,
|
||||||
i32 _;
|
PACKET_KIND_TRY_CONNECT,
|
||||||
|
PACKET_KIND_CONNECT_SUCCESS,
|
||||||
|
PACKET_KIND_DISCONNECT,
|
||||||
|
PACKET_KIND_MSG_CHUNK
|
||||||
};
|
};
|
||||||
|
|
||||||
struct host_channel_id {
|
enum packet_flag {
|
||||||
u32 idx;
|
PACKET_FLAG_NONE = 0,
|
||||||
u32 gen;
|
PACKET_FLAG_RELIABLE = (1 << 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct packet {
|
||||||
|
u64 seq;
|
||||||
|
u8 flags;
|
||||||
|
u64 data_len;
|
||||||
|
u8 data[PACKET_DATA_MAX_LEN];
|
||||||
|
|
||||||
|
struct packet *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct host_channel {
|
struct host_channel {
|
||||||
b32 valid;
|
b32 valid;
|
||||||
|
b32 connected;
|
||||||
|
struct host *host;
|
||||||
|
|
||||||
struct host_channel_id id;
|
struct host_channel_id id;
|
||||||
|
struct sock_address address;
|
||||||
|
|
||||||
|
struct packet *first_reliable_packet;
|
||||||
|
struct packet *last_reliable_packet;
|
||||||
|
struct packet *first_unreliable_packet;
|
||||||
|
struct packet *last_unreliable_packet;
|
||||||
|
|
||||||
|
u64 their_acked_seq;
|
||||||
|
u64 our_acked_seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum host_cmd_kind {
|
struct host_channel_node {
|
||||||
HOST_CMD_KIND_NONE,
|
struct host_channel *channel;
|
||||||
|
struct host_channel_node *next;
|
||||||
HOST_CMD_KIND_CONNECT,
|
|
||||||
HOST_CMD_KIND_DISCONNECT,
|
|
||||||
HOST_CMD_KIND_WRITE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum host_event_kind {
|
struct host_channel_list {
|
||||||
HOST_EVENT_KIND_NONE,
|
struct host_channel_node *first;
|
||||||
|
struct host_channel_node *last;
|
||||||
HOST_EVENT_KIND_CHANNEL_OPENED,
|
|
||||||
HOST_EVENT_KIND_CHANNEL_CLOSED,
|
|
||||||
HOST_EVENT_KIND_MSG
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct host_cmd {
|
struct host_queued_event {
|
||||||
enum host_cmd_kind kind;
|
struct host_event event;
|
||||||
struct host_cmd *next;
|
struct host_queued_event *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct host_event {
|
struct recv_buffer {
|
||||||
enum host_event_kind kind;
|
u64 msg_id;
|
||||||
|
|
||||||
|
u64 last_chunk_len;
|
||||||
|
u64 num_chunks_total;
|
||||||
|
u64 num_chunks_received;
|
||||||
|
|
||||||
|
i64 touched_ns;
|
||||||
|
|
||||||
|
u8 *chunks_received_bitmap;
|
||||||
|
u8 *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct host {
|
READONLY GLOBAL struct host_channel _g_host_channel_nil = { .valid = false };
|
||||||
struct arena cmd_arena;
|
|
||||||
struct host_cmd *first_cmd;
|
|
||||||
struct host_cmd *last_cmd;
|
|
||||||
struct host_cmd *first_free_cmd;
|
|
||||||
|
|
||||||
struct arena channels_arena;
|
|
||||||
struct host_channel *channels;
|
|
||||||
u64 channels_reserved;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
GLOBAL struct {
|
||||||
|
i32 _;
|
||||||
|
} G = ZI, DEBUG_ALIAS(G, G_host);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Startup
|
* Startup
|
||||||
@ -86,37 +99,144 @@ struct host_startup_receipt host_startup(struct sock_startup_receipt *sock_sr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Allocation
|
* Host
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
struct host host_alloc(struct host_address_desc bind_address)
|
struct host host_alloc(struct sock_address bind_address)
|
||||||
{
|
{
|
||||||
struct host host = ZI;
|
struct host host = ZI;
|
||||||
host.cmd_arena = arena_alloc(GIGABYTE(64));
|
host.cmd_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
host.queued_event_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
host.packet_arena = arena_alloc(GIGABYTE(64));
|
||||||
host.channels_arena = arena_alloc(GIGABYTE(64));
|
host.channels_arena = arena_alloc(GIGABYTE(64));
|
||||||
|
host.sock = sock_alloc(bind_address, SOCK_FLAG_NON_BLOCKING);
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
void host_release(struct host *host)
|
void host_release(struct host *host)
|
||||||
{
|
{
|
||||||
|
sock_release(&host->sock);
|
||||||
arena_release(&host->channels_arena);
|
arena_release(&host->channels_arena);
|
||||||
|
arena_release(&host->packet_arena);
|
||||||
|
arena_release(&host->queued_event_arena);
|
||||||
arena_release(&host->cmd_arena);
|
arena_release(&host->cmd_arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Channel
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL struct host_channel *host_channel_from_address(struct host *host, struct sock_address address)
|
||||||
|
{
|
||||||
|
(UNUSED)host;
|
||||||
|
(UNUSED)address;
|
||||||
|
|
||||||
|
struct host_channel *res = &_g_host_channel_nil;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL struct host_channel_list host_channels_from_id(struct arena *arena, struct host *host, struct host_channel_id channel_id)
|
||||||
|
{
|
||||||
|
(UNUSED)arena;
|
||||||
|
(UNUSED)host;
|
||||||
|
(UNUSED)channel_id;
|
||||||
|
|
||||||
|
struct host_channel_list res = ZI;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL struct host_channel *host_channel_alloc(struct host *host, struct sock_address address)
|
||||||
|
{
|
||||||
|
(UNUSED)host;
|
||||||
|
(UNUSED)address;
|
||||||
|
|
||||||
|
struct host_channel *res = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL void host_channel_release(struct host_channel *channel)
|
||||||
|
{
|
||||||
|
(UNUSED)channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL struct recv_buffer *host_channel_get_recv_buffer(struct host_channel *channel, u64 msg_id)
|
||||||
|
{
|
||||||
|
(UNUSED)channel;
|
||||||
|
(UNUSED)msg_id;
|
||||||
|
|
||||||
|
struct recv_buffer *res = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Packet
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL struct packet *host_channel_packet_alloc(struct host_channel *channel, b32 is_reliable)
|
||||||
|
{
|
||||||
|
struct host *host = channel->host;
|
||||||
|
struct packet *packet = NULL;
|
||||||
|
if (host->first_free_packet) {
|
||||||
|
packet = host->first_free_packet;
|
||||||
|
host->first_free_packet = packet->next;
|
||||||
|
} else {
|
||||||
|
packet = arena_push(&host->packet_arena, struct packet);
|
||||||
|
}
|
||||||
|
MEMZERO_STRUCT(packet);
|
||||||
|
|
||||||
|
if (is_reliable) {
|
||||||
|
if (channel->last_reliable_packet) {
|
||||||
|
channel->last_reliable_packet->next = packet;
|
||||||
|
} else {
|
||||||
|
channel->first_reliable_packet = packet;
|
||||||
|
}
|
||||||
|
channel->last_reliable_packet = packet;
|
||||||
|
} else {
|
||||||
|
if (channel->last_unreliable_packet) {
|
||||||
|
channel->last_unreliable_packet->next = packet;
|
||||||
|
} else {
|
||||||
|
channel->first_unreliable_packet = packet;
|
||||||
|
}
|
||||||
|
channel->last_unreliable_packet = packet;
|
||||||
|
}
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Recv buffer
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL struct recv_buffer *recv_buffer_alloc(struct host_channel *channel, u64 msg_id, u64 chunk_count)
|
||||||
|
{
|
||||||
|
(UNUSED)channel;
|
||||||
|
(UNUSED)msg_id;
|
||||||
|
(UNUSED)chunk_count;
|
||||||
|
|
||||||
|
struct recv_buffer *res = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL b32 recv_buffer_is_chunk_filled(struct recv_buffer *rb, u64 chunk_id)
|
||||||
|
{
|
||||||
|
(UNUSED)rb;
|
||||||
|
(UNUSED)chunk_id;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERNAL void recv_buffer_set_chunk_received(struct recv_buffer *rb, u64 chunk_id)
|
||||||
|
{
|
||||||
|
(UNUSED)rb;
|
||||||
|
(UNUSED)chunk_id;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Queue
|
* Queue
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
INTERNAL struct host_cmd *host_cmd_alloc_and_append(struct host *host)
|
INTERNAL struct host_cmd *host_cmd_alloc_and_append(struct host *host)
|
||||||
{
|
{
|
||||||
struct host_cmd *cmd = NULL;
|
struct host_cmd *cmd = arena_push_zero(&host->cmd_arena, struct host_cmd);
|
||||||
if (host->first_free_cmd) {
|
|
||||||
cmd = host->first_free_cmd;
|
|
||||||
host->first_free_cmd = cmd->next_free;
|
|
||||||
} else {
|
|
||||||
cmd = arena_push(&host->arena, struct host_cmd);
|
|
||||||
}
|
|
||||||
MEMZERO_STRUCT(cmd);
|
|
||||||
if (host->last_cmd) {
|
if (host->last_cmd) {
|
||||||
host->last_cmd->next = cmd;
|
host->last_cmd->next = cmd;
|
||||||
} else {
|
} else {
|
||||||
@ -126,24 +246,29 @@ INTERNAL struct host_cmd *host_cmd_alloc_and_append(struct host *host)
|
|||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void host_queue_connect_to_address(struct host *host, struct host_address_desc host_address_desc)
|
void host_queue_connect_to_address(struct host *host, struct sock_address connect_address)
|
||||||
{
|
{
|
||||||
struct sock_address_desc sock_address_desc = sock_address_desc_from_host_address_desc(host_address_desc);
|
struct host_channel *channel = host_channel_from_address(host, connect_address);
|
||||||
struct sock_address address = sock_address_from_desc(sock_address_desc);
|
|
||||||
struct channel *channel = channel_from_address(host, address);
|
|
||||||
if (!channel->valid) {
|
if (!channel->valid) {
|
||||||
channel = channel_alloc(host, address);
|
channel = host_channel_alloc(host, connect_address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void host_queue_disconnect(struct host *host, struct host_channel_id channel_id)
|
void host_queue_disconnect(struct host *host, struct host_channel_id channel_id)
|
||||||
{
|
{
|
||||||
|
(UNUSED)host;
|
||||||
|
(UNUSED)channel_id;
|
||||||
|
|
||||||
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
|
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
|
||||||
cmd->kind = HOST_CMD_KIND_DISCONNECT;
|
cmd->kind = HOST_CMD_KIND_DISCONNECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg)
|
void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg)
|
||||||
{
|
{
|
||||||
|
(UNUSED)host;
|
||||||
|
(UNUSED)channel_id;
|
||||||
|
(UNUSED)msg;
|
||||||
|
|
||||||
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
|
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
|
||||||
cmd->kind = HOST_CMD_KIND_WRITE;
|
cmd->kind = HOST_CMD_KIND_WRITE;
|
||||||
}
|
}
|
||||||
@ -152,39 +277,69 @@ void host_queue_write(struct host *host, struct host_channel_id channel_id, stru
|
|||||||
* Update
|
* Update
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
|
INTERNAL struct host_queued_event *host_queued_event_alloc_and_append(struct host *host)
|
||||||
|
{
|
||||||
|
struct host_queued_event *qe = arena_push_zero(&host->queued_event_arena, struct host_queued_event);
|
||||||
|
if (host->last_queued_event) {
|
||||||
|
host->last_queued_event->next = qe;
|
||||||
|
} else {
|
||||||
|
host->first_queued_event = qe;
|
||||||
|
}
|
||||||
|
host->last_queued_event = qe;
|
||||||
|
++host->num_queued_events;
|
||||||
|
return qe;
|
||||||
|
}
|
||||||
|
|
||||||
void host_update(struct host *host)
|
void host_update(struct host *host)
|
||||||
{
|
{
|
||||||
struct temp_arena scratch = scratch_begin_no_conflict();
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
||||||
|
|
||||||
struct socket *sock = &host->sock;
|
i64 now_ns = sys_time_ns();
|
||||||
|
|
||||||
|
struct sock *sock = &host->sock;
|
||||||
struct string read_buff = ZI;
|
struct string read_buff = ZI;
|
||||||
read_buff.len = KILOBYTE(64);
|
read_buff.len = PACKET_DATA_MAX_LEN;
|
||||||
read_buff.text = arena_push_array(scratch.arena, u8, read_buff.len);
|
read_buff.text = arena_push_array(scratch.arena, u8, read_buff.len);
|
||||||
|
|
||||||
/* Read socket */
|
/* Read socket */
|
||||||
while (true) {
|
while (true) {
|
||||||
struct sock_read_result res = sock_read(sock, read_buff);
|
struct sock_read_result res = sock_read(sock, read_buff);
|
||||||
if (res.valid) {
|
if (res.valid) {
|
||||||
struct string data = res.data;
|
struct string sock_data = res.data;
|
||||||
struct sock_address address = res.address;
|
struct sock_address address = res.address;
|
||||||
struct byte_reader br = br_from_buffer(data);
|
struct byte_reader br = br_from_buffer(sock_data);
|
||||||
u32 magic = br_read_u32(&br);
|
u32 magic = br_read_u32(&br);
|
||||||
if (magic == PACKET_MAGIC) {
|
if (magic == PACKET_MAGIC) {
|
||||||
/* TODO: Combine kind byte with flags byte */
|
/* TODO: Combine kind byte with flags byte */
|
||||||
struct host_channel *channel = host_channel_from_sock_address(host, address);
|
struct host_channel *channel = host_channel_from_address(host, address);
|
||||||
enum packet_kind packet_kind = br_read_i8(&br);
|
enum packet_kind packet_kind = br_read_i8(&br);
|
||||||
u8 packet_flags = br_read_u8(&br);
|
u8 packet_flags = br_read_u8(&br);
|
||||||
u64 packet_id = br_read_var_uint(&br);
|
|
||||||
u64 packet_reliable_id = 0;
|
u64 their_acked_seq = br_read_var_uint(&br);
|
||||||
if (packet_flags & PACKET_FLAG_RELIABLE) {
|
if (their_acked_seq > channel->their_acked_seq) {
|
||||||
packet_reliable_id = br_read_var_uint(&br);
|
channel->their_acked_seq = their_acked_seq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b32 should_process_packet = false;
|
||||||
|
if (packet_flags & PACKET_FLAG_RELIABLE) {
|
||||||
|
u64 packet_seq = br_read_var_uint(&br);
|
||||||
|
if (packet_seq == channel->our_acked_seq + 1) {
|
||||||
|
channel->our_acked_seq = packet_seq;
|
||||||
|
should_process_packet = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
should_process_packet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_process_packet) {
|
||||||
switch (packet_kind) {
|
switch (packet_kind) {
|
||||||
case PACKET_KIND_TRY_CONNECT:
|
case PACKET_KIND_TRY_CONNECT:
|
||||||
{
|
{
|
||||||
/* A foreign host is trying to connect to us */
|
/* A foreign host is trying to connect to us */
|
||||||
if (!channel->valid) {
|
if (!channel->valid) {
|
||||||
channel = channel_alloc(host, address);
|
/* TODO: Verify that some per-host uuid isn't
|
||||||
|
* present in a rolling window to prevent reconnects right after a disconnect? */
|
||||||
|
channel = host_channel_alloc(host, address);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -192,9 +347,9 @@ void host_update(struct host *host)
|
|||||||
{
|
{
|
||||||
/* We successfully connected to a foreign host and they are ready to receive messages */
|
/* We successfully connected to a foreign host and they are ready to receive messages */
|
||||||
if (channel->valid && !channel->connected) {
|
if (channel->valid && !channel->connected) {
|
||||||
struct host_queued_event *queued_event = queued_event_alloc_and_append(host);
|
struct host_queued_event *queued_event = host_queued_event_alloc_and_append(host);
|
||||||
queued_event->kind = HOST_EVENT_KIND_CHANNEL_OPENED;
|
queued_event->event.kind = HOST_EVENT_KIND_CHANNEL_OPENED;
|
||||||
queued_event->channel_id = channel->id;
|
queued_event->event.channel_id = channel->id;
|
||||||
channel->connected = true;
|
channel->connected = true;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@ -203,10 +358,10 @@ void host_update(struct host *host)
|
|||||||
{
|
{
|
||||||
/* A foreign host disconnected from us */
|
/* A foreign host disconnected from us */
|
||||||
if (channel->valid) {
|
if (channel->valid) {
|
||||||
struct host_queued_event *queued_event = queued_event_alloc_and_append(host);
|
struct host_queued_event *queued_event = host_queued_event_alloc_and_append(host);
|
||||||
queued_event->kind = HOST_EVENT_KIND_CHANNEL_CLOSED;
|
queued_event->event.kind = HOST_EVENT_KIND_CHANNEL_CLOSED;
|
||||||
queued_event->channel_id = channel->id;
|
queued_event->event.channel_id = channel->id;
|
||||||
channel_release(channel);
|
host_channel_release(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
@ -217,38 +372,38 @@ void host_update(struct host *host)
|
|||||||
u64 msg_id = br_read_var_uint(&br);
|
u64 msg_id = br_read_var_uint(&br);
|
||||||
u64 chunk_id = br_read_var_uint(&br);
|
u64 chunk_id = br_read_var_uint(&br);
|
||||||
u64 chunk_count = br_read_var_uint(&br);
|
u64 chunk_count = br_read_var_uint(&br);
|
||||||
u64 data_len = ((chunk_id + 1) == chunk_count) ? br_read_u8(&br) : PACKET_CHUNK_MAX_LEN;
|
|
||||||
|
|
||||||
struct msg_buffer *msg_buff = channel_get_msg_buffer(channel, msg_id);
|
b32 is_last_chunk = (chunk_id + 1) == chunk_count;
|
||||||
if (!msg_buff) {
|
u64 data_len = is_last_chunk ? (br_read_u8(&br) + 1) : PACKET_CHUNK_MAX_LEN;
|
||||||
msg_buff = channel_msg_buffer_alloc(channel, chunk_count);
|
|
||||||
|
struct recv_buffer *recv_buff = host_channel_get_recv_buffer(channel, msg_id);
|
||||||
|
if (!recv_buff) {
|
||||||
|
recv_buff = recv_buffer_alloc(channel, msg_id, chunk_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chunk_count == msg_buff->chunk_count && chunk_id < chunk_count) {
|
if (chunk_count == recv_buff->num_chunks_total && chunk_id < chunk_count) {
|
||||||
struct msg_chunk *chunk = &msg_buff->chunks[chunk_id];
|
if (!recv_buffer_is_chunk_filled(recv_buff, chunk_id)) {
|
||||||
if (!chunk->filled) {
|
u8 *src = br_seek(&br, data_len);
|
||||||
u8 *data = br_seek(&br, data_len);
|
if (src) {
|
||||||
if (data) {
|
u8 *dst = &recv_buff->data[chunk_id * PACKET_CHUNK_MAX_LEN];
|
||||||
MEMCPY(chunk->data, data, data_len);
|
MEMCPY(dst, src, data_len);
|
||||||
chunk->data_len = data_len;
|
if (is_last_chunk) {
|
||||||
chunk->filled = true;
|
recv_buff->last_chunk_len = data_len;
|
||||||
++msg_buff->filled_chunks;
|
}
|
||||||
msg_buff->last_touch = now;
|
recv_buffer_set_chunk_received(recv_buff, chunk_id);
|
||||||
if (msg_buff->filled_chunks == chunk_count) {
|
++recv_buff->num_chunks_received;
|
||||||
|
recv_buff->touched_ns = now_ns;
|
||||||
|
if (recv_buff->num_chunks_received == chunk_count) {
|
||||||
/* All chunks filled, message has finished assembling */
|
/* All chunks filled, message has finished assembling */
|
||||||
/* TODO: Message ordering */
|
/* TODO: Message ordering */
|
||||||
struct host_queued_event *queued_event = queued_event_alloc_and_append(host);
|
struct host_queued_event *queued_event = host_queued_event_alloc_and_append(host);
|
||||||
struct string data = ZI;
|
struct string data = ZI;
|
||||||
data.text = arena_push_array(&host->queued_event_arena, u8, chunk_count * PACKET_CHUNK_MAX_LEN);
|
data.len = ((chunk_count - 1) * PACKET_CHUNK_MAX_LEN) + recv_buff->last_chunk_len;
|
||||||
for (u64 i = 0; i < chunk_count; ++i) {
|
data.text = arena_push_array(&host->queued_event_arena, u8, data.len);
|
||||||
struct msg_chunk *filled_chunk = &msg_buff->chunks[i];
|
MEMCPY(data.text, recv_buff->data, data.len);
|
||||||
u64 len = filled_chunk->data_len;
|
queued_event->event.kind = HOST_EVENT_KIND_MSG;
|
||||||
MEMCPY(data.text + data.len, filled_chunk->data, len);
|
queued_event->event.msg = data;
|
||||||
data.len += len;
|
queued_event->event.channel_id = channel->id;
|
||||||
}
|
|
||||||
queued_event->kind = HOST_EVENT_KIND_MSG;
|
|
||||||
queued_event->msg_data = data;
|
|
||||||
queued_event->channel_id = channel->id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,6 +413,8 @@ void host_update(struct host *host)
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -265,66 +422,93 @@ void host_update(struct host *host)
|
|||||||
|
|
||||||
/* Release expired msg buffers */
|
/* Release expired msg buffers */
|
||||||
|
|
||||||
/* Try connecting to unconnected hosts */
|
/* Update channels */
|
||||||
for (u64 i = 0; i < host->channels; ++i) {
|
for (u64 i = 0; i < host->channels_reserved; ++i) {
|
||||||
struct channel *channel = &host->channels[i];
|
struct host_channel *channel = &host->channels[i];
|
||||||
if (channel->valid && !channel->connected) {
|
if (channel->valid) {
|
||||||
|
/* Send / resend handshake if not connected */
|
||||||
|
if (!channel->connected) {
|
||||||
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
|
struct host_cmd *cmd = host_cmd_alloc_and_append(host);
|
||||||
cmd->kind = HOST_CMD_KIND_TRY_CONNECT;
|
cmd->kind = HOST_CMD_KIND_TRY_CONNECT;
|
||||||
cmd->channel_id = channel->id;
|
cmd->channel_id = channel->id;
|
||||||
}
|
}
|
||||||
|
/* Release acked reliable packets */
|
||||||
|
{
|
||||||
|
u64 acked_seq = channel->their_acked_seq;
|
||||||
|
struct packet *packet = channel->first_reliable_packet;
|
||||||
|
while (packet) {
|
||||||
|
struct packet *next = packet->next;
|
||||||
|
u64 seq = packet->seq;
|
||||||
|
if (seq < acked_seq) {
|
||||||
|
packet->next = host->first_free_packet;
|
||||||
|
host->first_free_packet = packet;
|
||||||
|
channel->first_reliable_packet = next;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
packet = next;
|
||||||
|
}
|
||||||
|
if (channel->first_reliable_packet == NULL) {
|
||||||
|
channel->last_reliable_packet = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release un
|
|
||||||
|
|
||||||
/* Process cmds */
|
/* Process cmds */
|
||||||
|
/* TODO: Unreliable packets don't need to be allocated into unreliable packet queue, should just send them and forget */
|
||||||
for (struct host_cmd *cmd = host->first_cmd; cmd; cmd = cmd->next) {
|
for (struct host_cmd *cmd = host->first_cmd; cmd; cmd = cmd->next) {
|
||||||
struct host_channel *channel = host_channel_from_id(host, cmd->channel_id);
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
if (channel->valid) {
|
|
||||||
enum host_cmd_kind kind = cmd->kind;
|
enum host_cmd_kind kind = cmd->kind;
|
||||||
struct sock_address address = channel->sock_address;
|
struct host_channel_id channel_id = cmd->channel_id;
|
||||||
|
struct host_channel_list channels = host_channels_from_id(temp.arena, host, channel_id);
|
||||||
|
for (struct host_channel_node *node = channels.first; node; node = node->next) {
|
||||||
|
struct host_channel *channel = node->channel;
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case HOST_CMD_KIND_TRY_CONNECT:
|
case HOST_CMD_KIND_TRY_CONNECT:
|
||||||
{
|
{
|
||||||
struct packet *packet = host_packet_alloc(host, address, false);
|
u8 packet_flags = 0;
|
||||||
|
struct packet *packet = host_channel_packet_alloc(channel, false);
|
||||||
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
||||||
bw_write_i8(&bw, PACKET_KIND_TRY_CONNECT);
|
bw_write_i8(&bw, PACKET_KIND_TRY_CONNECT);
|
||||||
bw_write_u8(&bw, packet_flags);
|
bw_write_u8(&bw, packet_flags);
|
||||||
bw_write_var_uint(&bw, packet->id);
|
bw_write_var_uint(&bw, channel->our_acked_seq);
|
||||||
bw_write_var_uint(&bw, packet->reliable_id);
|
|
||||||
packet->data_len = bw_pos(&bw);
|
packet->data_len = bw_pos(&bw);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case HOST_CMD_KIND_CONNECT_SUCCESS:
|
case HOST_CMD_KIND_CONNECT_SUCCESS:
|
||||||
{
|
{
|
||||||
struct packet *packet = host_packet_alloc(host, address, false);
|
u8 packet_flags = 0;
|
||||||
|
struct packet *packet = host_channel_packet_alloc(channel, false);
|
||||||
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
||||||
bw_write_i8(&bw, PACKET_KIND_CONNECT_SUCCESS);
|
bw_write_i8(&bw, PACKET_KIND_CONNECT_SUCCESS);
|
||||||
bw_write_u8(&bw, packet_flags);
|
bw_write_u8(&bw, packet_flags);
|
||||||
bw_write_var_uint(&bw, packet->id);
|
bw_write_var_uint(&bw, channel->our_acked_seq);
|
||||||
bw_write_var_uint(&bw, packet->reliable_id);
|
|
||||||
packet->data_len = bw_pos(&bw);
|
packet->data_len = bw_pos(&bw);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case HOST_CMD_KIND_DISCONNECT:
|
case HOST_CMD_KIND_DISCONNECT:
|
||||||
{
|
{
|
||||||
struct packet *packet = host_packet_alloc(host, address, false);
|
u8 packet_flags = 0;
|
||||||
|
struct packet *packet = host_channel_packet_alloc(channel, false);
|
||||||
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
||||||
bw_write_i8(&bw, PACKET_KIND_DISCONNECT);
|
bw_write_i8(&bw, PACKET_KIND_DISCONNECT);
|
||||||
bw_write_u8(&bw, packet_flags);
|
bw_write_u8(&bw, packet_flags);
|
||||||
bw_write_var_uint(&bw, packet->id);
|
bw_write_var_uint(&bw, channel->our_acked_seq);
|
||||||
packet->data_len = bw_pos(&bw);
|
packet->data_len = bw_pos(&bw);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case HOST_CMD_KIND_WRITE:
|
case HOST_CMD_KIND_WRITE:
|
||||||
{
|
{
|
||||||
b32 is_reliable = packet_flags & HOST_WRITE_FLAG_RELIABLE;
|
b32 is_reliable = cmd->write_reliable;
|
||||||
struct string msg = cmd->msg;
|
u8 packet_flags = (is_reliable * PACKET_FLAG_RELIABLE);
|
||||||
|
struct string msg = cmd->write_msg;
|
||||||
|
|
||||||
u64 chunk_count = 0;
|
u64 chunk_count = 0;
|
||||||
if (msg.len > 0) {
|
if (msg.len > 0) {
|
||||||
chunk_count = ((msg.len - 1) / PACKET_CHUNK_MAX_LEN) + 1
|
chunk_count = (msg.len - 1) / PACKET_CHUNK_MAX_LEN;
|
||||||
}
|
}
|
||||||
|
chunk_count += 1;
|
||||||
|
|
||||||
for (u64 i = 0; i < chunk_count; ++i) {
|
for (u64 i = 0; i < chunk_count; ++i) {
|
||||||
u64 data_len = PACKET_CHUNK_MAX_LEN;
|
u64 data_len = PACKET_CHUNK_MAX_LEN;
|
||||||
@ -333,16 +517,17 @@ void host_update(struct host *host)
|
|||||||
data_len = msg.len % PACKET_CHUNK_MAX_LEN;
|
data_len = msg.len % PACKET_CHUNK_MAX_LEN;
|
||||||
}
|
}
|
||||||
u8 *data = msg.text + (i * PACKET_CHUNK_MAX_LEN);
|
u8 *data = msg.text + (i * PACKET_CHUNK_MAX_LEN);
|
||||||
struct packet *packet = host_packet_alloc(host, address, is_reliable);
|
struct packet *packet = host_channel_packet_alloc(channel, is_reliable);
|
||||||
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data));
|
||||||
bw_write_i8(&bw, PACKET_KIND_MSG_CHUNK);
|
bw_write_i8(&bw, PACKET_KIND_MSG_CHUNK);
|
||||||
bw_write_u8(&bw, packet_flags);
|
bw_write_u8(&bw, packet_flags);
|
||||||
bw_write_var_uint(&bw, packet->id);
|
bw_write_var_uint(&bw, channel->our_acked_seq);
|
||||||
if (is_reliable) {
|
if (is_reliable) {
|
||||||
bw_write_var_uint(&bw, packet->reliable_id);
|
bw_write_var_uint(&bw, packet->seq);
|
||||||
}
|
}
|
||||||
if (is_last_chunk) {
|
if (is_last_chunk) {
|
||||||
bw_write_u8(&bw, data_len);
|
/* FIXME: Ensure data_len can never be 0 */
|
||||||
|
bw_write_u8(&bw, data_len - 1);
|
||||||
}
|
}
|
||||||
bw_write_buffer(&bw, STRING(data_len, data));
|
bw_write_buffer(&bw, STRING(data_len, data));
|
||||||
packet->data_len = bw_pos(&bw);
|
packet->data_len = bw_pos(&bw);
|
||||||
@ -352,24 +537,35 @@ void host_update(struct host *host)
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
arena_temp_end(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send packets */
|
/* Process packets */
|
||||||
for (struct packet *packet = host->first_packet; packet; packet = packet->next) {
|
/* TODO: Aggregate small packets */
|
||||||
struct sock_address address = packet->address;
|
for (u64 i = 0; i < host->channels_reserved; ++i) {
|
||||||
|
struct host_channel *channel = &host->channels[i];
|
||||||
|
struct sock_address address = channel->address;
|
||||||
|
/* Send unreliable packets to channel */
|
||||||
|
for (struct packet *packet = channel->first_unreliable_packet; packet; packet = packet->next) {
|
||||||
sock_write(sock, address, STRING(packet->data_len, packet->data));
|
sock_write(sock, address, STRING(packet->data_len, packet->data));
|
||||||
}
|
}
|
||||||
|
/* Send un-acked reliable packets to channel */
|
||||||
|
for (struct packet *packet = channel->first_reliable_packet; packet; packet = packet->next) {
|
||||||
|
sock_write(sock, address, STRING(packet->data_len, packet->data));
|
||||||
|
}
|
||||||
|
/* Release unreliable packets */
|
||||||
|
if (channel->first_unreliable_packet) {
|
||||||
|
host->first_free_packet = channel->first_unreliable_packet;
|
||||||
|
channel->last_unreliable_packet->next = host->first_free_packet;
|
||||||
|
channel->first_unreliable_packet = NULL;
|
||||||
|
channel->last_unreliable_packet = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset packets */
|
|
||||||
host->first_packet = NULL;
|
|
||||||
host->last_packet = NULL;
|
|
||||||
host->first_free_packet = NULL;
|
|
||||||
arena_reset(&host->packet_arena);
|
|
||||||
|
|
||||||
/* Reset cmds */
|
/* Reset cmds */
|
||||||
host->first_cmd = NULL;
|
host->first_cmd = NULL;
|
||||||
host->last_cmd = NULL;
|
host->last_cmd = NULL;
|
||||||
host->first_free_cmd = NULL;
|
|
||||||
arena_reset(&host->cmd_arena);
|
arena_reset(&host->cmd_arena);
|
||||||
|
|
||||||
scratch_end(scratch);
|
scratch_end(scratch);
|
||||||
@ -388,17 +584,20 @@ struct host_event_array host_pop_events(struct arena *arena, struct host *host)
|
|||||||
u64 i = 0;
|
u64 i = 0;
|
||||||
for (struct host_queued_event *qe = host->first_queued_event; qe; qe = qe->next) {
|
for (struct host_queued_event *qe = host->first_queued_event; qe; qe = qe->next) {
|
||||||
struct host_event *dest = &res.events[i];
|
struct host_event *dest = &res.events[i];
|
||||||
*dest = (struct host_event) {
|
*dest = qe->event;
|
||||||
.kind = qe->kind,
|
struct string src_msg = qe->event.msg;
|
||||||
.msg = qe->msg
|
if (src_msg.len > 0) {
|
||||||
};
|
dest->msg.text = arena_push_array(arena, u8, src_msg.len);
|
||||||
|
MEMCPY(dest->msg.text, src_msg.text, src_msg.len);
|
||||||
|
}
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset queued events */
|
/* Reset queued events */
|
||||||
hust->num_queued_events = 0;
|
host->num_queued_events = 0;
|
||||||
host->first_queued_event = NULL;
|
host->first_queued_event = NULL;
|
||||||
host->last_queued_event = NULL;
|
host->last_queued_event = NULL;
|
||||||
host->first_free_queued_event = NULL;
|
|
||||||
arena_reset(&host->queued_event_arena);
|
arena_reset(&host->queued_event_arena);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
120
src/host.h
120
src/host.h
@ -1,4 +1,124 @@
|
|||||||
#ifndef HOST_H
|
#ifndef HOST_H
|
||||||
#define HOST_H
|
#define HOST_H
|
||||||
|
|
||||||
|
#include "sock.h"
|
||||||
|
|
||||||
|
#define HOST_CHANNEL_ID_NIL (struct host_channel_id) { .gen = 0, .idx = 0 }
|
||||||
|
#define HOST_CHANNEL_ID_ALL (struct host_channel_id) { .gen = U32_MAX, .idx = U32_MAX }
|
||||||
|
|
||||||
|
struct packet;
|
||||||
|
|
||||||
|
enum host_cmd_kind {
|
||||||
|
HOST_CMD_KIND_NONE,
|
||||||
|
|
||||||
|
HOST_CMD_KIND_TRY_CONNECT,
|
||||||
|
HOST_CMD_KIND_CONNECT_SUCCESS,
|
||||||
|
HOST_CMD_KIND_DISCONNECT,
|
||||||
|
HOST_CMD_KIND_WRITE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum host_event_kind {
|
||||||
|
HOST_EVENT_KIND_NONE,
|
||||||
|
|
||||||
|
HOST_EVENT_KIND_CHANNEL_OPENED,
|
||||||
|
HOST_EVENT_KIND_CHANNEL_CLOSED,
|
||||||
|
HOST_EVENT_KIND_MSG
|
||||||
|
};
|
||||||
|
|
||||||
|
struct host_cmd {
|
||||||
|
enum host_cmd_kind kind;
|
||||||
|
struct host_channel_id channel_id;
|
||||||
|
|
||||||
|
b32 write_reliable;
|
||||||
|
struct string write_msg;
|
||||||
|
|
||||||
|
struct host_cmd *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct host_event {
|
||||||
|
enum host_event_kind kind;
|
||||||
|
struct host_channel_id channel_id;
|
||||||
|
struct string msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct host_event_array {
|
||||||
|
struct host_event *events;
|
||||||
|
u64 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct host {
|
||||||
|
struct sock sock;
|
||||||
|
|
||||||
|
struct arena cmd_arena;
|
||||||
|
struct host_cmd *first_cmd;
|
||||||
|
struct host_cmd *last_cmd;
|
||||||
|
struct host_cmd *first_free_cmd;
|
||||||
|
|
||||||
|
struct arena queued_event_arena;
|
||||||
|
struct host_queued_event *first_queued_event;
|
||||||
|
struct host_queued_event *last_queued_event;
|
||||||
|
u64 num_queued_events;
|
||||||
|
|
||||||
|
struct arena packet_arena;
|
||||||
|
struct packet *first_free_packet;
|
||||||
|
|
||||||
|
struct arena channels_arena;
|
||||||
|
struct host_channel *channels;
|
||||||
|
u64 channels_reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Startup
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct host_startup_receipt { i32 _; };
|
||||||
|
struct host_startup_receipt host_startup(struct sock_startup_receipt *sock_sr);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Host
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct host host_alloc(struct sock_address bind_address);
|
||||||
|
|
||||||
|
void host_release(struct host *host);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Queue
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void host_queue_connect_to_address(struct host *host, struct sock_address connect_address);
|
||||||
|
|
||||||
|
void host_queue_disconnect(struct host *host, struct host_channel_id channel_id);
|
||||||
|
|
||||||
|
void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Update
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
void host_update(struct host *host);
|
||||||
|
|
||||||
|
/* ========================== *
|
||||||
|
* Events
|
||||||
|
* ========================== */
|
||||||
|
|
||||||
|
struct host_event_array host_pop_events(struct arena *arena, struct host *host);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INLINE b32 host_channel_id_is_nil(struct host_channel_id id)
|
||||||
|
{
|
||||||
|
return id.gen == 0 && id.idx == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
55
src/sock.h
55
src/sock.h
@ -1,6 +1,60 @@
|
|||||||
#ifndef SOCK_H
|
#ifndef SOCK_H
|
||||||
#define SOCK_H
|
#define SOCK_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum sock_flag {
|
||||||
|
SOCK_FLAG_NONE = 0,
|
||||||
|
SOCK_FLAG_NON_BLOCKING = (1 << 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sock {
|
||||||
|
i32 _;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sock_address_kind {
|
||||||
|
SOCK_ADDRESS_KIND_NONE,
|
||||||
|
SOCK_ADDRESS_KIND_UNBINDABLE,
|
||||||
|
|
||||||
|
/* Network addresses */
|
||||||
|
SOCK_ADDRESS_KIND_ANY_LOCAL_INTERFACE,
|
||||||
|
SOCK_ADDRESS_KIND_RAW
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sock_address {
|
||||||
|
enum sock_address_kind kind;
|
||||||
|
u8 ip[16];
|
||||||
|
u16 port;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sock_read_result {
|
||||||
|
b32 valid;
|
||||||
|
struct sock_address address;
|
||||||
|
struct string data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sock_startup_receipt { i32 _; };
|
||||||
|
struct sock_startup_receipt sock_startup(void);
|
||||||
|
|
||||||
|
struct sock_address sock_address_from_string(struct string str);
|
||||||
|
struct sock_address sock_address_from_any_local_interface_with_port(u16 port);
|
||||||
|
struct sock_address sock_address_from_any_local_interface_with_dynamic_port(void);
|
||||||
|
struct sock_address sock_address_unbindable(void);
|
||||||
|
|
||||||
|
struct sock sock_alloc(struct sock_address bind_address, u32 sock_flags);
|
||||||
|
void sock_release(struct sock *sock);
|
||||||
|
struct sock_read_result sock_read(struct sock *sock, struct string read_buff);
|
||||||
|
void sock_write(struct sock *sock, struct sock_address address, struct string data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
#define SOCK_IP_ANY_INTERFACE CPPCOMPAT_INITLIST_TYPE(struct sock_ip) { 0 }
|
#define SOCK_IP_ANY_INTERFACE CPPCOMPAT_INITLIST_TYPE(struct sock_ip) { 0 }
|
||||||
#define SOCK_PORT_DYNAMIC (0)
|
#define SOCK_PORT_DYNAMIC (0)
|
||||||
|
|
||||||
@ -58,5 +112,6 @@ void sock_write(struct sock *sock, struct sock_address address, struct string ms
|
|||||||
void sock_testsend(void);
|
void sock_testsend(void);
|
||||||
|
|
||||||
void sock_testrecv(void);
|
void sock_testrecv(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
117
src/sock_win32.c
117
src/sock_win32.c
@ -1,3 +1,117 @@
|
|||||||
|
#if 1
|
||||||
|
|
||||||
|
|
||||||
|
#include "sock.h"
|
||||||
|
#include "sys.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "arena.h"
|
||||||
|
#include "scratch.h"
|
||||||
|
|
||||||
|
#include <WinSock2.h>
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
|
||||||
|
//#define MAX_IP_STR_LEN 46
|
||||||
|
|
||||||
|
struct sock_startup_receipt sock_startup(void)
|
||||||
|
{
|
||||||
|
WSADATA wsa_data;
|
||||||
|
/* Startup winsock */
|
||||||
|
WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
||||||
|
return (struct sock_startup_receipt) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock_address sock_address_from_string(struct string str)
|
||||||
|
{
|
||||||
|
(UNUSED)str;
|
||||||
|
|
||||||
|
struct sock_address res = ZI;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
struct addrinfo *servinfo;
|
||||||
|
|
||||||
|
struct addrinfo hints = ZI;
|
||||||
|
hints.ai_family = AF_UNSPEC;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
|
status = getaddrinfo(NULL, "3490", &hints, &servinfo)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock_address sock_address_from_any_local_interface_with_port(u16 port)
|
||||||
|
{
|
||||||
|
(UNUSED)port;
|
||||||
|
|
||||||
|
struct sock_address res = ZI;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock_address sock_address_from_any_local_interface_with_dynamic_port(void)
|
||||||
|
{
|
||||||
|
struct sock_address res = ZI;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock_address sock_address_unbindable(void)
|
||||||
|
{
|
||||||
|
struct sock_address res = ZI;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock sock_alloc(struct sock_address bind_address, u32 sock_flags)
|
||||||
|
{
|
||||||
|
(UNUSED)bind_address;
|
||||||
|
(UNUSED)sock_flags;
|
||||||
|
|
||||||
|
struct sock sock = ZI;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sock_release(struct sock *sock)
|
||||||
|
{
|
||||||
|
(UNUSED)sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sock_read_result sock_read(struct sock *sock, struct string read_buff)
|
||||||
|
{
|
||||||
|
(UNUSED)sock;
|
||||||
|
(UNUSED)read_buff;
|
||||||
|
|
||||||
|
struct sock_read_result res = ZI;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sock_write(struct sock *sock, struct sock_address address, struct string data)
|
||||||
|
{
|
||||||
|
(UNUSED)sock;
|
||||||
|
(UNUSED)address;
|
||||||
|
(UNUSED)data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
#include "sys.h"
|
#include "sys.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@ -492,3 +606,6 @@ void sock_testrecv(void)
|
|||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
WSACleanup();
|
WSACleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
50
src/user.c
50
src/user.c
@ -20,6 +20,7 @@
|
|||||||
#include "rng.h"
|
#include "rng.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sock.h"
|
#include "sock.h"
|
||||||
|
#include "host.h"
|
||||||
|
|
||||||
struct bind_state {
|
struct bind_state {
|
||||||
b32 is_held; /* Is this bind held down this frame */
|
b32 is_held; /* Is this bind held down this frame */
|
||||||
@ -42,6 +43,8 @@ GLOBAL struct {
|
|||||||
|
|
||||||
struct sys_window *window;
|
struct sys_window *window;
|
||||||
|
|
||||||
|
struct host host;
|
||||||
|
|
||||||
/* Render targets */
|
/* Render targets */
|
||||||
struct renderer_texture final_texture;
|
struct renderer_texture final_texture;
|
||||||
struct renderer_texture world_texture;
|
struct renderer_texture world_texture;
|
||||||
@ -137,7 +140,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct game_startup_receipt *game_sr,
|
struct game_startup_receipt *game_sr,
|
||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
struct asset_cache_startup_receipt *asset_cache_sr,
|
||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct sock_startup_receipt *sock_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
struct sys_window *window)
|
struct sys_window *window)
|
||||||
{
|
{
|
||||||
(UNUSED)work_sr;
|
(UNUSED)work_sr;
|
||||||
@ -148,14 +151,14 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
(UNUSED)game_sr;
|
(UNUSED)game_sr;
|
||||||
(UNUSED)asset_cache_sr;
|
(UNUSED)asset_cache_sr;
|
||||||
(UNUSED)mixer_sr;
|
(UNUSED)mixer_sr;
|
||||||
(UNUSED)sock_sr;
|
(UNUSED)host_sr;
|
||||||
|
|
||||||
G.arena = arena_alloc(GIGABYTE(64));
|
G.arena = arena_alloc(GIGABYTE(64));
|
||||||
G.sys_events_mutex = sys_mutex_alloc();
|
G.sys_events_mutex = sys_mutex_alloc();
|
||||||
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
G.sys_events_arena = arena_alloc(GIGABYTE(64));
|
||||||
world_alloc(&G.world);
|
world_alloc(&G.world);
|
||||||
|
|
||||||
struct net_address_desc bind_addr = NET_ADDRESS_DESC_ALL_LOCAL_INTERFACES(NET_PORT_DYNAMIC);
|
struct sock_address bind_addr = sock_address_from_any_local_interface_with_dynamic_port();
|
||||||
G.host = host_alloc(bind_addr);
|
G.host = host_alloc(bind_addr);
|
||||||
|
|
||||||
G.world_to_ui_xf = XFORM_IDENT;
|
G.world_to_ui_xf = XFORM_IDENT;
|
||||||
@ -215,6 +218,7 @@ INTERNAL SYS_WINDOW_EVENT_CALLBACK_FUNC_DEF(window_event_callback, event)
|
|||||||
* Game -> user communication
|
* Game -> user communication
|
||||||
* ========================== */
|
* ========================== */
|
||||||
|
|
||||||
|
#if 0
|
||||||
INTERNAL struct blend_tick *blend_tick_alloc(void)
|
INTERNAL struct blend_tick *blend_tick_alloc(void)
|
||||||
{
|
{
|
||||||
struct blend_tick *bt = NULL;
|
struct blend_tick *bt = NULL;
|
||||||
@ -361,6 +365,7 @@ INTERNAL struct interp_ticks pull_ticks(i64 blend_time_ns)
|
|||||||
.to_tick = to_tick
|
.to_tick = to_tick
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Debug draw
|
* Debug draw
|
||||||
@ -455,7 +460,17 @@ INTERNAL SORT_COMPARE_FUNC_DEF(entity_draw_order_cmp, arg_a, arg_b, udata)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INTERNAL void queue_game_cmd(struct arena *arena, struct game_cmd_list *list, struct game_cmd src)
|
||||||
|
{
|
||||||
|
struct game_cmd *cmd = arena_push(arena, struct game_cmd);
|
||||||
|
*cmd = src;
|
||||||
|
if (list->last) {
|
||||||
|
list->last->next = cmd;
|
||||||
|
} else {
|
||||||
|
list->first = cmd;
|
||||||
|
}
|
||||||
|
list->last = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -588,7 +603,7 @@ INTERNAL void user_update(void)
|
|||||||
|
|
||||||
struct game_event_list game_events = ZI;
|
struct game_event_list game_events = ZI;
|
||||||
{
|
{
|
||||||
struct host_events_array = host_pop_events(scratch.arena, G.host);
|
struct host_event_array host_events = host_pop_events(scratch.arena, &G.host);
|
||||||
game_events_from_host_events(scratch.arena, host_events, &game_events);
|
game_events_from_host_events(scratch.arena, host_events, &game_events);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,14 +618,13 @@ INTERNAL void user_update(void)
|
|||||||
static f64 last_try_connect = 0;
|
static f64 last_try_connect = 0;
|
||||||
f64 now = SECONDS_FROM_NS(sys_time_ns());
|
f64 now = SECONDS_FROM_NS(sys_time_ns());
|
||||||
if (last_try_connect == 0 || (now - last_try_connect) > 5) {
|
if (last_try_connect == 0 || (now - last_try_connect) > 5) {
|
||||||
struct host_address_desc connect_addr = host_address_desc_from_string(LIT("127.0.0.1:12345"));
|
struct sock_address connect_addr = sock_address_from_string(LIT("127.0.0.1:12345"));
|
||||||
host_connect_to_address(G.host, connect_addr);
|
host_queue_connect_to_address(&G.host, connect_addr);
|
||||||
last_try_connect = now;
|
last_try_connect = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u64 i = 0; i < game_events.count; ++i) {
|
for (struct game_event *event = game_events.first; event; event = event->next) {
|
||||||
struct game_event event = game_events.events[i];
|
enum game_event_kind kind = event->kind;
|
||||||
enum game_event_kind kind = event.kind;
|
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case GAME_EVENT_KIND_CONNECT:
|
case GAME_EVENT_KIND_CONNECT:
|
||||||
@ -623,10 +637,10 @@ INTERNAL void user_update(void)
|
|||||||
last_try_connect = 0;
|
last_try_connect = 0;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GAME_EVENT_KIND_SNAPSHOT:
|
case GAME_EVENT_KIND_SNAPSHOT_FULL:
|
||||||
{
|
{
|
||||||
struct string data = event.data;
|
struct string snapshot_data = event->snapshot_data;
|
||||||
game_tick_from_string(&G.world, data);
|
game_tick_from_string(snapshot_data, &G.world);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
@ -1656,17 +1670,13 @@ INTERNAL void user_update(void)
|
|||||||
{
|
{
|
||||||
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
struct temp_arena temp = arena_temp_begin(scratch.arena);
|
||||||
|
|
||||||
struct string cmds_str = game_string_from_cmds(temp.arena, game_cmds_list);
|
struct string cmds_str = game_string_from_cmds(temp.arena, cmd_list);
|
||||||
|
host_queue_write(&G.host, HOST_CHANNEL_ID_ALL, cmds_str);
|
||||||
struct host_msg_desc msg = ZI;
|
|
||||||
msg.channel = CHANNEL_ID_ALL;
|
|
||||||
msg.data = cmds_str;
|
|
||||||
host_queue_write(G.host, msg);
|
|
||||||
|
|
||||||
arena_temp_end(temp);
|
arena_temp_end(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
host_update(G.host);
|
host_update(&G.host);
|
||||||
|
|
||||||
/* ========================== *
|
/* ========================== *
|
||||||
* Render
|
* Render
|
||||||
|
|||||||
@ -10,7 +10,7 @@ struct draw_startup_receipt;
|
|||||||
struct game_startup_receipt;
|
struct game_startup_receipt;
|
||||||
struct asset_cache_startup_receipt;
|
struct asset_cache_startup_receipt;
|
||||||
struct mixer_startup_receipt;
|
struct mixer_startup_receipt;
|
||||||
struct sock_startup_receipt;
|
struct host_startup_receipt;
|
||||||
|
|
||||||
enum user_bind_kind {
|
enum user_bind_kind {
|
||||||
USER_BIND_KIND_NONE,
|
USER_BIND_KIND_NONE,
|
||||||
@ -57,7 +57,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
|
|||||||
struct game_startup_receipt *game_sr,
|
struct game_startup_receipt *game_sr,
|
||||||
struct asset_cache_startup_receipt *asset_cache_sr,
|
struct asset_cache_startup_receipt *asset_cache_sr,
|
||||||
struct mixer_startup_receipt *mixer_sr,
|
struct mixer_startup_receipt *mixer_sr,
|
||||||
struct sock_startup_receipt *sock_sr,
|
struct host_startup_receipt *host_sr,
|
||||||
struct sys_window *window);
|
struct sys_window *window);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user