host progress

This commit is contained in:
jacob 2025-02-03 19:54:06 -06:00
parent 062285b1b3
commit a80eff9e39
17 changed files with 1210 additions and 539 deletions

View File

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

View File

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

View File

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

137
src/client.c Normal file
View File

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

55
src/client.h Normal file
View File

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

View File

@ -501,7 +501,7 @@ struct entity_handle {
u64 gen;
};
struct space_client_handle {
struct space_entry_handle {
u64 idx;
u64 gen;
};

View File

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

View File

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

View File

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

View File

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

404
src/host.c Normal file
View File

@ -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 <chunk_id> out of <chunk_count> belonging to message <msg_id> */
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);
}

4
src/host.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef HOST_H
#define HOST_H
#endif

View File

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

View File

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

View File

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

View File

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

View File

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