serialize game cmds

This commit is contained in:
jacob 2025-01-30 13:36:44 -06:00
parent 7d0826b57b
commit a1a1430fd8
7 changed files with 413 additions and 302 deletions

View File

@ -647,7 +647,10 @@ struct ase_decode_image_result ase_decode_image(struct arena *arena, struct buff
br_seek(&br, sizeof(u8) * 3); br_seek(&br, sizeof(u8) * 3);
u16 str_len = br_read_u16(&br); u16 str_len = br_read_u16(&br);
u8 *str_bytes = br_read_raw(&br, str_len); u8 *str_bytes = br_seek(&br, str_len);
if (!str_bytes) {
str_len = 0;
}
layer->name = (struct string) { layer->name = (struct string) {
str_len, str_len,
arena_push_array(scratch.arena, u8, str_len) arena_push_array(scratch.arena, u8, str_len)
@ -898,7 +901,10 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct buff
br_seek(&br, 13); br_seek(&br, 13);
u16 str_len = br_read_u16(&br); u16 str_len = br_read_u16(&br);
u8 *str_bytes = br_read_raw(&br, str_len); u8 *str_bytes = br_seek(&br, str_len);
if (!str_bytes) {
str_len = 0;
}
span->name = (struct string) { span->name = (struct string) {
str_len, str_len,
arena_push_array(arena, u8, str_len) arena_push_array(arena, u8, str_len)
@ -923,7 +929,10 @@ struct ase_decode_sheet_result ase_decode_sheet(struct arena *arena, struct buff
struct string name; struct string name;
{ {
u16 str_len = br_read_u16(&br); u16 str_len = br_read_u16(&br);
u8 *str_bytes = br_read_raw(&br, str_len); u8 *str_bytes = br_seek(&br, str_len);
if (!str_bytes) {
str_len = 0;
}
name = (struct string) { name = (struct string) {
str_len, str_len,
arena_push_array(arena, u8, str_len) arena_push_array(arena, u8, str_len)

View File

@ -6,37 +6,73 @@
* Writer * Writer
* ========================== */ * ========================== */
INTERNAL b32 write_check_overflow(struct byte_writer *bw, u64 amount) INTERNAL b32 write_check_overflow(struct byte_writer *bw, i64 amount)
{ {
b32 overflowed = bw->overflowed; b32 overflowed = bw->overflowed;
i64 new_space_left = (bw->at + amount) - (bw->buff.data + bw->buff.size); if (overflowed || amount < 0) {
if (!overflowed && new_space_left < 0) { overflowed = true;
if (bw->arena) { bw->overflowed = true;
arena_push_array(bw->arena, u8, -new_space_left); } else {
bw->buff.size += -new_space_left; u8 *end = bw->buff.data + bw->buff.size;
/* Writer memory should be contiguous in arena */ u8 *new_pos = bw->at + amount;
ASSERT((bw->buff.data + bw->buff.size) == (bw->arena->base + bw->arena->pos)); i64 new_space_left = end - new_pos;
} else { if (new_space_left < 0) {
bw->overflowed = true; struct arena *arena = bw->arena;
overflowed = true; if (arena) {
if ((arena->base + arena->pos) == end) {
arena_push_array(arena, u8, -new_space_left);
bw->buff.size += -new_space_left;
} else {
/* Writer memory must be contiguous in arena */
overflowed = true;
bw->overflowed = true;
}
} else {
overflowed = true;
bw->overflowed = true;
}
} }
} }
ASSERT(!overflowed); ASSERT(!overflowed);
return overflowed; return overflowed;
} }
INTERNAL void write_unsafe(struct byte_writer *bw, void *v, u64 size) INTERNAL void write(struct byte_writer *bw, void *v, u64 size)
{ {
if (write_check_overflow(bw, size)) {
return;
}
MEMCPY(bw->at, v, size); MEMCPY(bw->at, v, size);
bw->at += size; bw->at += size;
} }
INTERNAL void write(struct byte_writer *bw, void *v, u64 size) struct byte_writer bw_from_buffer(struct buffer buff)
{ {
if (write_check_overflow(bw, sizeof(v))) { struct byte_writer bw = ZI;
return; bw.buff = buff;
} bw.at = buff.data;
write_unsafe(bw, v, size); return bw;
}
/* Returns a writer that will allocate to arena instead of overflowing (writes must stay contiguous in arena) */
struct byte_writer bw_from_arena(struct arena *arena)
{
struct byte_writer bw = ZI;
bw.arena = arena;
bw.buff.data = arena->base + arena->pos;
bw.buff.size = 0;
bw.at = bw.buff.data;
return bw;
}
/* Seeks forward and returns a new writer pointing to the skipped bytes */
struct byte_writer bw_branch(struct byte_writer *bw, u64 size)
{
struct buffer buff = BUFFER(size, bw->at);
struct byte_writer branch = bw_from_buffer(buff);
bw_seek(bw, size);
branch.overflowed = bw->overflowed;
return branch;
} }
void bw_seek(struct byte_writer *bw, u64 amount) void bw_seek(struct byte_writer *bw, u64 amount)
@ -49,9 +85,8 @@ void bw_seek(struct byte_writer *bw, u64 amount)
void bw_seek_to(struct byte_writer *bw, u64 pos) void bw_seek_to(struct byte_writer *bw, u64 pos)
{ {
if (pos > bw->buff.size) { if (write_check_overflow(bw, (i64)pos - (i64)(bw->at - bw->buff.data))) {
ASSERT(false); return;
bw->overflowed = true;
} }
bw->at = bw->buff.data + pos; bw->at = bw->buff.data + pos;
} }
@ -113,140 +148,137 @@ void bw_write_var_sint(struct byte_writer *bw, i64 v)
bw_write_i64(bw, v); bw_write_i64(bw, v);
} }
void bw_write_f32(struct byte_writer *bw, f32 v)
{
write(bw, &v, sizeof(v));
}
void bw_write_f64(struct byte_writer *bw, f64 v)
{
write(bw, &v, sizeof(v));
}
void bw_write_v2(struct byte_writer *bw, struct v2 v)
{
bw_write_f32(bw, v.x);
bw_write_f32(bw, v.y);
}
/* ========================== * /* ========================== *
* Reader * Reader
* ========================== */ * ========================== */
INTERNAL b32 read_check_overflow(struct byte_reader *br, u64 amount) INTERNAL b32 read_check_overflow(struct byte_reader *br, i64 amount)
{ {
if (br->overflowed || br->at + amount > br->buff.data + br->buff.size) { b32 overflowed = br->overflowed;
if (overflowed || amount < 0 || (br->at + amount) > (br->buff.data + br->buff.size) ){
ASSERT(false); ASSERT(false);
br->overflowed = true; br->overflowed = true;
return true; overflowed = true;
} }
return false; return overflowed;
} }
void br_seek(struct byte_reader *br, u64 amount) INTERNAL void read(struct byte_reader *br, void *dst, u64 size)
{
if (read_check_overflow(br, amount)) {
return;
}
br->at += amount;
}
void br_seek_to(struct byte_reader *br, u64 pos)
{
if (pos > br->buff.size) {
ASSERT(false);
br->overflowed = true;
}
br->at = br->buff.data + pos;
}
INTERNAL void *read_unsafe(struct byte_reader *br, u64 size)
{
void *prev = br->at;
br->at += size;
return prev;
}
/* Will return NULL on overflow */
void *br_read_raw(struct byte_reader *br, u64 size)
{ {
if (read_check_overflow(br, size)) { if (read_check_overflow(br, size)) {
return NULL; MEMZERO(dst, size);
} }
return read_unsafe(br, size); MEMCPY(dst, br->at, size);
br->at += size;
} }
/* Will not read any data on overflow */ struct byte_reader br_from_buffer(struct buffer buff)
{
struct byte_reader br = ZI;
br.buff = buff;
br.at = buff.data;
return br;
}
/* 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;
}
ptr = br->at;
br->at += amount;
return ptr;
}
/* 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.data))) {
return ptr;
}
ptr = br->at;
br->at = br->buff.data + pos;
return ptr;
}
/* Will fill buff with zeroes on overflow */
void br_read_to_buffer(struct byte_reader *br, struct buffer buff) void br_read_to_buffer(struct byte_reader *br, struct buffer buff)
{ {
if (read_check_overflow(br, buff.size)) { read(br, buff.data, buff.size);
return;
}
u8 *bytes = read_unsafe(br, buff.size);
MEMCPY(buff.data, bytes, buff.size);
} }
u8 br_read_u8(struct byte_reader *br) u8 br_read_u8(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(u8))) { u8 res;
return 0; read(br, &res, sizeof(res));
}
u8 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(u8)), sizeof(u8));
return res; return res;
} }
u16 br_read_u16(struct byte_reader *br) u16 br_read_u16(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(u16))) { u16 res;
return 0; read(br, &res, sizeof(res));
}
u16 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(u16)), sizeof(u16));
return res; return res;
} }
u32 br_read_u32(struct byte_reader *br) u32 br_read_u32(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(u32))) { u32 res;
return 0; read(br, &res, sizeof(res));
}
u32 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(u32)), sizeof(u32));
return res; return res;
} }
u64 br_read_u64(struct byte_reader *br) u64 br_read_u64(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(u64))) { u64 res;
return 0; read(br, &res, sizeof(res));
}
u64 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(u64)), sizeof(u64));
return res; return res;
} }
i8 br_read_i8(struct byte_reader *br) i8 br_read_i8(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(i8))) { i8 res;
return 0; read(br, &res, sizeof(res));
}
i8 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(i8)), sizeof(i8));
return res; return res;
} }
i16 br_read_i16(struct byte_reader *br) i16 br_read_i16(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(i16))) {
return 0;
}
i16 res = 0; i16 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(i16)), sizeof(i16)); read(br, &res, sizeof(res));
return res; return res;
} }
i32 br_read_i32(struct byte_reader *br) i32 br_read_i32(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(i32))) {
return 0;
}
i32 res = 0; i32 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(i32)), sizeof(i32)); read(br, &res, sizeof(res));
return res; return res;
} }
i64 br_read_i64(struct byte_reader *br) i64 br_read_i64(struct byte_reader *br)
{ {
if (read_check_overflow(br, sizeof(i64))) { i64 res;
return 0; read(br, &res, sizeof(res));
}
i64 res = 0;
MEMCPY(&res, read_unsafe(br, sizeof(i64)), sizeof(i64));
return res; return res;
} }
@ -261,3 +293,25 @@ i64 br_read_var_sint(struct byte_reader *br)
/* TODO: real varint read */ /* TODO: real varint read */
return br_read_i64(br); return br_read_i64(br);
} }
f32 br_read_f32(struct byte_reader *br)
{
f32 res;
read(br, &res, sizeof(res));
return res;
}
f64 br_read_f64(struct byte_reader *br)
{
f64 res;
read(br, &res, sizeof(res));
return res;
}
struct v2 br_read_v2(struct byte_reader *br)
{
struct v2 res;
res.x = br_read_f32(br);
res.y = br_read_f32(br);
return res;
}

View File

@ -15,62 +15,12 @@ struct byte_reader {
}; };
/* ========================== * /* ========================== *
* Constructor utils * Writer
* ========================== */ * ========================== */
INLINE struct byte_writer bw_from_buffer(struct buffer buff) struct byte_writer bw_from_buffer(struct buffer buff);
{ struct byte_writer bw_from_arena(struct arena *arena);
struct byte_writer bw = ZI; struct byte_writer bw_branch(struct byte_writer *bw, u64 size);
bw.buff = buff;
bw.at = buff.data;
return bw;
}
INLINE struct byte_writer bw_from_arena(struct arena *arena)
{
struct byte_writer bw = ZI;
bw.arena = arena;
bw.buff.data = arena->base;
bw.buff.size = 0;
bw.at = bw.buff.data;
return bw;
}
INLINE struct byte_reader br_from_buffer(struct buffer buff)
{
struct byte_reader br = ZI;
br.buff = buff;
br.at = buff.data;
return br;
}
INLINE struct byte_writer bw_copy(struct byte_writer *bw)
{
return *bw;
}
INLINE struct byte_reader br_copy(struct byte_reader *br)
{
return *br;
}
/* Generate a buffer struct containing written bytes only */
INLINE struct buffer bw_get_written_buffer(struct byte_writer *bw)
{
struct buffer buff = ZI;
buff.data = bw->buff.data;
buff.size = bw->at - bw->buff.data;
return buff;
}
INLINE u64 bw_pos(struct byte_reader *bw)
{
return bw->at - bw->buff.data;
}
/* ========================== *
* Write
* ========================== */
void bw_seek(struct byte_writer *bw, u64 amount); void bw_seek(struct byte_writer *bw, u64 amount);
void bw_seek_to(struct byte_writer *bw, u64 pos); void bw_seek_to(struct byte_writer *bw, u64 pos);
@ -86,18 +36,36 @@ void bw_write_i32(struct byte_writer *bw, i32 v);
void bw_write_i64(struct byte_writer *bw, i64 v); void bw_write_i64(struct byte_writer *bw, i64 v);
void bw_write_var_uint(struct byte_writer *bw, u64 v); void bw_write_var_uint(struct byte_writer *bw, u64 v);
void bw_write_var_sint(struct byte_writer *bw, i64 v); void bw_write_var_sint(struct byte_writer *bw, i64 v);
void bw_write_f32(struct byte_writer *bw, f32 v);
void bw_write_f64(struct byte_writer *bw, f64 v);
void bw_write_v2(struct byte_writer *bw, struct v2 v);
/* Returns a buffer containing written bytes only */
INLINE struct buffer bw_get_written(struct byte_writer *bw)
{
struct buffer buff = ZI;
buff.data = bw->buff.data;
buff.size = bw->at - bw->buff.data;
return buff;
}
INLINE u64 bw_pos(struct byte_writer *bw)
{
return bw->at - bw->buff.data;
}
/* ========================== * /* ========================== *
* Read * Reader
* ========================== */ * ========================== */
void br_seek(struct byte_reader *br, u64 amount); /* Will fill struct with zeroes on overflow */
void br_seek_to(struct byte_reader *br, u64 pos);
/* Will not read any data on overflow */
#define br_read_to_struct(br_ptr, var_ptr) (br_read_to_buffer(br_ptr, BUFFER(sizeof(*var_ptr), (u8 *)var_ptr))) #define br_read_to_struct(br_ptr, var_ptr) (br_read_to_buffer(br_ptr, BUFFER(sizeof(*var_ptr), (u8 *)var_ptr)))
void *br_read_raw(struct byte_reader *br, u64 size); struct byte_reader br_from_buffer(struct buffer buff);
void *br_seek(struct byte_reader *br, u64 amount);
void *br_seek_to(struct byte_reader *br, u64 pos);
void br_read_to_buffer(struct byte_reader *br, struct buffer buff); void br_read_to_buffer(struct byte_reader *br, struct buffer buff);
u8 br_read_u8(struct byte_reader *br); u8 br_read_u8(struct byte_reader *br);
u16 br_read_u16(struct byte_reader *br); u16 br_read_u16(struct byte_reader *br);
@ -109,6 +77,9 @@ i32 br_read_i32(struct byte_reader *br);
i64 br_read_i64(struct byte_reader *br); i64 br_read_i64(struct byte_reader *br);
u64 br_read_var_uint(struct byte_reader *br); u64 br_read_var_uint(struct byte_reader *br);
i64 br_read_var_sint(struct byte_reader *br); i64 br_read_var_sint(struct byte_reader *br);
f32 br_read_f32(struct byte_reader *br);
f64 br_read_f64(struct byte_reader *br);
struct v2 br_read_v2(struct byte_reader *br);
INLINE u64 br_bytes_left(const struct byte_reader *br) INLINE u64 br_bytes_left(const struct byte_reader *br)
{ {

View File

@ -14,6 +14,7 @@
#include "collider.h" #include "collider.h"
#include "rng.h" #include "rng.h"
#include "space.h" #include "space.h"
#include "byteio.h"
GLOBAL struct { GLOBAL struct {
struct atomic_i32 game_thread_shutdown; struct atomic_i32 game_thread_shutdown;
@ -31,9 +32,10 @@ GLOBAL struct {
b32 extra_spawn; b32 extra_spawn;
b32 should_reset_level; b32 should_reset_level;
/* Game thread input */ /* Game input */
struct sys_mutex game_cmds_mutex; struct sys_mutex game_input_mutex;
struct arena game_cmds_arena; struct arena game_input_arena;
struct entity *root; struct entity *root;
/* Bookkeeping structures */ /* Bookkeeping structures */
@ -69,9 +71,9 @@ struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr,
(UNUSED)sound_sr; (UNUSED)sound_sr;
(UNUSED)phys_sr; (UNUSED)phys_sr;
/* Initialize game cmd storage */ /* Initialize game input storage */
G.game_cmds_mutex = sys_mutex_alloc(); G.game_input_mutex = sys_mutex_alloc();
G.game_cmds_arena = arena_alloc(GIGABYTE(64)); G.game_input_arena = arena_alloc(GIGABYTE(64));
/* Initialize empty world */ /* Initialize empty world */
reset_world(); reset_world();
@ -95,34 +97,6 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(game_shutdown)
sys_thread_wait_release(&G.game_thread); sys_thread_wait_release(&G.game_thread);
} }
/* ========================== *
* Game cmd
* ========================== */
INTERNAL void push_cmds(struct game_cmd_array cmd_array)
{
struct sys_lock lock = sys_mutex_lock_e(&G.game_cmds_mutex);
struct game_cmd *cmds = arena_push_array(&G.game_cmds_arena, struct game_cmd, cmd_array.count);
MEMCPY(cmds, cmd_array.cmds, cmd_array.count * sizeof(*cmds));
sys_mutex_unlock(&lock);
}
INTERNAL struct game_cmd_array pop_cmds(struct arena *arena)
{
struct game_cmd_array array = ZI;
if (G.game_cmds_arena.pos > 0) {
struct sys_lock lock = sys_mutex_lock_e(&G.game_cmds_mutex);
struct buffer game_cmds_buff = arena_to_buffer(&G.game_cmds_arena);
arena_align(arena, alignof(struct game_cmd));
array.cmds = (struct game_cmd *)arena_push_array(arena, u8, game_cmds_buff.size);
array.count = game_cmds_buff.size / sizeof(struct game_cmd);
MEMCPY(array.cmds, game_cmds_buff.data, game_cmds_buff.size);
arena_reset(&G.game_cmds_arena);
sys_mutex_unlock(&lock);
}
return array;
}
/* ========================== * /* ========================== *
* Reset * Reset
* ========================== */ * ========================== */
@ -480,7 +454,7 @@ INTERNAL void publish_game_tick(void)
sys_mutex_unlock(&lock); sys_mutex_unlock(&lock);
} }
INTERNAL void game_update(struct game_cmd_array game_cmds) INTERNAL void game_update(struct game_cmd_list game_cmds)
{ {
__prof; __prof;
@ -537,14 +511,12 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
* Process global game cmds * Process global game cmds
* ========================== */ * ========================== */
for (u64 cmd_index = 0; cmd_index < game_cmds.count; ++cmd_index) { for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) {
struct game_cmd cmd = game_cmds.cmds[cmd_index]; switch (cmd->kind) {
switch (cmd.kind) {
/* Cursor */ /* Cursor */
case GAME_CMD_KIND_CURSOR_MOVE: case GAME_CMD_KIND_CURSOR_MOVE:
{ {
G.user_cursor = cmd.cursor_pos; G.user_cursor = cmd->cursor_pos;
} break; } break;
/* Clear level */ /* Clear level */
@ -735,19 +707,18 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
struct v2 focus = ent->control.focus; struct v2 focus = ent->control.focus;
b32 firing = entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED); b32 firing = entity_has_prop(ent, ENTITY_PROP_TRIGGERING_EQUIPPED);
for (u64 i = 0; i < game_cmds.count; ++i) { for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) {
struct game_cmd cmd = game_cmds.cmds[i]; b32 start = cmd->state == GAME_CMD_STATE_START;
b32 start = cmd.state == GAME_CMD_STATE_START; b32 stop = cmd->state == GAME_CMD_STATE_STOP;
b32 stop = cmd.state == GAME_CMD_STATE_STOP;
/* TODO: Combine movement from multiple inputs? E.G. a sudden /* TODO: Combine movement from multiple inputs? E.G. a sudden
* start and immediate stop cmd should still move the player a * start and immediate stop cmd should still move the player a
* tad. */ * tad. */
switch (cmd.kind) { switch (cmd->kind) {
case GAME_CMD_KIND_PLAYER_MOVE: case GAME_CMD_KIND_PLAYER_MOVE:
{ {
move = cmd.move_dir; move = cmd->move_dir;
focus = cmd.aim_dir; focus = cmd->aim_dir;
} break; } break;
case GAME_CMD_KIND_PLAYER_FIRE: case GAME_CMD_KIND_PLAYER_FIRE:
@ -1094,12 +1065,11 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
/* Mouse drag */ /* Mouse drag */
ctx.dbg_cursor_pos = G.user_cursor; ctx.dbg_cursor_pos = G.user_cursor;
for (u64 i = 0; i < game_cmds.count; ++i) { for (struct game_cmd *cmd = game_cmds.first; cmd; cmd = cmd->next) {
struct game_cmd cmd = game_cmds.cmds[i]; if (cmd->kind == GAME_CMD_KIND_DRAG_OBJECT) {
if (cmd.kind == GAME_CMD_KIND_DRAG_OBJECT) { if (cmd->state == GAME_CMD_STATE_START) {
if (cmd.state == GAME_CMD_STATE_START) {
ctx.dbg_start_dragging = true; ctx.dbg_start_dragging = true;
} else if (cmd.state == GAME_CMD_STATE_STOP) { } else if (cmd->state == GAME_CMD_STATE_STOP) {
ctx.dbg_stop_dragging = true; ctx.dbg_stop_dragging = true;
} }
} }
@ -1342,50 +1312,116 @@ INTERNAL void game_update(struct game_cmd_array game_cmds)
} }
/* ========================== * /* ========================== *
* Game thread * Game cmd
* ========================== */ * ========================== */
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg) struct buffer game_string_from_cmds(struct arena *arena, struct game_cmd_list *cmds)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct byte_writer bw = bw_from_arena(arena);
(UNUSED)arg; for (struct game_cmd *cmd = cmds->first; cmd; cmd = cmd->next) {
i64 last_frame_ns = 0; struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
i64 target_dt_ns = NS_FROM_SECONDS(GAME_FPS > (0) ? (1.0 / GAME_FPS) : 0); u64 start = bw_pos(&bw);
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 game_cmd_array game_cmds = pop_cmds(temp.arena);
if (!G.paused) {
game_update(game_cmds);
}
/* Check for pause / next frame cmds */
for (u64 i = 0; i < game_cmds.count; ++i) {
struct game_cmd cmd = game_cmds.cmds[i];
switch (cmd.kind) {
case GAME_CMD_KIND_PAUSE: {
G.paused = !G.paused;
} break;
case GAME_CMD_KIND_STEP: { bw_write_i8(&bw, cmd->kind);
if (G.paused) { bw_write_i8(&bw, cmd->state);
G.paused = false;
game_update(game_cmds);
G.paused = true;
}
} break;
default: break; #if RTC
} bw_write_u32(&bw, cmd->collider_gjk_steps);
} #endif
switch (cmd->kind) {
case GAME_CMD_KIND_PLAYER_MOVE:
{
bw_write_v2(&bw, cmd->move_dir);
bw_write_v2(&bw, cmd->aim_dir);
} break;
case GAME_CMD_KIND_CURSOR_MOVE:
{
bw_write_v2(&bw, cmd->cursor_pos);
} break;
default: break;
} }
arena_temp_end(temp);
u64 size = bw_pos(&bw) - start;
if (size > 100) {
DEBUGBREAKABLE;
}
bw_write_u64(&bw_size, size);
} }
scratch_end(scratch); return bw.buff;
}
struct game_cmd_list game_cmds_from_string(struct arena *arena, struct buffer str)
{
struct game_cmd_list l = ZI;
struct byte_reader br = br_from_buffer(str);
while (br_bytes_left(&br) > 0) {
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:
{
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);
if (l.last) {
l.last->next = cmd;
} else {
l.first = cmd;
}
l.last = cmd;
}
return l;
}
/* ========================== *
* Game cmd
* ========================== */
INTERNAL void push_input_string(struct buffer input)
{
struct sys_lock lock = sys_mutex_lock_e(&G.game_input_mutex);
u8 *dst = arena_push_array(&G.game_input_arena, u8, input.size);
MEMCPY(dst, input.data, input.size);
sys_mutex_unlock(&lock);
}
INTERNAL struct buffer pop_input_string(struct arena *arena)
{
struct buffer buff = ZI;
struct sys_lock lock = sys_mutex_lock_e(&G.game_input_mutex);
buff.size = G.game_input_arena.pos;
buff.data = arena_push_array(arena, u8, buff.size);
MEMCPY(buff.data, G.game_input_arena.base, buff.size);
arena_reset(&G.game_input_arena);
sys_mutex_unlock(&lock);
return buff;
} }
/* ========================== * /* ========================== *
@ -1410,7 +1446,56 @@ u64 game_get_latest_tick_continuity_gen(void)
return atomic_u64_eval(&G.prev_tick_continuity_gen); return atomic_u64_eval(&G.prev_tick_continuity_gen);
} }
void game_push_cmds(struct game_cmd_array cmd_array) void game_push_cmds_string(struct buffer str)
{ {
push_cmds(cmd_array); push_input_string(str);
}
/* ========================== *
* Game thread
* ========================== */
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(game_thread_entry_point, arg)
{
struct temp_arena scratch = scratch_begin_no_conflict();
(UNUSED)arg;
i64 last_frame_ns = 0;
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 buffer 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);
}
scratch_end(scratch);
} }

View File

@ -7,8 +7,25 @@ struct sprite_startup_receipt;
struct sound_startup_receipt; struct sound_startup_receipt;
struct phys_startup_receipt; struct phys_startup_receipt;
/* Absolute layers */
#define GAME_LAYER_FLOOR_DECALS -300
#define GAME_LAYER_BULLETS -200
#define GAME_LAYER_TRACERS -100
#define GAME_LAYER_SHOULDERS 0
/* Relative layers */
#define GAME_LAYER_RELATIVE_DEFAULT 0
#define GAME_LAYER_RELATIVE_WEAPON 1
struct game_startup_receipt { i32 _; };
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);
/* ========================== * /* ========================== *
* Game cmd * Game CMD
* ========================== */ * ========================== */
enum game_cmd_state { enum game_cmd_state {
@ -48,33 +65,25 @@ struct game_cmd {
#if RTC #if RTC
u32 collider_gjk_steps; u32 collider_gjk_steps;
#endif #endif
struct game_cmd *next;
}; };
struct game_cmd_array { struct game_cmd_list {
struct game_cmd *cmds; struct game_cmd *first;
u64 count; struct game_cmd *last;
}; };
/* Absolute layers */ /* ========================== *
#define GAME_LAYER_FLOOR_DECALS -300 * Interface
#define GAME_LAYER_BULLETS -200 * ========================== */
#define GAME_LAYER_TRACERS -100
#define GAME_LAYER_SHOULDERS 0
/* Relative layers */ struct buffer game_string_from_cmds(struct arena *arena, struct game_cmd_list *cmds);
#define GAME_LAYER_RELATIVE_DEFAULT 0 struct game_cmd_list game_cmds_from_string(struct arena *arena, struct buffer str);
#define GAME_LAYER_RELATIVE_WEAPON 1
struct game_startup_receipt { i32 _; };
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);
void game_get_latest_tick(struct world *dest); void game_get_latest_tick(struct world *dest);
u64 game_get_latest_tick_id(void); u64 game_get_latest_tick_id(void);
u64 game_get_latest_tick_continuity_gen(void); u64 game_get_latest_tick_continuity_gen(void);
void game_push_cmds_string(struct buffer str);
void game_push_cmds(struct game_cmd_array cmd_array);
#endif #endif

View File

@ -93,10 +93,11 @@ struct tar_archive tar_parse(struct arena *arena, struct buffer data, struct str
struct string file_size_oct_str = { .len = 11, .text = header.file_size }; struct string file_size_oct_str = { .len = 11, .text = header.file_size };
u64 file_size = str_oct_to_u64(file_size_oct_str); u64 file_size = str_oct_to_u64(file_size_oct_str);
struct buffer file_data = { u8 *file_data_ptr = br_seek(&br, file_size);
.size = file_size, if (!file_data_ptr) {
.data = br_read_raw(&br, file_size) file_size = 0;
}; }
struct buffer file_data = BUFFER(file_size, file_data_ptr);
/* Skip sector padding */ /* Skip sector padding */
u64 remaining = (512 - (file_size % 512)) % 512; u64 remaining = (512 - (file_size % 512)) % 512;

View File

@ -360,48 +360,30 @@ INTERNAL struct interp_ticks pull_ticks(i64 blend_time_ns)
* User -> game communication * User -> game communication
* ========================== */ * ========================== */
struct game_cmd_node { INTERNAL void queue_game_cmd(struct arena *arena, struct game_cmd_list *list, struct game_cmd src)
struct game_cmd cmd;
struct game_cmd_node *next;
};
struct game_cmd_list {
struct game_cmd_node *first;
struct game_cmd_node *last;
};
INTERNAL void queue_game_cmd(struct arena *arena, struct game_cmd_list *list, struct game_cmd cmd)
{ {
struct game_cmd_node *node = arena_push_zero(arena, struct game_cmd_node); struct game_cmd *cmd = arena_push(arena, struct game_cmd);
node->cmd = cmd; *cmd = src;
if (list->last) { if (list->last) {
list->last->next = node; list->last->next = cmd;
} else { } else {
list->first = node; list->first = cmd;
} }
list->last = node; list->last = cmd;
} }
INTERNAL void pubilsh_game_cmds(struct game_cmd_list *list) INTERNAL void pubilsh_game_cmds(struct game_cmd_list *list)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
struct buffer buff = game_string_from_cmds(scratch.arena, list);
/* Construct array */ game_push_cmds_string(buff);
struct game_cmd_array array = { .cmds = arena_dry_push(scratch.arena, struct game_cmd) };
for (struct game_cmd_node *node = list->first; node; node = node->next) {
struct game_cmd *cmd = arena_push(scratch.arena, struct game_cmd);
*cmd = node->cmd;
++array.count;
}
/* Push array to game thread */
if (array.count > 0) {
game_push_cmds(array);
}
scratch_end(scratch); scratch_end(scratch);
} }
/* ========================== *
* Debug draw
* ========================== */
/* TODO: remove this (testing) */ /* TODO: remove this (testing) */
INTERNAL void debug_draw_xform(struct xform xf, u32 color_x, u32 color_y) INTERNAL void debug_draw_xform(struct xform xf, u32 color_x, u32 color_y)
{ {