host progress
This commit is contained in:
parent
062285b1b3
commit
a80eff9e39
@ -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;
|
||||
|
||||
20
src/byteio.c
20
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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
137
src/client.c
Normal 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
55
src/client.h
Normal 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
|
||||
@ -501,7 +501,7 @@ struct entity_handle {
|
||||
u64 gen;
|
||||
};
|
||||
|
||||
struct space_client_handle {
|
||||
struct space_entry_handle {
|
||||
u64 idx;
|
||||
u64 gen;
|
||||
};
|
||||
|
||||
19
src/entity.c
19
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;
|
||||
}
|
||||
|
||||
@ -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 */
|
||||
|
||||
502
src/game.c
502
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;
|
||||
|
||||
@ -555,11 +491,65 @@ INTERNAL void game_update(struct game_cmd_list game_cmds)
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Process global game cmds
|
||||
* Release entities
|
||||
* ========================== */
|
||||
|
||||
release_entities_with_prop(ENTITY_PROP_RELEASE_NEXT_TICK);
|
||||
|
||||
/* ========================== *
|
||||
* Activate entities
|
||||
* ========================== */
|
||||
|
||||
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)) {
|
||||
u64 atick = ent->activation_tick;
|
||||
if (atick != 0 || G.tick.tick_id >= atick) {
|
||||
entity_activate(ent, G.tick.tick_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Reset triggered entities
|
||||
* ========================== */
|
||||
|
||||
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)) {
|
||||
entity_disable_prop(ent, ENTITY_PROP_TRIGGER_NEXT_TICK);
|
||||
entity_enable_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK);
|
||||
} else if (entity_has_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK)) {
|
||||
entity_disable_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK);
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Process host events into game cmds
|
||||
* ========================== */
|
||||
|
||||
|
||||
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) {
|
||||
switch (cmd->kind) {
|
||||
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:
|
||||
{
|
||||
@ -578,54 +568,38 @@ INTERNAL void game_update(struct game_cmd_list game_cmds)
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Release entities
|
||||
* ========================== */
|
||||
|
||||
release_entities_with_prop(ENTITY_PROP_RELEASE_NEXT_TICK);
|
||||
|
||||
/* ========================== *
|
||||
* Activate entities
|
||||
* ========================== */
|
||||
|
||||
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
|
||||
struct entity *ent = &store->entities[entity_index];
|
||||
if (!ent->valid) continue;
|
||||
|
||||
if (!entity_has_prop(ent, ENTITY_PROP_ACTIVE)) {
|
||||
u64 atick = ent->activation_tick;
|
||||
if (atick != 0 || G.tick.tick_id >= atick) {
|
||||
entity_activate(ent, G.tick.tick_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Reset triggered entities
|
||||
* ========================== */
|
||||
|
||||
for (u64 entity_index = 0; entity_index < store->reserved; ++entity_index) {
|
||||
struct entity *ent = &store->entities[entity_index];
|
||||
if (!entity_is_valid_and_active(ent)) continue;
|
||||
|
||||
if (entity_has_prop(ent, ENTITY_PROP_TRIGGER_NEXT_TICK)) {
|
||||
entity_disable_prop(ent, ENTITY_PROP_TRIGGER_NEXT_TICK);
|
||||
entity_enable_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK);
|
||||
} else if (entity_has_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK)) {
|
||||
entity_disable_prop(ent, ENTITY_PROP_TRIGGERED_THIS_TICK);
|
||||
} 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 entity from sprite
|
||||
* Update entities from sprite
|
||||
* ========================== */
|
||||
|
||||
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 (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,17 +1384,32 @@ 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);
|
||||
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->kind = GAME_CMD_KIND_CLIENT_CONNECT;
|
||||
} break;
|
||||
|
||||
case HOST_EVENT_KIND_CHANNEL_CLOSED:
|
||||
{
|
||||
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;
|
||||
|
||||
@ -1434,72 +1437,103 @@ struct game_cmd_list game_cmds_from_string(struct arena *arena, struct string st
|
||||
|
||||
ASSERT(br_pos(&br) == cmd_pos_end);
|
||||
br_seek_to(&br, cmd_pos_end);
|
||||
}
|
||||
} break;
|
||||
|
||||
if (l.last) {
|
||||
l.last->next = cmd;
|
||||
default: break;
|
||||
}
|
||||
|
||||
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);
|
||||
game_update();
|
||||
}
|
||||
/* 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);
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
}
|
||||
|
||||
51
src/game.h
51
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
|
||||
|
||||
404
src/host.c
Normal file
404
src/host.c
Normal 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
4
src/host.h
Normal file
@ -0,0 +1,4 @@
|
||||
#ifndef HOST_H
|
||||
#define HOST_H
|
||||
|
||||
#endif
|
||||
20
src/phys.c
20
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
148
src/space.c
148
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;
|
||||
}
|
||||
|
||||
48
src/space.h
48
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
|
||||
|
||||
198
src/user.c
198
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,27 +580,66 @@ INTERNAL void user_update(void)
|
||||
tick_is_first_frame = G.world.tick_id == 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ========================== *
|
||||
* Find important entities
|
||||
* Process host events into game events
|
||||
* ========================== */
|
||||
|
||||
struct entity *active_camera;
|
||||
struct game_event_list game_events = ZI;
|
||||
{
|
||||
enum entity_prop props[] = { ENTITY_PROP_CAMERA, ENTITY_PROP_CAMERA_ACTIVE };
|
||||
active_camera = entity_find_first_match_all(store, (struct entity_prop_array) { .count = ARRAY_COUNT(props), .props = props });
|
||||
}
|
||||
|
||||
struct entity *active_player;
|
||||
{
|
||||
enum entity_prop props[] = { ENTITY_PROP_PLAYER_CONTROLLED, ENTITY_PROP_ACTIVE };
|
||||
active_player = entity_find_first_match_all(store, (struct entity_prop_array) { .count = ARRAY_COUNT(props), .props = props });
|
||||
struct host_events_array = host_pop_events(scratch.arena, G.host);
|
||||
game_events_from_host_events(scratch.arena, host_events, &game_events);
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Read sys 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 */
|
||||
@ -641,12 +657,12 @@ INTERNAL void user_update(void)
|
||||
}
|
||||
|
||||
if (event->kind == SYS_EVENT_KIND_BUTTON_UP) {
|
||||
#if DEVELOPER
|
||||
#if DEVELOPER
|
||||
/* Escape quit */
|
||||
if (event->button == SYS_BTN_ESC) {
|
||||
app_exit();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Update mouse pos */
|
||||
@ -661,15 +677,15 @@ INTERNAL void user_update(void)
|
||||
enum user_bind_kind bind = g_binds[button];
|
||||
if (bind) {
|
||||
b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN;
|
||||
#if 0
|
||||
#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
|
||||
#else
|
||||
b32 out_of_bounds = false;
|
||||
#endif
|
||||
#endif
|
||||
G.bind_states[bind].is_held = pressed && !out_of_bounds;
|
||||
if (pressed) {
|
||||
if (!out_of_bounds) {
|
||||
@ -684,6 +700,23 @@ INTERNAL void user_update(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* Find important entities
|
||||
* ========================== */
|
||||
|
||||
struct entity *active_camera;
|
||||
{
|
||||
enum entity_prop props[] = { ENTITY_PROP_CAMERA, ENTITY_PROP_CAMERA_ACTIVE };
|
||||
active_camera = entity_find_first_match_all(store, (struct entity_prop_array) { .count = ARRAY_COUNT(props), .props = props });
|
||||
}
|
||||
|
||||
struct entity *active_player;
|
||||
{
|
||||
enum entity_prop props[] = { ENTITY_PROP_PLAYER_CONTROLLED, ENTITY_PROP_ACTIVE };
|
||||
active_player = entity_find_first_match_all(store, (struct entity_prop_array) { .count = ARRAY_COUNT(props), .props = props });
|
||||
}
|
||||
|
||||
/* ========================== *
|
||||
* 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user