diff --git a/src/app.c b/src/app.c index 0bf0c4b6..50dcdd21 100644 --- a/src/app.c +++ b/src/app.c @@ -246,6 +246,7 @@ void app_entry_point(void) /* Startup systems */ struct sock_startup_receipt sock_sr = sock_startup(); + struct host_startup_receipt host_sr = host_startup(&sock_sr); struct resource_startup_receipt resource_sr = resource_startup(); struct rng_startup_receipt rng_sr = rng_startup(&resource_sr); struct renderer_startup_receipt renderer_sr = renderer_startup(&window); @@ -258,11 +259,10 @@ void app_entry_point(void) struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr); struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr); struct phys_startup_receipt phys_sr = phys_startup(); - struct game_startup_receipt game_sr = game_startup(&mixer_sr, &sprite_sr, &sound_sr, &phys_sr, &sock_sr); - struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &game_sr, &asset_cache_sr, &mixer_sr, &sock_sr, &window); + struct game_startup_receipt game_sr = game_startup(&mixer_sr, &sprite_sr, &sound_sr, &phys_sr, &host_sr); + struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &sprite_sr, &draw_sr, &game_sr, &asset_cache_sr, &mixer_sr, &host_sr, &window); struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr); - (UNUSED)sock_sr; (UNUSED)user_sr; (UNUSED)playback_sr; (UNUSED)rng_sr; diff --git a/src/byteio.c b/src/byteio.c index 5994cc72..74418c5a 100644 --- a/src/byteio.c +++ b/src/byteio.c @@ -91,9 +91,9 @@ void bw_seek_to(struct byte_writer *bw, u64 pos) bw->at = bw->buff.text + pos; } -void bw_write_string(struct byte_writer *bw, struct string s) +void bw_write_buffer(struct byte_writer *bw, struct string buff) { - write(bw, s.text, s.len); + write(bw, buff.text, buff.len); } void bw_write_u8(struct byte_writer *bw, u8 v) @@ -199,11 +199,10 @@ struct byte_reader br_from_buffer(struct string buff) /* Returns pointer to old position, or NULL on overflow */ void *br_seek(struct byte_reader *br, u64 amount) { - void *ptr = NULL; if (read_check_overflow(br, amount)) { - return ptr; + return NULL; } - ptr = br->at; + void *ptr = br->at; br->at += amount; return ptr; } @@ -211,17 +210,16 @@ void *br_seek(struct byte_reader *br, u64 amount) /* Returns pointer to old position, or NULL on overflow */ void *br_seek_to(struct byte_reader *br, u64 pos) { - void *ptr = NULL; if (read_check_overflow(br, (i64)pos - (i64)(br->at - br->buff.text))) { - return ptr; + return NULL; } - ptr = br->at; + void *ptr = br->at; br->at = br->buff.text + pos; return ptr; } /* Will fill buff with zeroes on overflow */ -void br_read_to_string(struct byte_reader *br, struct string buff) +void br_read_to_buffer(struct byte_reader *br, struct string buff) { read(br, buff.text, buff.len); } @@ -284,13 +282,13 @@ i64 br_read_i64(struct byte_reader *br) u64 br_read_var_uint(struct byte_reader *br) { - /* TODO: real varint read */ + /* TODO: real variable length read */ return br_read_u64(br); } i64 br_read_var_sint(struct byte_reader *br) { - /* TODO: real varint read */ + /* TODO: real variable length read */ return br_read_i64(br); } diff --git a/src/byteio.h b/src/byteio.h index 6a5fe808..3bb3c152 100644 --- a/src/byteio.h +++ b/src/byteio.h @@ -25,7 +25,7 @@ struct byte_writer bw_branch(struct byte_writer *bw, u64 size); void bw_seek(struct byte_writer *bw, u64 amount); void bw_seek_to(struct byte_writer *bw, u64 pos); -void bw_write_string(struct byte_writer *bw, struct string s); +void bw_write_buffer(struct byte_writer *bw, struct string buff); void bw_write_u8(struct byte_writer *bw, u8 v); void bw_write_u16(struct byte_writer *bw, u16 v); void bw_write_u32(struct byte_writer *bw, u32 v); @@ -59,14 +59,14 @@ INLINE u64 bw_pos(struct byte_writer *bw) * ========================== */ /* Will fill struct with zeroes on overflow */ -#define br_read_to_struct(br_ptr, var_ptr) (br_read_to_string(br_ptr, STRING(sizeof(*var_ptr), (u8 *)var_ptr))) +#define br_read_to_struct(br_ptr, var_ptr) (br_read_to_buffer(br_ptr, STRING(sizeof(*var_ptr), (u8 *)var_ptr))) struct byte_reader br_from_buffer(struct string buff); void *br_seek(struct byte_reader *br, u64 amount); void *br_seek_to(struct byte_reader *br, u64 pos); -void br_read_to_string(struct byte_reader *br, struct string s); +void br_read_to_buffer(struct byte_reader *br, struct string s); u8 br_read_u8(struct byte_reader *br); u16 br_read_u16(struct byte_reader *br); u32 br_read_u32(struct byte_reader *br); diff --git a/src/client.c b/src/client.c new file mode 100644 index 00000000..a1e9daf7 --- /dev/null +++ b/src/client.c @@ -0,0 +1,137 @@ +#include "client.h" +#include "host.h" + +#define CHANNEL_LOOKUP_BUCKETS 4096 + +/* Offset in bytes from start of store struct to start of clients array (assume adjacently allocated) */ + +/* FIXME: Incorrect since buckets are also allocated */ +#define STORE_CLIENTS_OFFSET (sizeof(struct client_store) + (sizeof(struct client_store) % alignof(struct client))) + + +/* Accessed via client_nil() */ +READONLY struct client _g_client_nil = { .valid = false }; +READONLY struct client_store _g_client_store_nil = { .valid = false }; + +/* ========================== * + * Store + * ========================== */ + +struct client_store *client_store_alloc(void) +{ + struct arena arena = ARENA_ALLOC(GIGABYTE(64)); + struct client_store *store = arena_push_zero(&arena, struct client_store); + store->arena = arena; + 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->clients = arena_dry_push(store, struct client); + return store; +} + +void client_store_release(struct client_store *store) +{ + arena_release(&store->arena); +} + +struct client_store *client_store_from_client(struct client *client) +{ + if (client->valid) { + u64 first_client_addr = (u64)(client - client->handle.idx); + struct client_store *client_store = (struct client_store *)(first_client_addr - STORE_CLIENTS_OFFSET); + ASSERT(client_store->clients == (struct client *)first_client_addr); + return client_store; + } else { + return client_store_nil(); + } +} + +/* ========================== * + * Client + * ========================== */ + +struct client *client_from_handle(struct client_store *store, struct client_handle handle) +{ + if (handle.gen != 0 && handle.idx < store->clients_reserved) { + struct client *client = &store->clients[handle.idx]; + if (client->handle.gen == handle.gen) { + return client; + } + } + return client_nil(); +} + +struct client *client_from_channel_id(struct client_store *store, struct host_channel_id channel_id) +{ + struct client *res = client_nil(); + u64 channel_hash = hash_from_channel_id(channel_id); + u64 bucket_index = channel_hash % store->num_channel_lookup_buckets; + struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[index]; + for (struct client *client = bucket->first; client; client = client->next_hash) { + if (client->channel_hash == channel_hash) { + res = client; + break; + } + } + return res; +} + +struct client client_alloc(struct client_store *store, struct host_channel_id channel_id) +{ + struct client *client = NULL; + struct client_handle handle = ZI; + if (store->first_free_client) { + client = store->first_free_client; + store->first_free_client = client->next_free; + handle = client->handle; + ++handle.gen; + } else { + client = arena_push(&store->arena, struct client); + handle.gen = 1; + handle.idx = store->clients_reserved; + ++store->clients_reserved; + } + client = _g_client_nil; + client->valid = true; + client->handle = handle; + + u64 channel_hash = hash_from_channel_id(channel_id); + client->channel_id = channel_id; + client->channel_hash = channel_hash; + + /* Insert into channel lookup */ + u64 bucket_index = channel_hash % store->num_channel_lookup_buckets; + struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[index]; + if (bucket->last) { + bucket->last->next_hash = client; + client->prev_hash = bucket->last; + } else { + bucket->first = client; + } + bucket->last = client; +} + +void client_release(struct client *client) +{ + struct client_store *store = client_store_from_client(client); + client->valid = false; + ++client->handle.gen; + client->next_free = store->first_free_client; + store->first_free_client = client; + + /* Remove from channel lookup */ + u64 channel_hash = hash_from_channel_id(channel_id); + u64 bucket_index = channel_hash % store->num_channel_lookup_buckets; + struct channel_lookup_bucket *bucket = &store->channel_lookup_buckets[index]; + struct client *prev = client->prev_hash; + struct client *next = client->next_hash; + if (prev) { + prev->next_hash = next; + } else { + bucket->first = next; + } + if (next) { + next->prev_hash = prev; + } else { + bucket->last = prev; + } +} diff --git a/src/client.h b/src/client.h new file mode 100644 index 00000000..1a54aa71 --- /dev/null +++ b/src/client.h @@ -0,0 +1,55 @@ +#ifndef CLIENT_H +#define CLIENT_H + +struct client_handle { + b32 valid; + u32 idx; + u32 gen; +}; + +struct client { + b32 valid; + struct host_channel_id channel_id; + struct client *next_free; + struct client *next_hash; + struct client *prev_hash; + + struct entity_handle ent; +}; + +struct channel_lookup_bucket { + struct client *first; + struct client *last; +}; + +struct client_store { + struct arena arena; + + struct channel_lookup_bucket channel_lookup_buckets; + u64 num_channel_lookup_buckets; + + struct client *clients; + u64 clients_reserved; +}; + +INLINE struct client *client_nil(void) +{ + extern READONLY struct client _g_client_nil; + return &_g_client_nil; +} + +INLINE struct client *client_store_nil(void) +{ + extern READONLY struct client_store _g_client_store_nil; + return &_g_client_store_nil; +} + +struct client_store *client_store_alloc(void); +void client_store_release(struct client_store *store); +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_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); +void client_release(struct client *client); + +#endif diff --git a/src/common.h b/src/common.h index 586c0bed..66f6570e 100644 --- a/src/common.h +++ b/src/common.h @@ -501,7 +501,7 @@ struct entity_handle { u64 gen; }; -struct space_client_handle { +struct space_entry_handle { u64 idx; u64 gen; }; diff --git a/src/entity.c b/src/entity.c index c9d8d5ae..cd0a0683 100644 --- a/src/entity.c +++ b/src/entity.c @@ -5,7 +5,7 @@ #define STORE_ENTITIES_OFFSET (sizeof(struct entity_store) + (sizeof(struct entity_store) % alignof(struct entity))) /* Accessed via entity_store_nil() */ -READONLY struct entity_store _g_entity_store_nil = ZI; +READONLY struct entity_store _g_entity_store_nil = { .valid = false }; /* Accessed via entity_nil() */ /* TODO: Allocate nil entity in nil store */ @@ -21,18 +21,6 @@ READONLY struct entity _g_entity_nil = { .sprite_tint = COLOR_WHITE }; -GLOBAL READONLY struct entity g_entity_default = { - .valid = true, - .local_xform = XFORM_IDENT_NOCAST, - .cached_global_xform = XFORM_IDENT_NOCAST, - .cached_global_xform_dirty = true, - .friction = 0.5f, - .mass_unscaled = 1, - .inertia_unscaled = 1, - .sprite_local_xform = XFORM_IDENT_NOCAST, - .sprite_tint = COLOR_WHITE -}; - /* ========================== * * Store allocation * ========================== */ @@ -53,6 +41,7 @@ struct entity_store *entity_store_alloc(void) { struct arena arena = arena_alloc(GIGABYTE(64)); struct entity_store *store = arena_push_zero(&arena, struct entity_store); + store->valid = true; store->arena = arena; store->entities = arena_dry_push(&arena, struct entity); ASSERT((u64)store->entities - (u64)store == STORE_ENTITIES_OFFSET); /* Offset must be correct */ @@ -94,8 +83,10 @@ INTERNAL struct entity *entity_alloc_internal(struct entity_store *store) entity = arena_push(&store->arena, struct entity); handle = (struct entity_handle) { .gen = 1, .idx = store->reserved++ }; } - *entity = g_entity_default; + *entity = _g_entity_nil; + entity->valid = true; entity->handle = handle; + entity->cached_global_xform_dirty = true; ++store->allocated; return entity; } diff --git a/src/entity.h b/src/entity.h index 72bc0772..623ebfca 100644 --- a/src/entity.h +++ b/src/entity.h @@ -47,6 +47,7 @@ enum entity_prop { }; struct entity_store { + b32 valid; struct arena arena; u64 allocated; u64 reserved; @@ -133,7 +134,7 @@ struct entity { struct phys_collision_debug collision_debug_data; #endif - struct space_client_handle space_handle; + struct space_entry_handle space_handle; /* ====================================================================== */ /* Contact constraint */ diff --git a/src/game.c b/src/game.c index d7bd7989..f9bdf78e 100644 --- a/src/game.c +++ b/src/game.c @@ -15,7 +15,7 @@ #include "rng.h" #include "space.h" #include "byteio.h" -#include "sock.h" +#include "client.h" GLOBAL struct { struct atomic_i32 game_thread_shutdown; @@ -23,9 +23,10 @@ GLOBAL struct { u64 last_phys_iteration; - b32 paused; struct sprite_scope *sprite_frame_scope; + struct host host; + /* For debugging */ struct v2 user_cursor; @@ -33,10 +34,6 @@ GLOBAL struct { b32 extra_spawn; b32 should_reset_level; - /* Game input */ - struct sys_mutex game_input_mutex; - struct arena game_input_arena; - struct entity *root; /* Bookkeeping structures */ @@ -45,12 +42,9 @@ GLOBAL struct { struct entity_lookup collision_debug_lookup; #endif struct space *space; + struct client_store *client_store; - /* Ticks */ - struct sys_mutex prev_tick_mutex; - struct atomic_u64 prev_tick_id; - struct atomic_u64 prev_tick_continuity_gen; - struct world prev_tick; + /* Tick */ struct world tick; } G = ZI, DEBUG_ALIAS(G, G_game); @@ -66,27 +60,27 @@ struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr, struct sprite_startup_receipt *sheet_sr, struct sound_startup_receipt *sound_sr, struct phys_startup_receipt *phys_sr, - struct sock_startup_receipt *sock_sr) + struct host_startup_receipt *host_sr) { (UNUSED)mixer_sr; (UNUSED)sheet_sr; (UNUSED)sound_sr; (UNUSED)phys_sr; - (UNUSED)sock_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(); + + /* Intialize host */ + struct host_address_desc bind_address = HOST_ADDRESS_ALL_LOCAL_INTERFACES(12345); + G.host = host_alloc(bind_address); + /* Initialize empty world */ reset_world(); - /* Initialize prev tick */ - world_alloc(&G.prev_tick); - /* FIXME: Make the world struct itself readonly as well */ - arena_set_readonly(&G.prev_tick.entity_store->arena); - G.prev_tick_mutex = sys_mutex_alloc(); - G.game_thread = sys_thread_alloc(&game_thread_entry_point, NULL, LIT("[P2] Game thread")); app_register_exit_callback(&game_shutdown); @@ -330,11 +324,11 @@ INTERNAL void release_entities_with_prop(enum entity_prop prop) /* Release references */ for (u64 i = 0; i < ents_to_release_count; ++i) { struct entity *ent = ents_to_release[i]; - /* Release space client */ + /* Release space entry */ { - struct space_client *space_client = space_client_from_handle(space, ent->space_handle); - if (space_client->valid) { - space_client_release(space_client); + struct space_entry *space_entry = space_entry_from_handle(space, ent->space_handle); + if (space_entry->valid) { + space_entry_release(space_entry); } } } @@ -443,69 +437,11 @@ INTERNAL PHYS_COLLISION_CALLBACK_FUNC_DEF(on_collision, array) * Update * ========================== */ -INTERNAL void publish_game_tick(void) -{ - __prof; - struct sys_lock lock = sys_mutex_lock_e(&G.prev_tick_mutex); - arena_set_readwrite(&G.prev_tick.entity_store->arena); - { - world_copy_replace(&G.prev_tick, &G.tick); - } - arena_set_readonly(&G.prev_tick.entity_store->arena); - atomic_u64_eval_exchange(&G.prev_tick_id, G.prev_tick.tick_id); - atomic_u64_eval_exchange(&G.prev_tick_continuity_gen, G.prev_tick.continuity_gen); - sys_mutex_unlock(&lock); -} - -INTERNAL void game_update(struct game_cmd_list game_cmds) +INTERNAL void game_update() { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); - - - - - - - - - - - { - static struct sock sock = ZI; - if (!sock.handle) { - struct sock_address bind_addr = ZI; - bind_addr.ip = SOCK_IP_ANY_INTERFACE; - bind_addr.port = 12345; - sock = sock_alloc(bind_addr, SOCK_FLAG_NON_BLOCKING_RECV); - } - } - - - - - - - - - - - - - - - - - - - - - - - - - /* ========================== * * Reset level if necessary @@ -533,7 +469,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) G.root = entity_from_handle(G.tick.entity_store, G.tick.entity_store->root); struct entity *root = G.root; - struct entity_store *store = G.tick.entity_store; + struct entity_store *entity_store = G.tick.entity_store; struct space *space = G.space; struct sprite_scope *sprite_frame_scope = G.sprite_frame_scope; @@ -554,34 +490,6 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) } } - /* ========================== * - * Process global game cmds - * ========================== */ - - for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) { - switch (cmd->kind) { - /* Cursor */ - case GAME_CMD_KIND_CURSOR_MOVE: - { - G.user_cursor = cmd->cursor_pos; - } break; - - /* Clear level */ - case GAME_CMD_KIND_CLEAR_ALL: - { - G.should_reset_level = true; - } break; - - /* Spawn test */ - case GAME_CMD_KIND_SPAWN_TEST: - { - logf_info("Spawning (test)"); - spawn_test_entities(); - } break; - default: break; - }; - } - /* ========================== * * Release entities * ========================== */ @@ -592,8 +500,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Activate entities * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!ent->valid) continue; if (!entity_has_prop(ent, ENTITY_PROP_ACTIVE)) { @@ -608,8 +516,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Reset triggered entities * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_TRIGGER_NEXT_TICK)) { @@ -621,11 +529,77 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) } /* ========================== * - * Update entity from sprite + * Process host events into game cmds * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + + struct game_cmd_list game_cmds = ZI; + { + struct host_event_array host_events = host_events_pop(scratch.arena, G.host); + game_cmds_from_host_events(scratch.arena, host_events, &game_cmds); + } + + /* ========================== * + * Process game cmds + * ========================== */ + + for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) { + enum game_cmd_kind kind = cmd->kind; + struct host_channel_id channel_id = cmd->client_channel_id; + + struct client *client = client_from_channel_id(channel_id); + if (client->valid || channel_id == HOST_CHANNEL_ID_NIL) { + switch (kind) { + /* Cursor */ + case GAME_CMD_KIND_CURSOR_MOVE: + { + G.user_cursor = cmd->cursor_pos; + } break; + + /* Clear level */ + case GAME_CMD_KIND_CLEAR_ALL: + { + G.should_reset_level = true; + } break; + + /* Spawn test */ + case GAME_CMD_KIND_SPAWN_TEST: + { + logf_info("Spawning (test)"); + spawn_test_entities(); + } break; + + /* Disconnect client */ + case GAME_CMD_KIND_CLIENT_DISCONNECT: + { + if (client->valid) { + struct entity *client_ent = entity_from_handle(client->ent); + if (client_ent->valid) { + entity_disable_prop(client_ent, ENTITY_PROP_CLIENT_CONTROLLED); + entity_enable_prop(client_ent, ENTITY_PROP_RELEASE_NEXT_TICK); + host_queue_disconnect(&G.host, channel_id); + } + client_release(client); + } + } break; + + default: break; + }; + } else if (kind == GAME_CMD_KIND_CLIENT_CONNECT && channel_id != HOST_CHANNEL_ID_NIL && !client->valid) { + /* Connect client */ + struct client *client = client_alloc(G.client_store, channel_id); + struct client_ent *client_ent = entity_alloc(root); + entity_enable_prop(client_ent, ENTITY_PROP_CLIENT_CONTROLLED); + client_ent->client_handle = client->handle; + } + } + + /* ========================== * + * Update entities from sprite + * ========================== */ + + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (sprite_tag_is_nil(ent->sprite)) continue; @@ -719,12 +693,12 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Update attachments * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_ATTACHED)) continue; - struct entity *parent = entity_from_handle(store, ent->parent); + struct entity *parent = entity_from_handle(entity_store, ent->parent); struct sprite_tag parent_sprite = parent->sprite; struct sprite_sheet *parent_sheet = sprite_sheet_from_tag_await(sprite_frame_scope, parent_sprite); @@ -744,8 +718,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Update control from player cmds * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { @@ -803,8 +777,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * ========================== */ #if 0 - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_TEST)) continue; @@ -853,12 +827,12 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Trigger equipped * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED)) { - struct entity *eq = entity_from_handle(store, ent->equipped); + struct entity *eq = entity_from_handle(entity_store, ent->equipped); if (entity_is_valid_and_active(eq)) { entity_enable_prop(eq, ENTITY_PROP_TRIGGERED_THIS_TICK); } @@ -869,8 +843,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Process triggered entities * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK)) continue; if ((time - ent->last_triggered < ent->trigger_delay) && ent->last_triggered != 0) continue; @@ -933,8 +907,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * ========================== */ #if 0 - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { @@ -944,12 +918,12 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) } } #else - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { - struct entity *joint_ent = entity_from_handle(store, ent->move_joint); + struct entity *joint_ent = entity_from_handle(entity_store, ent->move_joint); if (!entity_is_valid_and_active(joint_ent)) { joint_ent = entity_alloc(root); joint_ent->mass_unscaled = F32_INFINITY; @@ -978,8 +952,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * ========================== */ #if GAME_PLAYER_AIM - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_PLAYER_CONTROLLED)) { @@ -987,7 +961,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) struct xform sprite_xf = xform_mul(xf, ent->sprite_local_xform); /* Retrieve / create aim joint */ - struct entity *joint_ent = entity_from_handle(store, ent->aim_joint); + struct entity *joint_ent = entity_from_handle(entity_store, ent->aim_joint); if (!entity_is_valid_and_active(joint_ent)) { joint_ent = entity_alloc(root); joint_ent->mass_unscaled = F32_INFINITY; @@ -1071,12 +1045,12 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Create ground friction force (gravity) * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_PHYSICAL_DYNAMIC)) continue; - struct entity *joint_ent = entity_from_handle(store, ent->ground_friction_joint); + struct entity *joint_ent = entity_from_handle(entity_store, ent->ground_friction_joint); struct phys_motor_joint_def def = ZI; def.e0 = root->handle; @@ -1102,7 +1076,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) { struct phys_ctx ctx = ZI; ctx.tick_id = G.tick.tick_id; - ctx.store = store; + ctx.store = entity_store; ctx.space = space; ctx.contact_lookup = &G.contact_lookup; ctx.pre_solve_callback = on_collision; @@ -1130,8 +1104,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Update tracers * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_TRACER)) continue; @@ -1154,13 +1128,13 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Initialize bullet kinematics from sources * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_BULLET)) continue; if (ent->activation_tick == G.tick.tick_id) { - struct entity *src = entity_from_handle(store, ent->bullet_src); + struct entity *src = entity_from_handle(entity_store, ent->bullet_src); struct xform src_xf = entity_get_xform(src); struct v2 pos = xform_mul_v2(src_xf, ent->bullet_src_pos); @@ -1171,7 +1145,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) /* Add shooter velocity to bullet */ { /* TODO: Add angular velocity as well? */ - struct entity *top = entity_from_handle(store, src->top); + struct entity *top = entity_from_handle(entity_store, src->top); impulse = v2_add(impulse, v2_mul(top->linear_velocity, dt)); } #endif @@ -1183,7 +1157,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) entity_apply_linear_impulse_to_center(ent, impulse); /* Initialize tracer */ - struct entity *tracer = entity_from_handle(store, ent->bullet_tracer); + struct entity *tracer = entity_from_handle(entity_store, ent->bullet_tracer); if (entity_is_valid_and_active(tracer)) { entity_set_xform(tracer, xf); entity_enable_prop(tracer, ENTITY_PROP_PHYSICAL_KINEMATIC); @@ -1209,8 +1183,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Update cameras * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_CAMERA)) continue; @@ -1218,7 +1192,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) /* Camera follow */ { - struct entity *follow = entity_from_handle(store, ent->camera_follow); + struct entity *follow = entity_from_handle(entity_store, ent->camera_follow); f32 aspect_ratio = 1.0; { @@ -1250,8 +1224,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) { /* TODO: Update based on distance to quake */ ent->shake = 0; - for (u64 quake_ent_index = 0; quake_ent_index < store->reserved; ++quake_ent_index) { - struct entity *quake = &store->entities[quake_ent_index]; + for (u64 quake_ent_index = 0; quake_ent_index < entity_store->reserved; ++quake_ent_index) { + struct entity *quake = &entity_store->entities[quake_ent_index]; if (!entity_is_valid_and_active(quake)) continue; if (!entity_has_prop(quake, ENTITY_PROP_QUAKE)) continue; ent->shake += quake->quake_intensity; @@ -1265,8 +1239,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Update quakes * ========================== */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (!entity_has_prop(ent, ENTITY_PROP_QUAKE)) continue; @@ -1293,7 +1267,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) --stack_count; i32 parent_layer = parent->final_layer; - for (struct entity *child = entity_from_handle(store, parent->first); child->valid; child = entity_from_handle(store, child->next)) { + for (struct entity *child = entity_from_handle(entity_store, parent->first); child->valid; child = entity_from_handle(entity_store, child->next)) { if (entity_is_valid_and_active(child)) { child->final_layer = parent_layer + child->layer; *arena_push(temp.arena, struct entity *) = child; @@ -1313,8 +1287,8 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * user thread. This is so sounds play at the correct time on the user * thread regardless of interp delay. */ - for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) { - struct entity *ent = &store->entities[entity_index]; + for (u64 entity_index = 0; entity_index < entity_store->reserved; ++entity_index) { + struct entity *ent = &entity_store->entities[entity_index]; if (!entity_is_valid_and_active(ent)) continue; if (entity_has_prop(ent, ENTITY_PROP_TEST_SOUND_EMITTER)) { @@ -1344,9 +1318,23 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Publish tick * ========================== */ - /* Publish tick */ - G.tick.publishtime_ns = sys_time_ns(); - publish_game_tick(); + { + struct temp_arena temp = arena_temp_begin(scratch.arena); + + /* TODO: Not like this */ + struct game_event_desc snapshot_ed = ZI; + snapshot_ed.kind = GAME_EVENT_KIND_SNAPSHOT_FULL; + snapshot_ed.data = game_string_from_tick(&G.tick); + + struct host_msg_desc msg = ZI; + msg.channel = HOST_CHANNEL_ALL; + msg.data = snapshot; + host_queue_write(G.host, msg); + + temp_arena_end(temp); + } + + host_update(G.host); __profframe("Game"); /* ========================== * @@ -1362,7 +1350,7 @@ INTERNAL void game_update(struct game_cmd_list game_cmds) * Game cmd * ========================== */ -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) { struct byte_writer bw = bw_from_arena(arena); @@ -1396,110 +1384,156 @@ struct string game_string_from_cmds(struct arena *arena, struct game_cmd_list *c bw_write_u64(&bw_size, size); } - return bw_get_written(&bw); } -struct game_cmd_list game_cmds_from_string(struct arena *arena, struct string str) +void game_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_cmd *cmds_out) { - struct game_cmd_list l = ZI; - - struct byte_reader br = br_from_buffer(str); - while (br_bytes_left(&br) > 0) { + for (u64 i = 0; i < host_events.count; ++i) { struct game_cmd *cmd = arena_push_zero(arena, struct game_cmd); - u64 cmd_size = br_read_u64(&br); - u64 cmd_pos_end = br_pos(&br) + cmd_size; - - cmd->kind = br_read_i8(&br); - cmd->state = br_read_i8(&br); - -#if RTC - cmd->collider_gjk_steps = br_read_u32(&br); -#endif - - switch (cmd->kind) { - case GAME_CMD_KIND_PLAYER_MOVE: + struct host_channel_event host_event = host_events.events[i]; + enum host_event_kind host_event_kind = host_event.kind; + cmd->channel_id = host_event.channel_id; + switch (host_event_kind) { + case HOST_EVENT_KIND_CHANNEL_OPENED: { - cmd->move_dir = br_read_v2(&br); - cmd->aim_dir = br_read_v2(&br); + cmd->kind = GAME_CMD_KIND_CLIENT_CONNECT; } break; - case GAME_CMD_KIND_CURSOR_MOVE: + case HOST_EVENT_KIND_CHANNEL_CLOSED: { - cmd->cursor_pos = br_read_v2(&br); + cmd->kind = GAME_CMD_KIND_CLIENT_DISCONNECT; + cmd->disconnect_reason = LIT("Connection lost"); + } break; + + case HOST_EVENT_KIND_MSG: + { + struct byte_reader br = br_from_buffer(event.msg); + while (br_bytes_left(&br) > 0) { + u64 cmd_size = br_read_u64(&br); + u64 cmd_pos_end = br_pos(&br) + cmd_size; + + cmd->kind = br_read_i8(&br); + cmd->state = br_read_i8(&br); + +#if RTC + cmd->collider_gjk_steps = br_read_u32(&br); +#endif + + switch (cmd->kind) { + case GAME_CMD_KIND_PLAYER_MOVE: + { + cmd->move_dir = br_read_v2(&br); + cmd->aim_dir = br_read_v2(&br); + } break; + + case GAME_CMD_KIND_CURSOR_MOVE: + { + cmd->cursor_pos = br_read_v2(&br); + } break; + + default: break; + } + + ASSERT(br_pos(&br) == cmd_pos_end); + br_seek_to(&br, cmd_pos_end); + } } break; default: break; } - ASSERT(br_pos(&br) == cmd_pos_end); - br_seek_to(&br, cmd_pos_end); - - if (l.last) { - l.last->next = cmd; + if (cmds_out->last) { + cmds_out->last->next = cmd; } else { - l.first = cmd; + cmds_out->first = cmd; } - l.last = cmd; + cmds_out->last = cmd; } - return l; } /* ========================== * - * Game cmd + * Game event * ========================== */ -INTERNAL void push_input_string(struct string input) +struct string game_string_from_events(struct arena *arena, struct game_event_list events) { - struct sys_lock lock = sys_mutex_lock_e(&G.game_input_mutex); - u8 *dst = arena_push_array(&G.game_input_arena, u8, input.len); - MEMCPY(dst, input.text, input.len); - sys_mutex_unlock(&lock); + struct byte_writer bw = bw_from_arena(arena); + for (struct game_cmd *cmd = cmds->first; cmd; cmd = cmd->next) { + struct byte_writer bw_size = bw_branch(&bw, sizeof(u64)); + u64 start = bw_pos(&bw); + + switch (cmd->kind) { + case GAME_EVENT_KIND_SNAPSHOT_FULL: + { + } break; + + default: break; + } + + u64 size = bw_pos(&bw) - start; + bw_write_u64(&bw_size, size); + } + return bw_get_written(&bw); } -INTERNAL struct string pop_input_string(struct arena *arena) +void game_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct game_event *events_out) { - struct string s = ZI; - struct sys_lock lock = sys_mutex_lock_e(&G.game_input_mutex); - s.len = G.game_input_arena.pos; - s.text = arena_push_array(arena, u8, s.len); - MEMCPY(s.text, G.game_input_arena.base, s.len); - arena_reset(&G.game_input_arena); - sys_mutex_unlock(&lock); - return s; -} + for (u64 i = 0; i < host_events.count; ++i) { + struct game_event *event = arena_push_zero(arena, struct game_event); + struct host_channel_event host_event = host_events.events[i]; + enum host_event_kind host_event_kind = host_event.kind; + event->channel_id = host_event.channel_id; + switch (host_event_kind) { + case HOST_EVENT_KIND_CHANNEL_OPENED: + { + event->kind = GAME_EVENT_KIND_CONNECT; + } break; -/* ========================== * - * Interface - * ========================== */ + case HOST_EVENT_KIND_CHANNEL_CLOSED: + { + event->kind = GAME_EVENT_KIND_DISCONNECT; + event->disconnect_reason = LIT("Connection lost"); + } break; -void game_get_latest_tick(struct world *dest) -{ - __prof; - struct sys_lock lock = sys_mutex_lock_s(&G.prev_tick_mutex); - world_copy_replace(dest, &G.prev_tick); - sys_mutex_unlock(&lock); -} + case HOST_EVENT_KIND_MSG: + { + struct byte_reader br = br_from_buffer(event.msg); + while (br_bytes_left(&br) > 0) { + u64 event_size = br_read_u64(&br); + u64 event_pos_end = br_pos(&br) + event_size; -u64 game_get_latest_tick_id(void) -{ - return atomic_u64_eval(&G.prev_tick_id); -} + event->kind = br_read_i8(&br); + switch (event->kind) { + case GAME_EVENT_KIND_SNAPSHOT_FULL: + { + } break; -u64 game_get_latest_tick_continuity_gen(void) -{ - return atomic_u64_eval(&G.prev_tick_continuity_gen); -} + default: break; + } -void game_push_cmds_string(struct string str) -{ - push_input_string(str); + ASSERT(br_pos(&br) == event_pos_end); + br_seek_to(&br, event_pos_end); + } + } break; + + default: break; + } + + if (events_out->last) { + events_out->last->next = cmd; + } else { + events_out->first = cmd; + } + events_out->last = cmd; + } } /* ========================== * * Game thread * ========================== */ + INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg) { struct temp_arena scratch = scratch_begin_no_conflict(); @@ -1509,38 +1543,8 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg) i64 target_dt_ns = NS_FROM_SECONDS(GAME_FPS > (0) ? (1.0 / GAME_FPS) : 0); while (!atomic_i32_eval(&G.game_thread_shutdown)) { __profscope(game_update_w_sleep); - struct temp_arena temp = arena_temp_begin(scratch.arena); sleep_frame(last_frame_ns, target_dt_ns); last_frame_ns = sys_time_ns(); - { - struct string input_str = pop_input_string(temp.arena); - struct game_cmd_list cmds = game_cmds_from_string(temp.arena, input_str); - if (!G.paused) { - game_update(cmds); - } - /* Check for pause / next frame cmds */ - for (struct game_cmd *cmd = cmds.first; cmd; cmd = cmd->next) { - switch (cmd->kind) { - case GAME_CMD_KIND_PAUSE: - { - G.paused = !G.paused; - } break; - - case GAME_CMD_KIND_STEP: - { - if (G.paused) { - G.paused = false; - game_update(cmds); - G.paused = true; - } - } break; - - default: break; - } - } - } - arena_temp_end(temp); + game_update(); } - - scratch_end(scratch); } diff --git a/src/game.h b/src/game.h index 5999c6e8..4eaf36b4 100644 --- a/src/game.h +++ b/src/game.h @@ -6,7 +6,7 @@ struct mixer_startup_receipt; struct sprite_startup_receipt; struct sound_startup_receipt; struct phys_startup_receipt; -struct sock_startup_receipt; +struct host_startup_receipt; /* Absolute layers */ #define GAME_LAYER_FLOOR_DECALS -300 @@ -23,11 +23,11 @@ struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr, struct sprite_startup_receipt *sheet_sr, struct sound_startup_receipt *sound_sr, struct phys_startup_receipt *phys_sr, - struct sock_startup_receipt *sock_sr); + struct host_startup_receipt *host_sr); /* ========================== * - * Game CMD + * Game cmd * ========================== */ enum game_cmd_state { @@ -42,6 +42,9 @@ enum game_cmd_kind { GAME_CMD_KIND_PLAYER_MOVE, GAME_CMD_KIND_PLAYER_FIRE, + GAME_CMD_KIND_CLIENT_CONNECT, + GAME_CMD_KIND_CLIENT_DISCONNECT, + /* Testing */ GAME_CMD_KIND_CLEAR_ALL, GAME_CMD_KIND_SPAWN_TEST, @@ -56,6 +59,7 @@ enum game_cmd_kind { struct game_cmd { enum game_cmd_kind kind; enum game_cmd_state state; + struct host_channel_id channel_id; /* GAME_CMD_KIND_PLAYER_MOVE */ struct v2 move_dir; @@ -64,6 +68,9 @@ struct game_cmd { /* GAME_CMD_KIND_CURSOR_MOVE */ struct v2 cursor_pos; + /* GAME_CMD_KIND_PLAYER_DISCONNECT */ + struct string disconnect_reason; + #if RTC u32 collider_gjk_steps; #endif @@ -76,16 +83,40 @@ struct game_cmd_list { struct game_cmd *last; }; +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); + /* ========================== * - * Interface + * Game event * ========================== */ -struct string game_string_from_cmds(struct arena *arena, struct game_cmd_list *cmds); -struct game_cmd_list game_cmds_from_string(struct arena *arena, struct string str); +enum game_event_kind { + GAME_EVENT_KIND_NONE, -void game_get_latest_tick(struct world *dest); -u64 game_get_latest_tick_id(void); -u64 game_get_latest_tick_continuity_gen(void); -void game_push_cmds_string(struct string str); + GAME_EVENT_KIND_CONNECT, + GAME_EVENT_KIND_DISCONNECT, + GAME_EVENT_KIND_SNAPSHOT_FULL, + + //GAME_EVENT_KIND_ENTITY_UPDATE, + //GAME_EVENT_KIND_ENTITY_CREATE, + //GAME_EVENT_KIND_ENTITY_DESTROY +}; + +struct game_event { + enum game_event_kind kind; + + struct entity_handle entity; + struct string update_data; + + struct game_event *next; +}; + +struct game_event_list { + struct game_event *first; + struct game_event *last; +}; + +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); #endif diff --git a/src/host.c b/src/host.c new file mode 100644 index 00000000..a4c684ed --- /dev/null +++ b/src/host.c @@ -0,0 +1,404 @@ +#include "host.h" +#include "arena.h" +#include "scratch.h" +#include "byteio.h" + +//#define HOST_NETWORK_ADDRESS_STRING(str) +//#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port) +//#define HOST_NETWORK_ADDRESS_NONE + +#define PACKET_MAGIC 0xd9e3b8b6 +#define PACKET_CHUNK_MAX_LEN 256 + +GLOBAL struct { + i32 _; +} G = ZI, DEBUG_ALIAS(G, G_host); + + +struct host_address_desc { + i32 _; +}; + +struct host_channel_id { + u32 idx; + u32 gen; +}; + +struct host_channel { + b32 valid; + struct host_channel_id id; +}; + +enum host_cmd_kind { + HOST_CMD_KIND_NONE, + + HOST_CMD_KIND_CONNECT, + 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_cmd *next; +}; + +struct host_event { + enum host_event_kind kind; +}; + +struct host { + 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; +}; + + + + + + + + + + +/* ========================== * + * Startup + * ========================== */ + +struct host_startup_receipt host_startup(struct sock_startup_receipt *sock_sr) +{ + (UNUSED)sock_sr; + + return (struct host_startup_receipt) { 0 }; +} + +/* ========================== * + * Allocation + * ========================== */ + +struct host host_alloc(struct host_address_desc bind_address) +{ + struct host host = ZI; + host.cmd_arena = arena_alloc(GIGABYTE(64)); + host.channels_arena = arena_alloc(GIGABYTE(64)); + return host; +} + +void host_release(struct host *host) +{ + arena_release(&host->channels_arena); + arena_release(&host->cmd_arena); +} + +/* ========================== * + * Queue + * ========================== */ + +INTERNAL struct host_cmd *host_cmd_alloc_and_append(struct host *host) +{ + struct host_cmd *cmd = NULL; + 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) { + host->last_cmd->next = cmd; + } else { + host->first_cmd = cmd; + } + host->last_cmd = cmd; + return cmd; +} + +void host_queue_connect_to_address(struct host *host, struct host_address_desc host_address_desc) +{ + struct sock_address_desc sock_address_desc = sock_address_desc_from_host_address_desc(host_address_desc); + struct sock_address address = sock_address_from_desc(sock_address_desc); + struct channel *channel = channel_from_address(host, address); + if (!channel->valid) { + channel = channel_alloc(host, address); + } +} + +void host_queue_disconnect(struct host *host, struct host_channel_id channel_id) +{ + struct host_cmd *cmd = host_cmd_alloc_and_append(host); + cmd->kind = HOST_CMD_KIND_DISCONNECT; +} + +void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg) +{ + struct host_cmd *cmd = host_cmd_alloc_and_append(host); + cmd->kind = HOST_CMD_KIND_WRITE; +} + +/* ========================== * + * Update + * ========================== */ + +void host_update(struct host *host) +{ + struct temp_arena scratch = scratch_begin_no_conflict(); + + struct socket *sock = &host->sock; + struct string read_buff = ZI; + read_buff.len = KILOBYTE(64); + read_buff.text = arena_push_array(scratch.arena, u8, read_buff.len); + + /* Read socket */ + while (true) { + struct sock_read_result res = sock_read(sock, read_buff); + if (res.valid) { + struct string data = res.data; + struct sock_address address = res.address; + struct byte_reader br = br_from_buffer(data); + u32 magic = br_read_u32(&br); + if (magic == PACKET_MAGIC) { + /* TODO: Combine kind byte with flags byte */ + struct host_channel *channel = host_channel_from_sock_address(host, address); + enum packet_kind packet_kind = br_read_i8(&br); + u8 packet_flags = br_read_u8(&br); + u64 packet_id = br_read_var_uint(&br); + u64 packet_reliable_id = 0; + if (packet_flags & PACKET_FLAG_RELIABLE) { + packet_reliable_id = br_read_var_uint(&br); + } + switch (packet_kind) { + case PACKET_KIND_TRY_CONNECT: + { + /* A foreign host is trying to connect to us */ + if (!channel->valid) { + channel = channel_alloc(host, address); + } + } break; + + case PACKET_KIND_CONNECT_SUCCESS: + { + /* We successfully connected to a foreign host and they are ready to receive messages */ + if (channel->valid && !channel->connected) { + struct host_queued_event *queued_event = queued_event_alloc_and_append(host); + queued_event->kind = HOST_EVENT_KIND_CHANNEL_OPENED; + queued_event->channel_id = channel->id; + channel->connected = true; + } + } break; + + case PACKET_KIND_DISCONNECT: + { + /* A foreign host disconnected from us */ + if (channel->valid) { + struct host_queued_event *queued_event = queued_event_alloc_and_append(host); + queued_event->kind = HOST_EVENT_KIND_CHANNEL_CLOSED; + queued_event->channel_id = channel->id; + channel_release(channel); + } + + } break; + + case PACKET_KIND_MSG_CHUNK: + { + /* Packet is chunk out of belonging to message */ + u64 msg_id = br_read_var_uint(&br); + u64 chunk_id = 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); + if (!msg_buff) { + msg_buff = channel_msg_buffer_alloc(channel, chunk_count); + } + + if (chunk_count == msg_buff->chunk_count && chunk_id < chunk_count) { + struct msg_chunk *chunk = &msg_buff->chunks[chunk_id]; + if (!chunk->filled) { + u8 *data = br_seek(&br, data_len); + if (data) { + MEMCPY(chunk->data, data, data_len); + chunk->data_len = data_len; + chunk->filled = true; + ++msg_buff->filled_chunks; + msg_buff->last_touch = now; + if (msg_buff->filled_chunks == chunk_count) { + /* All chunks filled, message has finished assembling */ + /* TODO: Message ordering */ + struct host_queued_event *queued_event = queued_event_alloc_and_append(host); + struct string data = ZI; + data.text = arena_push_array(&host->queued_event_arena, u8, chunk_count * PACKET_CHUNK_MAX_LEN); + for (u64 i = 0; i < chunk_count; ++i) { + struct msg_chunk *filled_chunk = &msg_buff->chunks[i]; + u64 len = filled_chunk->data_len; + MEMCPY(data.text + data.len, filled_chunk->data, len); + data.len += len; + } + queued_event->kind = HOST_EVENT_KIND_MSG; + queued_event->msg_data = data; + queued_event->channel_id = channel->id; + } + } + } + } + } break; + + default: break; + } + } + } else { + break; + } + } + + /* Release expired msg buffers */ + + /* Try connecting to unconnected hosts */ + for (u64 i = 0; i < host->channels; ++i) { + struct channel *channel = &host->channels[i]; + if (channel->valid && !channel->connected) { + struct host_cmd *cmd = host_cmd_alloc_and_append(host); + cmd->kind = HOST_CMD_KIND_TRY_CONNECT; + cmd->channel_id = channel->id; + } + } + + /* Release un + + /* Process cmds */ + for (struct host_cmd *cmd = host->first_cmd; cmd; cmd = cmd->next) { + struct host_channel *channel = host_channel_from_id(host, cmd->channel_id); + if (channel->valid) { + enum host_cmd_kind kind = cmd->kind; + struct sock_address address = channel->sock_address; + + switch (kind) { + case HOST_CMD_KIND_TRY_CONNECT: + { + struct packet *packet = host_packet_alloc(host, address, false); + struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data)); + bw_write_i8(&bw, PACKET_KIND_TRY_CONNECT); + bw_write_u8(&bw, packet_flags); + bw_write_var_uint(&bw, packet->id); + bw_write_var_uint(&bw, packet->reliable_id); + packet->data_len = bw_pos(&bw); + } break; + + case HOST_CMD_KIND_CONNECT_SUCCESS: + { + struct packet *packet = host_packet_alloc(host, address, false); + struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data)); + bw_write_i8(&bw, PACKET_KIND_CONNECT_SUCCESS); + bw_write_u8(&bw, packet_flags); + bw_write_var_uint(&bw, packet->id); + bw_write_var_uint(&bw, packet->reliable_id); + packet->data_len = bw_pos(&bw); + } break; + + case HOST_CMD_KIND_DISCONNECT: + { + struct packet *packet = host_packet_alloc(host, address, false); + struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data)); + bw_write_i8(&bw, PACKET_KIND_DISCONNECT); + bw_write_u8(&bw, packet_flags); + bw_write_var_uint(&bw, packet->id); + packet->data_len = bw_pos(&bw); + } break; + + case HOST_CMD_KIND_WRITE: + { + b32 is_reliable = packet_flags & HOST_WRITE_FLAG_RELIABLE; + struct string msg = cmd->msg; + u64 chunk_count = 0; + if (msg.len > 0) { + chunk_count = ((msg.len - 1) / PACKET_CHUNK_MAX_LEN) + 1 + } + + for (u64 i = 0; i < chunk_count; ++i) { + u64 data_len = PACKET_CHUNK_MAX_LEN; + b32 is_last_chunk = i + 1 == chunk_count; + if (is_last_chunk) { + data_len = msg.len % PACKET_CHUNK_MAX_LEN; + } + u8 *data = msg.text + (i * PACKET_CHUNK_MAX_LEN); + struct packet *packet = host_packet_alloc(host, address, is_reliable); + struct byte_writer bw = bw_from_buffer(STRING_FROM_ARRAY(packet->data)); + bw_write_i8(&bw, PACKET_KIND_MSG_CHUNK); + bw_write_u8(&bw, packet_flags); + bw_write_var_uint(&bw, packet->id); + if (is_reliable) { + bw_write_var_uint(&bw, packet->reliable_id); + } + if (is_last_chunk) { + bw_write_u8(&bw, data_len); + } + bw_write_buffer(&bw, STRING(data_len, data)); + packet->data_len = bw_pos(&bw); + } + } break; + + default: break; + } + } + } + + /* Send packets */ + for (struct packet *packet = host->first_packet; packet; packet = packet->next) { + struct sock_address address = packet->address; + sock_write(sock, address, STRING(packet->data_len, packet->data)); + } + + /* Reset packets */ + host->first_packet = NULL; + host->last_packet = NULL; + host->first_free_packet = NULL; + arena_reset(&host->packet_arena); + + /* Reset cmds */ + host->first_cmd = NULL; + host->last_cmd = NULL; + host->first_free_cmd = NULL; + arena_reset(&host->cmd_arena); + + scratch_end(scratch); +} + +/* ========================== * + * Events + * ========================== */ + +struct host_event_array host_pop_events(struct arena *arena, struct host *host) +{ + struct host_event_array res = ZI; + res.count = host->num_queued_events; + res.events = arena_push_array(arena, struct host_event, res.count); + + u64 i = 0; + for (struct host_queued_event *qe = host->first_queued_event; qe; qe = qe->next) { + struct host_event *dest = &res.events[i]; + *dest = (struct host_event) { + .kind = qe->kind, + .msg = qe->msg + }; + ++i; + } + + /* Reset queued events */ + hust->num_queued_events = 0; + host->first_queued_event = NULL; + host->last_queued_event = NULL; + host->first_free_queued_event = NULL; + arena_reset(&host->queued_event_arena); +} diff --git a/src/host.h b/src/host.h new file mode 100644 index 00000000..68d4667c --- /dev/null +++ b/src/host.h @@ -0,0 +1,4 @@ +#ifndef HOST_H +#define HOST_H + +#endif diff --git a/src/phys.c b/src/phys.c index 806bd069..0c16849c 100644 --- a/src/phys.c +++ b/src/phys.c @@ -63,7 +63,7 @@ struct phys_collision_data_array phys_create_and_update_contacts(struct arena *a struct aabb aabb = collider_aabb_from_collider(&check0_collider, check0_xf); struct space_iter iter = space_iter_begin_aabb(space, aabb); - struct space_client *client; + struct space_entry *client; while ((client = space_iter_next(&iter))) { struct entity *check1 = entity_from_handle(store, client->ent); if (check1 == check0) continue; @@ -1049,9 +1049,9 @@ void phys_integrate_velocities(struct phys_ctx *ctx, f32 dt) struct xform xf = get_derived_xform(ent, dt); entity_set_xform(ent, xf); - struct space_client *space_client = space_client_from_handle(ctx->space, ent->space_handle); - if (space_client->valid) { - space_client_update_aabb(space_client, collider_aabb_from_collider(&ent->local_collider, xf)); + struct space_entry *space_entry = space_entry_from_handle(ctx->space, ent->space_handle); + if (space_entry->valid) { + space_entry_update_aabb(space_entry, collider_aabb_from_collider(&ent->local_collider, xf)); } } } @@ -1084,7 +1084,7 @@ f32 phys_determine_earliest_toi_for_bullets(struct phys_ctx *ctx, f32 step_dt, f struct aabb combined_aabb = collider_aabb_from_combined_aabb(aabb_t0, aabb_t1); struct space_iter iter = space_iter_begin_aabb(space, combined_aabb); - struct space_client *client; + struct space_entry *client; while ((client = space_iter_next(&iter))) { struct entity *e1 = entity_from_handle(store, client->ent); if (e1 == e0) continue; @@ -1120,12 +1120,12 @@ void phys_update_aabbs(struct phys_ctx *ctx) if (ent->local_collider.count <= 0) continue; struct xform xf = entity_get_xform(ent); - struct space_client *space_client = space_client_from_handle(ctx->space, ent->space_handle); - if (!space_client->valid) { - space_client = space_client_alloc(ctx->space, ent->handle); - ent->space_handle = space_client->handle; + struct space_entry *space_entry = space_entry_from_handle(ctx->space, ent->space_handle); + if (!space_entry->valid) { + space_entry = space_entry_alloc(ctx->space, ent->handle); + ent->space_handle = space_entry->handle; } - space_client_update_aabb(space_client, collider_aabb_from_collider(&ent->local_collider, xf)); + space_entry_update_aabb(space_entry, collider_aabb_from_collider(&ent->local_collider, xf)); } } diff --git a/src/scratch.h b/src/scratch.h index cedcf4d5..5776a5c7 100644 --- a/src/scratch.h +++ b/src/scratch.h @@ -43,11 +43,11 @@ INLINE void scratch_dbg_push(struct scratch_ctx *ctx, struct temp_arena *temp) } /* Any arena parameters in the calling function's context should be passed into this - * function as a potential `conflict`. This is to prevent conflicts when the + * function as a potential `conflict`. This is to prevent friction when the * context's arena is itself a scratch arena (since parameterized arenas are * often used to allocate persistent results for the caller). * - * Call `scratch_begin_no_conflict` instead if there is no arena in the current + * Use `scratch_begin_no_conflict` instead if there is no arena in the current * context that could potentially be a scratch arena. */ #define scratch_begin(c) _scratch_begin(c) diff --git a/src/space.c b/src/space.c index 6f2016fe..1cf4636e 100644 --- a/src/space.c +++ b/src/space.c @@ -3,11 +3,11 @@ #include "arena.h" #include "collider.h" -/* Offset in bytes from start of space struct to start of client array (assume adjacently allocated) */ -#define SPACE_CLIENTS_OFFSET (sizeof(struct space) + (sizeof(struct space) % alignof(struct space_client))) +/* Offset in bytes from start of space struct to start of entry array (assume adjacently allocated) */ +#define SPACE_ENTRIES_OFFSET (sizeof(struct space) + (sizeof(struct space) % alignof(struct space_entry))) /* Accessed via entity_nil() */ -READONLY struct space_client _g_space_client_nil = { .valid = false }; +READONLY struct space_entry _g_space_entry_nil = { .valid = false }; READONLY struct space_cell _g_space_cell_nil = { .valid = false }; READONLY struct space _g_space_nil = { .valid = false }; @@ -24,8 +24,8 @@ struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt) struct space *space = arena_push_zero(&arena, struct space); space->valid = true; - space->client_arena = arena; - space->clients = arena_dry_push(&space->client_arena, struct space_client); + space->entry_arena = arena; + space->entries = arena_dry_push(&space->entry_arena, struct space_entry); space->cell_arena = arena_alloc(GIGABYTE(64)); space->cell_size = cell_size; @@ -39,15 +39,15 @@ struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt) void space_release(struct space *space) { arena_release(&space->cell_arena); - arena_release(&space->client_arena); + arena_release(&space->entry_arena); } -struct space *space_from_client(struct space_client *client) +struct space *space_from_entry(struct space_entry *entry) { - if (client->valid) { - u64 first_client_addr = (u64)(client - client->handle.idx); - struct space *space = (struct space *)(first_client_addr - SPACE_CLIENTS_OFFSET); - ASSERT(space->clients == (struct space_client *)first_client_addr); + if (entry->valid) { + u64 first_entry_addr = (u64)(entry - entry->handle.idx); + struct space *space = (struct space *)(first_entry_addr - SPACE_ENTRIES_OFFSET); + ASSERT(space->entries == (struct space_entry *)first_entry_addr); return space; } else { return space_nil(); @@ -103,9 +103,9 @@ struct space_cell *space_get_cell(struct space *space, struct v2i32 cell_pos) return res; } -INTERNAL void space_cell_node_alloc(struct v2i32 cell_pos, struct space_client *client) +INTERNAL void space_cell_node_alloc(struct v2i32 cell_pos, struct space_entry *entry) { - struct space *space = space_from_client(client); + struct space *space = space_from_entry(entry); i32 bucket_index = cell_coords_to_bucket_index(space, cell_pos); struct space_cell_bucket *bucket = &space->buckets[bucket_index]; @@ -161,37 +161,37 @@ INTERNAL void space_cell_node_alloc(struct v2i32 cell_pos, struct space_client * } cell->last_node = node; - /* Insert into client list */ - node->client = client; - if (client->last_node) { - client->last_node->next_in_client = node; - node->prev_in_client = client->last_node; + /* Insert into entry list */ + node->entry = entry; + if (entry->last_node) { + entry->last_node->next_in_entry = node; + node->prev_in_entry = entry->last_node; } else { - client->first_node = node; + entry->first_node = node; } - client->last_node = node; + entry->last_node = node; } INTERNAL void space_cell_node_release(struct space_cell_node *n) { struct space_cell *cell = n->cell; - struct space_client *client = n->client; - struct space *space = space_from_client(client); + struct space_entry *entry = n->entry; + struct space *space = space_from_entry(entry); struct space_cell_bucket *bucket = cell->bucket; - /* Remove from client list */ + /* Remove from entry list */ { - struct space_cell_node *prev = n->prev_in_client; - struct space_cell_node *next = n->next_in_client; + struct space_cell_node *prev = n->prev_in_entry; + struct space_cell_node *next = n->next_in_entry; if (prev) { - prev->next_in_client = next; + prev->next_in_entry = next; } else { - client->first_node = next; + entry->first_node = next; } if (next) { - next->prev_in_client = prev; + next->prev_in_entry = prev; } else { - client->last_node = prev; + entry->last_node = prev; } } @@ -239,71 +239,71 @@ INTERNAL void space_cell_node_release(struct space_cell_node *n) } /* ========================== * - * Client + * Entry * ========================== */ -struct space_client *space_client_from_handle(struct space *space, struct space_client_handle handle) +struct space_entry *space_entry_from_handle(struct space *space, struct space_entry_handle handle) { - struct space_client *client = space_client_nil(); + struct space_entry *entry = space_entry_nil(); - if (handle.gen > 0 && handle.idx < space->num_clients_reserved) { - struct space_client *tmp = &space->clients[handle.idx]; + if (handle.gen > 0 && handle.idx < space->num_entries_reserved) { + struct space_entry *tmp = &space->entries[handle.idx]; if (tmp->handle.gen == handle.gen) { - client = tmp; + entry = tmp; } } - return client; + return entry; } -struct space_client *space_client_alloc(struct space *space, struct entity_handle entity) +struct space_entry *space_entry_alloc(struct space *space, struct entity_handle entity) { - struct space_client *client = NULL; - struct space_client_handle handle = ZI; - if (space->first_free_client) { - client = space->first_free_client; - space->first_free_client = client->next_free; - handle = client->handle; + struct space_entry *entry = NULL; + struct space_entry_handle handle = ZI; + if (space->first_free_entry) { + entry = space->first_free_entry; + space->first_free_entry = entry->next_free; + handle = entry->handle; } else { - client = arena_push(&space->client_arena, struct space_client); - handle.idx = space->num_clients_reserved; + entry = arena_push(&space->entry_arena, struct space_entry); + handle.idx = space->num_entries_reserved; handle.gen = 1; - ++space->num_clients_reserved; + ++space->num_entries_reserved; } - MEMZERO_STRUCT(client); - client->valid = true; - client->handle = handle; - client->ent = entity; - return client; + MEMZERO_STRUCT(entry); + entry->valid = true; + entry->handle = handle; + entry->ent = entity; + return entry; } -void space_client_release(struct space_client *client) +void space_entry_release(struct space_entry *entry) { /* Release nodes */ - struct space_cell_node *n = client->first_node; + struct space_cell_node *n = entry->first_node; while (n) { - struct space_cell_node *next = n->next_in_client; - /* TODO: More efficient batch release that doesn't care about maintaining client list */ + struct space_cell_node *next = n->next_in_entry; + /* TODO: More efficient batch release that doesn't care about maintaining entry list */ space_cell_node_release(n); n = next; } - struct space *space = space_from_client(client); - client->next_free = space->first_free_client; - client->valid = false; - ++client->handle.gen; - space->first_free_client = client; + struct space *space = space_from_entry(entry); + entry->next_free = space->first_free_entry; + entry->valid = false; + ++entry->handle.gen; + space->first_free_entry = entry; } -void space_client_update_aabb(struct space_client *client, struct aabb new_aabb) +void space_entry_update_aabb(struct space_entry *entry, struct aabb new_aabb) { - struct space *space = space_from_client(client); + struct space *space = space_from_entry(entry); f32 cell_size = space->cell_size; struct v2i32 old_cell_p0 = V2I32(0, 0); struct v2i32 old_cell_p1 = V2I32(0, 0); - if (client->first_node) { - struct aabb old_aabb = client->aabb; + if (entry->first_node) { + struct aabb old_aabb = entry->aabb; old_cell_p0 = world_to_cell_coords(cell_size, old_aabb.p0); old_cell_p1 = world_to_cell_coords(cell_size, old_aabb.p1); } @@ -312,17 +312,17 @@ void space_client_update_aabb(struct space_client *client, struct aabb new_aabb) struct v2i32 new_cell_p1 = world_to_cell_coords(cell_size, new_aabb.p1); /* Release outdated nodes */ - struct space_cell_node *n = client->first_node; + struct space_cell_node *n = entry->first_node; while (n) { struct space_cell *cell = n->cell; struct v2i32 cell_pos = cell->pos; if (cell_pos.x < new_cell_p0.x || cell_pos.x > new_cell_p1.x || cell_pos.y < new_cell_p0.y || cell_pos.y > new_cell_p1.y) { /* Cell is outside of new AABB */ - struct space_cell_node *next = n->next_in_client; + struct space_cell_node *next = n->next_in_entry; space_cell_node_release(n); n = next; } else { - n = n->next_in_client; + n = n->next_in_entry; } } @@ -331,12 +331,12 @@ void space_client_update_aabb(struct space_client *client, struct aabb new_aabb) for (i32 x = new_cell_p0.x; x <= new_cell_p1.x; ++x) { if (x != 0 && y != 0 && (x < old_cell_p0.x || x > old_cell_p1.x || y < old_cell_p0.y || y > old_cell_p1.y)) { /* Cell is outside of old AABB */ - space_cell_node_alloc(V2I32(x, y), client); + space_cell_node_alloc(V2I32(x, y), entry); } } } - client->aabb = new_aabb; + entry->aabb = new_aabb; } /* ========================== * @@ -366,7 +366,7 @@ struct space_iter space_iter_begin_aabb(struct space *space, struct aabb aabb) return iter; } -struct space_client *space_iter_next(struct space_iter *iter) +struct space_entry *space_iter_next(struct space_iter *iter) { struct space *space = iter->space; struct aabb iter_aabb = iter->aabb; @@ -387,9 +387,9 @@ struct space_client *space_iter_next(struct space_iter *iter) while (true) { if (next_node) { - struct space_client *client = next_node->client; - struct aabb client_aabb = client->aabb; - if (collider_test_aabb(client_aabb, iter_aabb)) { + struct space_entry *entry = next_node->entry; + struct aabb entry_aabb = entry->aabb; + if (collider_test_aabb(entry_aabb, iter_aabb)) { break; } else { next_node = next_node->next_in_cell; @@ -416,5 +416,5 @@ struct space_client *space_iter_next(struct space_iter *iter) iter->prev = next_node; iter->cell_cur = cell_cur; - return next_node ? next_node->client : NULL; + return next_node ? next_node->entry : NULL; } diff --git a/src/space.h b/src/space.h index b0496d1d..13144dfa 100644 --- a/src/space.h +++ b/src/space.h @@ -3,9 +3,9 @@ struct space_cell_bucket; -struct space_client { +struct space_entry { b32 valid; - struct space_client_handle handle; + struct space_entry_handle handle; struct space_cell_node *first_node; struct space_cell_node *last_node; @@ -13,22 +13,22 @@ struct space_client { struct aabb aabb; struct entity_handle ent; - struct space_client *next_free; + struct space_entry *next_free; }; -/* Links a cell to a client. - * Acts as both a list of clients contained by cell & a list of cells containing client. */ +/* Links a cell to a entry. + * Acts as both a list of entries contained by cell & a list of cells containing entry. */ struct space_cell_node { - struct space_client *client; + struct space_entry *entry; struct space_cell *cell; - /* For list of all clients contained by cell */ + /* For list of all entries contained by cell */ struct space_cell_node *prev_in_cell; struct space_cell_node *next_in_cell; - /* For list of all cells containing client */ - struct space_cell_node *prev_in_client; - struct space_cell_node *next_in_client; + /* For list of all cells containing entry */ + struct space_cell_node *prev_in_entry; + struct space_cell_node *next_in_entry; struct space_cell_node *next_free; }; @@ -63,10 +63,10 @@ struct space { struct space_cell *first_free_cell; struct space_cell_node *first_free_cell_node; - struct arena client_arena; - u64 num_clients_reserved; - struct space_client *clients; - struct space_client *first_free_client; + struct arena entry_arena; + u64 num_entries_reserved; + struct space_entry *entries; + struct space_entry *first_free_entry; }; struct space_iter { @@ -82,10 +82,10 @@ struct space_iter { * Nil * ========================== */ -INLINE struct space_client *space_client_nil(void) +INLINE struct space_entry *space_entry_nil(void) { - extern READONLY struct space_client _g_space_client_nil; - return &_g_space_client_nil; + extern READONLY struct space_entry _g_space_entry_nil; + return &_g_space_entry_nil; } INLINE struct space_cell *space_cell_nil(void) @@ -106,7 +106,7 @@ INLINE struct space *space_nil(void) struct space *space_alloc(f32 cell_size, u32 num_buckets_sqrt); void space_release(struct space *space); -struct space *space_from_client(struct space_client *client); +struct space *space_from_entry(struct space_entry *entry); /* ========================== * * Cell @@ -115,20 +115,20 @@ struct space *space_from_client(struct space_client *client); struct space_cell *space_get_cell(struct space *space, struct v2i32 cell_pos); /* ========================== * - * Client + * Entry * ========================== */ -struct space_client *space_client_from_handle(struct space *space, struct space_client_handle handle); -struct space_client *space_client_alloc(struct space *space, struct entity_handle entity); -void space_client_release(struct space_client *client); -void space_client_update_aabb(struct space_client *client, struct aabb new_aabb); +struct space_entry *space_entry_from_handle(struct space *space, struct space_entry_handle handle); +struct space_entry *space_entry_alloc(struct space *space, struct entity_handle entity); +void space_entry_release(struct space_entry *entry); +void space_entry_update_aabb(struct space_entry *entry, struct aabb new_aabb); /* ========================== * * Iter * ========================== */ struct space_iter space_iter_begin_aabb(struct space *space, struct aabb aabb); -struct space_client *space_iter_next(struct space_iter *iter); +struct space_entry *space_iter_next(struct space_iter *iter); #define space_iter_end(i) #endif diff --git a/src/user.c b/src/user.c index 84a7b904..a7880acf 100644 --- a/src/user.c +++ b/src/user.c @@ -155,6 +155,9 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, G.sys_events_arena = arena_alloc(GIGABYTE(64)); world_alloc(&G.world); + struct net_address_desc bind_addr = NET_ADDRESS_DESC_ALL_LOCAL_INTERFACES(NET_PORT_DYNAMIC); + G.host = host_alloc(bind_addr); + G.world_to_ui_xf = XFORM_IDENT; G.world_cmd_buffer = renderer_cmd_buffer_alloc(); G.ui_cmd_buffer = renderer_cmd_buffer_alloc(); @@ -359,30 +362,6 @@ INTERNAL struct interp_ticks pull_ticks(i64 blend_time_ns) }; } -/* ========================== * - * User -> game communication - * ========================== */ - -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; -} - -INTERNAL void pubilsh_game_cmds(struct game_cmd_list *list) -{ - struct temp_arena scratch = scratch_begin_no_conflict(); - struct string s = game_string_from_cmds(scratch.arena, list); - game_push_cmds_string(s); - scratch_end(scratch); -} - /* ========================== * * Debug draw * ========================== */ @@ -467,44 +446,41 @@ INTERNAL SORT_COMPARE_FUNC_DEF(entity_draw_order_cmp, arg_a, arg_b, udata) * Update * ========================== */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INTERNAL void user_update(void) { struct temp_arena scratch = scratch_begin_no_conflict(); - - - - - - - - - - - - - - - { - static struct sock sock = ZI; - if (!sock.handle) { - struct sock_address bind_addr = ZI; - bind_addr.ip = SOCK_IP_ANY_INTERFACE; - bind_addr.port = SOCK_PORT_DYNAMIC; /* Dynamic port */ - sock = sock_alloc(bind_addr, SOCK_FLAG_NON_BLOCKING_RECV); - } - } - - - - - - - - - - - /* ========================== * * Begin frame * ========================== */ @@ -518,6 +494,7 @@ INTERNAL void user_update(void) struct sprite_scope *sprite_frame_scope = sprite_scope_begin(); struct game_cmd_list cmd_list = ZI; +#if 0 /* ========================== * * Interpolate between game ticks * ========================== */ @@ -603,6 +580,127 @@ INTERNAL void user_update(void) tick_is_first_frame = G.world.tick_id == 0; #endif } +#endif + + /* ========================== * + * Process host events into game events + * ========================== */ + + struct game_event_list game_events = ZI; + { + struct host_events_array = host_pop_events(scratch.arena, G.host); + game_events_from_host_events(scratch.arena, host_events, &game_events); + } + + /* ========================== * + * Process game events + * ========================== */ + + + + + { + static f64 last_try_connect = 0; + f64 now = SECONDS_FROM_NS(sys_time_ns()); + 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")); + host_connect_to_address(G.host, connect_addr); + last_try_connect = now; + } + + for (u64 i = 0; i < game_events.count; ++i) { + struct game_event event = game_events.events[i]; + enum game_event_kind kind = event.kind; + + switch (kind) { + case GAME_EVENT_KIND_CONNECT: + { + last_try_connect = F64_INFINITY; + } break; + + case GAME_EVENT_KIND_DISCONNECT: + { + last_try_connect = 0; + } break; + + case GAME_EVENT_KIND_SNAPSHOT: + { + struct string data = event.data; + game_tick_from_string(&G.world, data); + } break; + + default: break; + } + } + } + + + /* ========================== * + * Process sys events into user bind state + * ========================== */ + + { + struct sys_event_array events = pop_sys_events(scratch.arena); + + /* Reset bind pressed / released states */ + for (u32 i = 0; i < ARRAY_COUNT(G.bind_states); ++i) { + G.bind_states[i] = (struct bind_state) { + .is_held = G.bind_states[i].is_held + }; + } + + for (u64 entity_index = 0; entity_index < events.count; ++entity_index) { + struct sys_event *event = &events.events[entity_index]; + + if (event->kind == SYS_EVENT_KIND_QUIT) { + app_exit(); + } + + if (event->kind == SYS_EVENT_KIND_BUTTON_UP) { + #if DEVELOPER + /* Escape quit */ + if (event->button == SYS_BTN_ESC) { + app_exit(); + } + #endif + } + + /* Update mouse pos */ + if (event->kind == SYS_EVENT_KIND_CURSOR_MOVE) { + G.screen_cursor = event->cursor_position; + } + + /* Update bind states */ + if ((event->kind == SYS_EVENT_KIND_BUTTON_DOWN || event->kind == SYS_EVENT_KIND_BUTTON_UP)) { + enum sys_btn button = event->button; + button = button >= SYS_BTN_COUNT ? SYS_BTN_NONE : button; + enum user_bind_kind bind = g_binds[button]; + if (bind) { + b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN; + #if 0 + b32 out_of_bounds = button >= SYS_BTN_M1 && button <= SYS_BTN_M5 && + (G.ui_cursor.x < 0 || + G.ui_cursor.y < 0 || + G.ui_cursor.x > G.ui_size.x || + G.ui_cursor.y > G.ui_size.y); + #else + b32 out_of_bounds = false; + #endif + G.bind_states[bind].is_held = pressed && !out_of_bounds; + if (pressed) { + if (!out_of_bounds) { + ++G.bind_states[bind].num_presses_and_repeats; + if (!event->is_repeat) { + ++G.bind_states[bind].num_presses; + } + } + } else { + ++G.bind_states[bind].num_releases; + } + } + } + } + } /* ========================== * * Find important entities @@ -620,71 +718,6 @@ INTERNAL void user_update(void) active_player = entity_find_first_match_all(store, (struct entity_prop_array) { .count = ARRAY_COUNT(props), .props = props }); } - /* ========================== * - * Read sys events - * ========================== */ - - struct sys_event_array events = pop_sys_events(scratch.arena); - - /* Reset bind pressed / released states */ - for (u32 i = 0; i < ARRAY_COUNT(G.bind_states); ++i) { - G.bind_states[i] = (struct bind_state) { - .is_held = G.bind_states[i].is_held - }; - } - - for (u64 entity_index = 0; entity_index < events.count; ++entity_index) { - struct sys_event *event = &events.events[entity_index]; - - if (event->kind == SYS_EVENT_KIND_QUIT) { - app_exit(); - } - - if (event->kind == SYS_EVENT_KIND_BUTTON_UP) { -#if DEVELOPER - /* Escape quit */ - if (event->button == SYS_BTN_ESC) { - app_exit(); - } -#endif - } - - /* Update mouse pos */ - if (event->kind == SYS_EVENT_KIND_CURSOR_MOVE) { - G.screen_cursor = event->cursor_position; - } - - /* Update bind states */ - if ((event->kind == SYS_EVENT_KIND_BUTTON_DOWN || event->kind == SYS_EVENT_KIND_BUTTON_UP)) { - enum sys_btn button = event->button; - button = button >= SYS_BTN_COUNT ? SYS_BTN_NONE : button; - enum user_bind_kind bind = g_binds[button]; - if (bind) { - b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN; -#if 0 - b32 out_of_bounds = button >= SYS_BTN_M1 && button <= SYS_BTN_M5 && - (G.ui_cursor.x < 0 || - G.ui_cursor.y < 0 || - G.ui_cursor.x > G.ui_size.x || - G.ui_cursor.y > G.ui_size.y); -#else - b32 out_of_bounds = false; -#endif - G.bind_states[bind].is_held = pressed && !out_of_bounds; - if (pressed) { - if (!out_of_bounds) { - ++G.bind_states[bind].num_presses_and_repeats; - if (!event->is_repeat) { - ++G.bind_states[bind].num_presses; - } - } - } else { - ++G.bind_states[bind].num_releases; - } - } - } - } - /* ========================== * * Debug commands * ========================== */ @@ -1619,8 +1652,21 @@ INTERNAL void user_update(void) } - /* Push game cmds */ - pubilsh_game_cmds(&cmd_list); + /* Publish game cmds */ + { + struct temp_arena temp = arena_temp_begin(scratch.arena); + + struct string cmds_str = game_string_from_cmds(temp.arena, game_cmds_list); + + 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); + } + + host_update(G.host); /* ========================== * * Render