host progress

This commit is contained in:
jacob 2025-02-06 14:32:06 -06:00
parent a80eff9e39
commit 627f736345
13 changed files with 820 additions and 304 deletions

View File

@ -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();

View File

@ -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) {

View File

@ -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

View File

@ -506,6 +506,11 @@ struct space_entry_handle {
u64 gen; u64 gen;
}; };
struct host_channel_id {
u32 gen;
u32 idx;
};
/* ========================== * /* ========================== *
* Tag structs * Tag structs
* ========================== */ * ========================== */

View File

@ -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 */

View File

@ -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);

View File

@ -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

View File

@ -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;
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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