crappy delta encoding test

This commit is contained in:
jacob 2025-02-11 19:31:06 -06:00
parent fd550a7119
commit ff0fbf0878
14 changed files with 647 additions and 777 deletions

View File

@ -91,7 +91,7 @@ void bw_seek_to(struct byte_writer *bw, u64 pos)
bw->at = bw->buff.text + pos;
}
void br_write_bytes(struct byte_writer *bw, struct string bytes)
void bw_write_bytes(struct byte_writer *bw, struct string bytes)
{
write(bw, bytes.text, bytes.len);
}
@ -167,7 +167,7 @@ void bw_write_v2(struct byte_writer *bw, struct v2 v)
void bw_write_string(struct byte_writer *bw, struct string str)
{
bw_write_var_uint(bw, str.len);
br_write_bytes(bw, str);
bw_write_bytes(bw, str);
}
/* ========================== *

View File

@ -25,7 +25,7 @@ struct byte_writer bw_branch(struct byte_writer *bw, u64 size);
void bw_seek(struct byte_writer *bw, u64 amount);
void bw_seek_to(struct byte_writer *bw, u64 pos);
void br_write_bytes(struct byte_writer *bw, struct string bytes);
void bw_write_bytes(struct byte_writer *bw, struct string bytes);
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);

View File

@ -724,7 +724,7 @@ void host_update(struct host *host)
u64 chunk_count = br_read_var_uint(&br);
b32 is_last_chunk = (chunk_id + 1) == chunk_count;
u64 data_len = is_last_chunk ? (br_read_var_uint(&br) + 1) : PACKET_MSG_CHUNK_MAX_LEN;
u64 data_len = is_last_chunk ? br_read_var_uint(&br) : PACKET_MSG_CHUNK_MAX_LEN;
struct host_msg_assembler *ma = host_get_msg_assembler(host, channel->id, msg_id);
if (!ma) {
@ -916,9 +916,9 @@ void host_update(struct host *host)
bw_write_var_uint(&bw, chunk_count);
if (is_last_chunk) {
/* FIXME: Ensure data_len can never be 0 */
bw_write_var_uint(&bw, data_len - 1);
bw_write_var_uint(&bw, data_len);
}
br_write_bytes(&bw, STRING(data_len, data));
bw_write_bytes(&bw, STRING(data_len, data));
host_packet->data_len = bw_pos(&bw);
}
} break;

286
src/sim.c
View File

@ -1,7 +1,6 @@
#include "sim.h"
#include "sim_ent.h"
#include "sim_client.h"
#include "sim_encode.h"
#include "sim_snapshot.h"
#include "sys.h"
#include "util.h"
@ -22,7 +21,6 @@
* Ctx
* ========================== */
struct sim_ctx *sim_ctx_alloc(struct sprite_startup_receipt *sprite_sr,
struct phys_startup_receipt *phys_sr,
struct host_startup_receipt *host_sr,
@ -79,7 +77,7 @@ void sim_ctx_release(struct sim_ctx *ctx)
/* TODO: Remove this */
INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
INTERNAL void spawn_test_entities(struct sim_ctx *ctx, struct v2 offset)
{
struct sim_ent *root = sim_ent_from_handle(ctx->world, SIM_ENT_ROOT_HANDLE);
root->mass_unscaled = F32_INFINITY;
@ -90,6 +88,7 @@ INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
struct sim_ent *e = sim_ent_alloc(root);
struct v2 pos = V2(1, -2);
pos = v2_add(pos, offset);
f32 r = 0;
struct v2 size = V2(1, 1);
struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size);
@ -112,6 +111,7 @@ INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
struct sim_ent *e = sim_ent_alloc(root);
struct v2 pos = V2(1, -0.5);
pos = v2_add(pos, offset);
f32 r = 0;
struct v2 size = V2(0.5, 0.25);
struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size);
@ -135,6 +135,7 @@ INTERNAL void spawn_test_entities(struct sim_ctx *ctx)
struct sim_ent *e = sim_ent_alloc(root);
struct v2 pos = V2(1, -0.5);
pos = v2_add(pos, offset);
f32 r = PI / 4;
struct v2 size = V2(0.5, 0.25);
struct xform xf = XFORM_TRS(.t = pos, .r = r, .s = size);
@ -408,10 +409,10 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
/* Release old snapshots */
{
/* TODO: Something better */
i64 release_tick = (i64)ctx->world->tick - 5; /* Arbitrary tick offset */
i64 release_tick = (i64)ctx->world->tick - 100; /* Arbitrary tick offset */
if (release_tick > 0) {
struct sim_snapshot *old = sim_snapshot_from_tick(ctx->snapshot_store, release_tick);
if (old) {
if (old->valid) {
sim_snapshot_release(old);
}
}
@ -452,7 +453,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
static b32 run = 0;
if (!run) {
run = 1;
spawn_test_entities(ctx);
spawn_test_entities(ctx, V2(0, 0));
}
}
@ -552,6 +553,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
* ========================== */
/* Process client cmds */
ctx->oldest_client_ack_tick = ctx->world->tick;
for (u64 i = 0; i < ctx->world->num_clients_reserved; ++i) {
struct sim_client *client = &ctx->world->clients[i];
if (client->valid) {
@ -560,9 +562,13 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
struct sim_control *control = &client->control;
client->dbg_drag_start = false;
client->dbg_drag_stop = false;
for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
if (cmd->ack_tick > client->ack_tick) {
client->ack_tick = cmd->ack_tick;
}
enum sim_cmd_kind kind = cmd->kind;
b32 start = cmd->state == SIM_CMD_STATE_START;
b32 stop = cmd->state == SIM_CMD_STATE_STOP;
@ -620,7 +626,10 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
case SIM_CMD_KIND_SPAWN_TEST:
{
logf_info("Spawning (test)");
spawn_test_entities(ctx);
u32 count = 1000;
for (u32 j = 0; j < count; ++j) {
spawn_test_entities(ctx, V2(0, (((f32)j / (f32)count) - 0.5) * 2000));
}
} break;
/* Disconnect client */
@ -633,6 +642,10 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
default: break;
};
}
if (client->ack_tick < ctx->oldest_client_ack_tick || ctx->oldest_client_ack_tick == 0) {
ctx->oldest_client_ack_tick = client->ack_tick;
}
}
}
@ -667,9 +680,9 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
if (!sim_ent_is_valid_and_active(ent)) continue;
if (sprite_tag_is_nil(ent->sprite)) continue;
/* Update animation */
struct sprite_sheet *sheet = sprite_sheet_from_tag_await(sprite_frame_scope, ent->sprite);
/* Update animation */
{
struct sprite_sheet_span span = sprite_sheet_get_span(sheet, ent->sprite_span_name);
@ -1340,6 +1353,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
* Publish tick
* ========================== */
#if 0
for (u64 i = 0; i < ctx->world->num_clients_reserved; ++i) {
struct sim_client *client = &ctx->world->clients[i];
if (client->valid) {
@ -1349,7 +1363,7 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
struct sim_event snapshot_event = ZI;
snapshot_event.tick = ctx->world->tick;
snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT;
snapshot_event.snapshot_data = sim_encode_snapshot(temp.arena, client, ctx->world);
snapshot_event.encoded_snapshot = sim_snapshot_encode(temp.arena, client, ctx->world);
struct sim_event_list l = ZI;
l.first = &snapshot_event;
@ -1362,6 +1376,45 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
arena_temp_end(temp);
}
}
#else
for (u64 i = 0; i < ctx->world->num_clients_reserved; ++i) {
struct sim_client *client = &ctx->world->clients[i];
if (client->valid) {
struct temp_arena temp = arena_temp_begin(scratch.arena);
u64 ss0_tick = client->ack_tick;
u64 ss1_tick = ctx->world->tick;
struct sim_snapshot *ss0 = sim_snapshot_from_tick(ctx->snapshot_store, ss0_tick);
struct sim_snapshot *ss1 = ctx->world;
ss0_tick = ss0->tick; /* In case ack tick is no longer in store we need to do a full resend */
struct sim_event snapshot_event = ZI;
snapshot_event.kind = SIM_EVENT_KIND_SNAPSHOT;
snapshot_event.tick = ctx->world->tick;
snapshot_event.snapshot_tick_start = ss0_tick;
snapshot_event.snapshot_tick_end = ss1_tick;
struct sim_encoder encoder = ZI;
encoder.client = client;
/* FIXME: Don't store external arena in bw. Could end up with hidden scratch conflicts. */
encoder.bw = bw_from_arena(temp.arena);
sim_snapshot_encode(&encoder, ss0, ss1);
snapshot_event.snapshot_encoded = bw_get_written(&encoder.bw);
struct sim_event_list l = ZI;
l.first = &snapshot_event;
l.last = &snapshot_event;
struct string msg = sim_string_from_events(temp.arena, l);
host_queue_write(ctx->host, client->channel_id, msg, 0);
//host_queue_write(ctx->host, HOST_CHANNEL_ID_ALL, msg, 0);
arena_temp_end(temp);
}
}
#endif
host_update(ctx->host);
__profframe("Sim");
@ -1374,3 +1427,214 @@ void sim_update(struct sim_ctx *ctx, i64 target_dt_ns)
scratch_end(scratch);
}
/* ========================== *
* Sim cmd
* ========================== */
struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds, u64 ack_tick)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
u64 start = bw_pos(&bw);
bw_write_i8(&bw, cmd->kind);
bw_write_i8(&bw, cmd->state);
bw_write_var_uint(&bw, ack_tick);
#if COLLIDER_DEBUG
bw_write_u32(&bw, cmd->collider_gjk_steps);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
{
bw_write_v2(&bw, cmd->move_dir);
bw_write_v2(&bw, cmd->aim_dir);
} break;
case SIM_CMD_KIND_CURSOR_MOVE:
{
bw_write_v2(&bw, cmd->cursor_pos);
} break;
default: break;
}
u64 size = bw_pos(&bw) - start;
bw_write_u64(&bw_size, size);
}
return bw_get_written(&bw);
}
void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_CONNECT;
cmd->channel_id = host_event.channel_id;
if (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_DISCONNECT;
cmd->disconnect_reason = LIT("Connection lost");
if (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
} break;
case HOST_EVENT_KIND_MSG:
{
struct byte_reader br = br_from_buffer(host_event.msg);
while (br_bytes_left(&br) > 0) {
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
u64 cmd_size = br_read_u64(&br);
u64 cmd_pos_end = br_pos(&br) + cmd_size;
cmd->kind = br_read_i8(&br);
cmd->channel_id = host_event.channel_id;
cmd->state = br_read_i8(&br);
cmd->ack_tick = br_read_var_uint(&br);
#if COLLIDER_DEBUG
cmd->collider_gjk_steps = br_read_u32(&br);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
{
cmd->move_dir = br_read_v2(&br);
cmd->aim_dir = br_read_v2(&br);
} break;
case SIM_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 (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
}
} break;
default: break;
}
}
}
/* ========================== *
* Sim event
* ========================== */
struct string sim_string_from_events(struct arena *arena, struct sim_event_list events)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
for (struct sim_event *event = events.first; event; event = event->next) {
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
u64 start = bw_pos(&bw);
bw_write_var_uint(&bw, event->tick);
bw_write_i8(&bw, event->kind);
switch (event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
bw_write_var_uint(&bw, event->snapshot_tick_start);
bw_write_var_uint(&bw, event->snapshot_tick_end);
bw_write_string(&bw, event->snapshot_encoded);
} break;
default: break;
}
u64 size = bw_pos(&bw) - start;
bw_write_u64(&bw_size, size);
}
return bw_get_written(&bw);
}
void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct sim_event *sim_event = arena_push_zero(arena, struct sim_event);
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
sim_event->channel_id = host_event.channel_id;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
sim_event->kind = SIM_EVENT_KIND_CONNECT;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
sim_event->kind = SIM_EVENT_KIND_DISCONNECT;
sim_event->disconnect_reason = LIT("Connection lost");
} break;
case HOST_EVENT_KIND_MSG:
{
struct byte_reader br = br_from_buffer(host_event.msg);
while (br_bytes_left(&br) > 0) {
u64 event_size = br_read_u64(&br);
u64 event_pos_end = br_pos(&br) + event_size;
sim_event->tick = br_read_var_uint(&br);
sim_event->kind = br_read_i8(&br);
switch (sim_event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
sim_event->snapshot_tick_start = br_read_var_uint(&br);
sim_event->snapshot_tick_end = br_read_var_uint(&br);
sim_event->snapshot_encoded = br_read_string(arena, &br);
} break;
default: break;
}
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 = sim_event;
} else {
events_out->first = sim_event;
}
events_out->last = sim_event;
}
}

View File

@ -43,6 +43,7 @@ struct sim_cmd {
enum sim_cmd_kind kind;
enum sim_cmd_state state;
struct host_channel_id channel_id;
u64 ack_tick;
/* SIM_CMD_KIND_PLAYER_CONTROL */
struct v2 move_dir;
@ -90,7 +91,9 @@ struct sim_event {
struct string disconnect_reason;
/* SIM_EVENT_KIND_SNAPSHOT */
struct string snapshot_data;
u64 snapshot_tick_start;
u64 snapshot_tick_end;
struct string snapshot_encoded; /* Delta encoded snapshot containing changes between start & end ticks */
struct sim_event *next;
};
@ -130,6 +133,8 @@ struct sim_ctx {
/* TODO: Store in snapshot for determinism */
u64 last_phys_iteration;
/* This is the oldest tick stored in ctx that we need to hold a reference to for delta encoding */
u64 oldest_client_ack_tick;
/* Bookkeeping structures */
/* TODO: Store in snapshot for determinism */
@ -155,5 +160,30 @@ void sim_ctx_release(struct sim_ctx *ctx);
void sim_update(struct sim_ctx *ctx, i64 target_dt_ns);
/* ========================== *
* Event & cmd encode/decode
* ========================== */
/* TODO: Move this */
#include "byteio.h"
#include "host.h"
struct sim_encoder {
struct byte_writer bw;
struct sim_client *client;
};
struct sim_decoder {
struct byte_reader br;
};
struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds, u64 ack_tick);
void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out);
struct string sim_string_from_events(struct arena *arena, struct sim_event_list events);
void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out);
#endif

View File

@ -1,4 +1,5 @@
#include "sim_client.h"
#include "sim.h"
#include "sim_snapshot.h"
#include "host.h"
#include "arena.h"
@ -118,3 +119,40 @@ void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_cli
c->control.focus = v2_lerp(c0->control.focus, c1->control.focus, blend);
}
}
/* ========================== *
* Encode
* ========================== */
void sim_client_encode(struct sim_encoder *enc, struct sim_client *c0, struct sim_client *c1)
{
struct byte_writer *bw = &enc->bw;
struct sim_snapshot *ss = c1->ss;
c1->ss = c0->ss;
if (MEMEQ_STRUCT(c0, c1)) {
bw_write_u8(bw, 0);
} else {
bw_write_u8(bw, 1);
struct string bytes = STRING_FROM_STRUCT(c1);
bw_write_var_uint(bw, bytes.len);
bw_write_bytes(bw, bytes);
}
c1->ss = ss;
}
/* ========================== *
* Decode
* ========================== */
void sim_client_decode(struct sim_decoder *dec, struct sim_client *c)
{
struct byte_reader *br = &dec->br;
struct sim_snapshot *ss = c->ss;
if (br_read_u8(br)) {
u64 size = br_read_var_uint(br);
*c = *(struct sim_client *)br_seek(br, size);
c->ss = ss;
}
}

View File

@ -25,6 +25,9 @@ struct sim_client {
struct sim_client_handle next_in_bucket;
struct sim_client_handle prev_in_bucket;
/* This is the last tick we know that this client has received */
u64 ack_tick;
struct v2 cursor_pos;
struct sim_ent_handle camera_ent;
struct sim_ent_handle control_ent;
@ -49,4 +52,7 @@ void sim_client_release(struct sim_client *client);
void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend);
void sim_client_encode(struct sim_encoder *enc, struct sim_client *c0, struct sim_client *c1);
void sim_client_decode(struct sim_decoder *dec, struct sim_client *c);
#endif

View File

@ -1,636 +0,0 @@
#include "sim_encode.h"
#include "sim_snapshot.h"
#include "arena.h"
#include "byteio.h"
/* ========================== *
* Sim cmd
* ========================== */
struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
u64 start = bw_pos(&bw);
bw_write_i8(&bw, cmd->kind);
bw_write_i8(&bw, cmd->state);
#if COLLIDER_DEBUG
bw_write_u32(&bw, cmd->collider_gjk_steps);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
{
bw_write_v2(&bw, cmd->move_dir);
bw_write_v2(&bw, cmd->aim_dir);
} break;
case SIM_CMD_KIND_CURSOR_MOVE:
{
bw_write_v2(&bw, cmd->cursor_pos);
} break;
default: break;
}
u64 size = bw_pos(&bw) - start;
bw_write_u64(&bw_size, size);
}
return bw_get_written(&bw);
}
void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_CONNECT;
cmd->channel_id = host_event.channel_id;
if (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_DISCONNECT;
cmd->disconnect_reason = LIT("Connection lost");
if (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
} break;
case HOST_EVENT_KIND_MSG:
{
struct byte_reader br = br_from_buffer(host_event.msg);
while (br_bytes_left(&br) > 0) {
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
u64 cmd_size = br_read_u64(&br);
u64 cmd_pos_end = br_pos(&br) + cmd_size;
cmd->kind = br_read_i8(&br);
cmd->channel_id = host_event.channel_id;
cmd->state = br_read_i8(&br);
#if COLLIDER_DEBUG
cmd->collider_gjk_steps = br_read_u32(&br);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
{
cmd->move_dir = br_read_v2(&br);
cmd->aim_dir = br_read_v2(&br);
} break;
case SIM_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 (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
}
} break;
default: break;
}
}
}
/* ========================== *
* Sim event
* ========================== */
struct string sim_string_from_events(struct arena *arena, struct sim_event_list events)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
for (struct sim_event *event = events.first; event; event = event->next) {
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
u64 start = bw_pos(&bw);
bw_write_var_uint(&bw, event->tick);
bw_write_i8(&bw, event->kind);
switch (event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
bw_write_string(&bw, event->snapshot_data);
} break;
default: break;
}
u64 size = bw_pos(&bw) - start;
bw_write_u64(&bw_size, size);
}
return bw_get_written(&bw);
}
void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct sim_event *sim_event = arena_push_zero(arena, struct sim_event);
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
sim_event->channel_id = host_event.channel_id;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
sim_event->kind = SIM_EVENT_KIND_CONNECT;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
sim_event->kind = SIM_EVENT_KIND_DISCONNECT;
sim_event->disconnect_reason = LIT("Connection lost");
} break;
case HOST_EVENT_KIND_MSG:
{
struct byte_reader br = br_from_buffer(host_event.msg);
while (br_bytes_left(&br) > 0) {
u64 event_size = br_read_u64(&br);
u64 event_pos_end = br_pos(&br) + event_size;
sim_event->tick = br_read_var_uint(&br);
sim_event->kind = br_read_i8(&br);
switch (sim_event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
sim_event->snapshot_data = br_read_string(arena, &br);
} break;
default: break;
}
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 = sim_event;
} else {
events_out->first = sim_event;
}
events_out->last = sim_event;
}
}
/* ========================== *
* Snapshot
* ========================== */
struct string sim_encode_snapshot(struct arena *arena, struct sim_client *client, struct sim_snapshot *snapshot)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
bw_write_var_uint(&bw, snapshot->continuity_gen);
bw_write_var_sint(&bw, snapshot->real_dt_ns);
bw_write_var_sint(&bw, snapshot->real_time_ns);
bw_write_f64(&bw, snapshot->world_timescale);
bw_write_var_sint(&bw, snapshot->world_dt_ns);
bw_write_var_sint(&bw, snapshot->world_time_ns);
bw_write_var_uint(&bw, client->handle.gen);
bw_write_var_uint(&bw, client->handle.idx);
/* Client s*/
u64 num_clients = snapshot->num_clients_reserved;
bw_write_var_uint(&bw, num_clients);
struct string clients_src = ZI;
clients_src.text = (u8 *)snapshot->clients;
clients_src.len = sizeof(struct sim_client) * num_clients;
br_write_bytes(&bw, clients_src);
/* Entity store */
u64 num_entities = snapshot->num_ents_reserved;
bw_write_var_uint(&bw, num_entities);
struct string entities_src = ZI;
entities_src.text = (u8 *)snapshot->ents;
entities_src.len = sizeof(struct sim_ent) * num_entities;
br_write_bytes(&bw, entities_src);
return bw_get_written(&bw);
}
void sim_decode_snapshot(struct string str, struct sim_snapshot *snapshot)
{
__prof;
struct byte_reader br = br_from_buffer(str);
snapshot->continuity_gen = br_read_var_uint(&br);
snapshot->real_dt_ns = br_read_var_sint(&br);
snapshot->real_time_ns = br_read_var_sint(&br);
snapshot->world_timescale = br_read_f64(&br);
snapshot->world_dt_ns = br_read_var_sint(&br);
snapshot->world_time_ns = br_read_var_sint(&br);
snapshot->local_client.gen = br_read_var_uint(&br);
snapshot->local_client.idx = br_read_var_uint(&br);
/* Client store */
u64 num_clients = br_read_var_uint(&br);
arena_push_array(&snapshot->clients_arena, struct sim_client, num_clients - snapshot->num_clients_reserved);
snapshot->num_clients_reserved = num_clients;
snapshot->num_clients_allocated = 0;
struct sim_client *clients_src = br_seek(&br, num_clients * sizeof(struct sim_client));
if (clients_src) {
for (u64 i = 0; i < num_clients; ++i) {
struct sim_client *src = &clients_src[i];
struct sim_client *dst = &snapshot->clients[i];
if (dst->valid) {
++snapshot->num_clients_allocated;
}
*dst = *src;
}
}
/* Entity store */
u64 num_entities = br_read_var_uint(&br);
arena_push_array(&snapshot->ents_arena, struct sim_ent, num_entities - snapshot->num_ents_reserved);
snapshot->num_ents_reserved = num_entities;
snapshot->num_ents_allocated = 0;
struct sim_ent *entities_src = br_seek(&br, num_entities * sizeof(struct sim_ent));
if (entities_src) {
for (u64 i = 0; i < num_entities; ++i) {
struct sim_ent *src = &entities_src[i];
struct sim_ent *dst = &snapshot->ents[i];
if (dst->valid) {
++snapshot->num_ents_allocated;
}
*dst = *src;
}
}
}
#if 0
#include "sim_encode.h"
#include "sim_snapshot.h"
#include "arena.h"
#include "byteio.h"
/* ========================== *
* Sim cmd
* ========================== */
struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
for (struct sim_cmd *cmd = cmds.first; cmd; cmd = cmd->next) {
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
u64 start = bw_pos(&bw);
bw_write_i8(&bw, cmd->kind);
bw_write_i8(&bw, cmd->state);
#if COLLIDER_DEBUG
bw_write_u32(&bw, cmd->collider_gjk_steps);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
{
bw_write_v2(&bw, cmd->move_dir);
bw_write_v2(&bw, cmd->aim_dir);
} break;
case SIM_CMD_KIND_CURSOR_MOVE:
{
bw_write_v2(&bw, cmd->cursor_pos);
} break;
default: break;
}
u64 size = bw_pos(&bw) - start;
bw_write_u64(&bw_size, size);
}
return bw_get_written(&bw);
}
void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_CONNECT;
cmd->channel_id = host_event.channel_id;
if (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
cmd->kind = SIM_CMD_KIND_SIM_CLIENT_DISCONNECT;
cmd->disconnect_reason = LIT("Connection lost");
if (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
} break;
case HOST_EVENT_KIND_MSG:
{
struct byte_reader br = br_from_buffer(host_event.msg);
while (br_bytes_left(&br) > 0) {
struct sim_cmd *cmd = arena_push_zero(arena, struct sim_cmd);
u64 cmd_size = br_read_u64(&br);
u64 cmd_pos_end = br_pos(&br) + cmd_size;
cmd->kind = br_read_i8(&br);
cmd->channel_id = host_event.channel_id;
cmd->state = br_read_i8(&br);
#if COLLIDER_DEBUG
cmd->collider_gjk_steps = br_read_u32(&br);
#endif
switch (cmd->kind) {
case SIM_CMD_KIND_PLAYER_CONTROL:
{
cmd->move_dir = br_read_v2(&br);
cmd->aim_dir = br_read_v2(&br);
} break;
case SIM_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 (cmds_out->last) {
cmds_out->last->next = cmd;
} else {
cmds_out->first = cmd;
}
cmds_out->last = cmd;
}
} break;
default: break;
}
}
}
/* ========================== *
* Sim event
* ========================== */
struct string sim_string_from_events(struct arena *arena, struct sim_event_list events)
{
__prof;
struct byte_writer bw = bw_from_arena(arena);
for (struct sim_event *event = events.first; event; event = event->next) {
struct byte_writer bw_size = bw_branch(&bw, sizeof(u64));
u64 start = bw_pos(&bw);
bw_write_var_uint(&bw, event->tick);
bw_write_i8(&bw, event->kind);
switch (event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
bw_write_string(&bw, event->snapshot_data);
} break;
default: break;
}
u64 size = bw_pos(&bw) - start;
bw_write_u64(&bw_size, size);
}
return bw_get_written(&bw);
}
void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out)
{
__prof;
for (u64 i = 0; i < host_events.count; ++i) {
struct sim_event *sim_event = arena_push_zero(arena, struct sim_event);
struct host_event host_event = host_events.events[i];
enum host_event_kind host_event_kind = host_event.kind;
sim_event->channel_id = host_event.channel_id;
switch (host_event_kind) {
case HOST_EVENT_KIND_CHANNEL_OPENED:
{
sim_event->kind = SIM_EVENT_KIND_CONNECT;
} break;
case HOST_EVENT_KIND_CHANNEL_CLOSED:
{
sim_event->kind = SIM_EVENT_KIND_DISCONNECT;
sim_event->disconnect_reason = LIT("Connection lost");
} break;
case HOST_EVENT_KIND_MSG:
{
struct byte_reader br = br_from_buffer(host_event.msg);
while (br_bytes_left(&br) > 0) {
u64 event_size = br_read_u64(&br);
u64 event_pos_end = br_pos(&br) + event_size;
sim_event->tick = br_read_var_uint(&br);
sim_event->kind = br_read_i8(&br);
switch (sim_event->kind) {
case SIM_EVENT_KIND_SNAPSHOT:
{
sim_event->snapshot_data = br_read_string(arena, &br);
} break;
default: break;
}
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 = sim_event;
} else {
events_out->first = sim_event;
}
events_out->last = sim_event;
}
}
/* ========================== *
* Ent
* ========================== */
void sim_encode_ent(struct sim_encoder *enc, struct sim_ent *ent)
{
(UNUSED)arena;
(UNUSED)client;
(UNUSED)ent;
struct byte_writer bw = bw_from_arena(arena);
return bw_get_written(&bw);
}
struct sim_ent *sim_decode_ent(struct sim_decoder *dec, struct sim_ent_store *store)
{
}
/* ========================== *
* Snapshot
* ========================== */
struct string sim_encode_snapshot(struct sim_encoder *enc, struct sim_snapshot *snapshot)
{
__prof;
struct byte_writer *bw = &enc->bw;
bw_write_var_uint(&bw, snapshot->continuity_gen);
bw_write_var_sint(&bw, snapshot->real_dt_ns);
bw_write_var_sint(&bw, snapshot->real_time_ns);
bw_write_f64(&bw, snapshot->world_timescale);
bw_write_var_sint(&bw, snapshot->world_dt_ns);
bw_write_var_sint(&bw, snapshot->world_time_ns);
bw_write_var_uint(&bw, client->handle.gen);
bw_write_var_uint(&bw, client->handle.idx);
/* Client store */
u64 num_clients = snapshot->client_store->num_reserved;
bw_write_var_uint(&bw, num_clients);
struct string clients_src = ZI;
clients_src.text = (u8 *)snapshot->client_store->clients;
clients_src.len = sizeof(struct sim_client) * num_clients;
br_write_bytes(&bw, clients_src);
/* Ents */
for (u64 i = 0; i < snapshot->num_ents_reserved; ++i) {
bw_write_u8(bw, 1);
struct sim_ent *ent = &snapshot->ents[i];
sim_encode_ent(enc, ent);
}
bw_write_u8(bw, 0);
return bw_get_written(&bw);
}
struct sim_snapshot *sim_decode_snapshot(struct sim_decoder *dec, struct sim_snapshot_store *store)
{
__prof;
struct byte_reader *br = &dec->bw;
snapshot->continuity_gen = br_read_var_uint(&br);
snapshot->real_dt_ns = br_read_var_sint(&br);
snapshot->real_time_ns = br_read_var_sint(&br);
snapshot->world_timescale = br_read_f64(&br);
snapshot->world_dt_ns = br_read_var_sint(&br);
snapshot->world_time_ns = br_read_var_sint(&br);
snapshot->local_client.gen = br_read_var_uint(&br);
snapshot->local_client.idx = br_read_var_uint(&br);
/* Client store */
u64 num_clients = br_read_var_uint(&br);
arena_push_array(&snapshot->client_store->arena, struct sim_client, num_clients - snapshot->client_store->num_reserved);
snapshot->client_store->num_reserved = num_clients;
snapshot->client_store->num_allocated = 0;
struct sim_client *clients_src = br_seek(&br, num_clients * sizeof(struct sim_client));
if (clients_src) {
for (u64 i = 0; i < num_clients; ++i) {
struct sim_client *src = &clients_src[i];
struct sim_client *dst = &snapshot->client_store->clients[i];
if (dst->valid) {
++snapshot->client_store->num_allocated;
}
*dst = *src;
}
}
/* Ents */
b32 read_entity = br_read_u8(br);
while (read_entity) {
sim_decode_ent(dec, snapshot);
read_entity = br_read_u8(br);
}f
}
#endif

View File

@ -1,64 +0,0 @@
#ifndef SIM_ENCODE_H
#define SIM_ENCODE_H
#include "host.h"
#include "sim.h"
struct sim_snapshot;
struct sim_client;
struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds);
void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out);
struct string sim_string_from_events(struct arena *arena, struct sim_event_list events);
void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out);
/* ========================== *
* Snapshot
* ========================== */
struct string sim_encode_snapshot(struct arena *arena, struct sim_client *client, struct sim_snapshot *snapshot);
void sim_decode_snapshot(struct string str, struct sim_snapshot *snapshot);
#endif
#if 0
#ifndef SIM_ENCODE_H
#define SIM_ENCODE_H
#include "sim.h"
#include "host.h"
struct sim_encoder {
struct byte_writer bw;
struct sim_client *client;
};
struct sim_decoder {
struct byte_reader bw;
};
struct string sim_string_from_cmds(struct arena *arena, struct sim_cmd_list cmds);
void sim_cmds_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_cmd_list *cmds_out);
struct string sim_string_from_events(struct arena *arena, struct sim_event_list events);
void sim_events_from_host_events(struct arena *arena, struct host_event_array host_events, struct sim_event_list *events_out);
struct string sim_encode_snapshot(struct arena *arena, struct sim_client *client, struct sim_snapshot *snapshot);
void sim_decode_snapshot(struct string str, struct sim_snapshot *snapshot);
#endif
#endif

View File

@ -1,4 +1,5 @@
#include "sim_ent.h"
#include "sim.h"
#include "sim_snapshot.h"
#include "math.h"
@ -473,3 +474,41 @@ void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64
e->tracer_gradient_end = v2_lerp(e0->tracer_gradient_end, e1->tracer_gradient_end, blend);
}
}
/* ========================== *
* Encode
* ========================== */
void sim_ent_encode(struct sim_encoder *enc, struct sim_ent *e0, struct sim_ent *e1)
{
struct byte_writer *bw = &enc->bw;
struct sim_snapshot *ss = e1->ss;
e1->ss = e0->ss;
if (MEMEQ_STRUCT(e0, e1)) {
bw_write_u8(bw, 0);
} else {
bw_write_u8(bw, 1);
struct string bytes = STRING_FROM_STRUCT(e1);
bw_write_var_uint(bw, bytes.len);
bw_write_bytes(bw, bytes);
}
e1->ss = ss;
}
/* ========================== *
* Decode
* ========================== */
void sim_ent_decode(struct sim_decoder *dec, struct sim_ent *e)
{
struct byte_reader *br = &dec->br;
struct sim_snapshot *ss = e->ss;
if (br_read_u8(br)) {
u64 size = br_read_var_uint(br);
*e = *(struct sim_ent *)br_seek(br, size);
e->ss = ss;
}
}

View File

@ -8,6 +8,9 @@
#define SIM_ENT_NIL_HANDLE ((struct sim_ent_handle){ .gen = 0, .idx = 0 })
#define SIM_ENT_ROOT_HANDLE ((struct sim_ent_handle){ .gen = 1, .idx = 0 })
struct sim_encoder;
struct sim_decoder;
enum sim_ent_prop {
SIM_ENT_PROP_NONE,
@ -402,4 +405,8 @@ void sim_ent_activate(struct sim_ent *ent, u64 current_tick);
/* Lerp */
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend);
/* Encode / decode */
void sim_ent_encode(struct sim_encoder *enc, struct sim_ent *e0, struct sim_ent *e1);
void sim_ent_decode(struct sim_decoder *dec, struct sim_ent *e);
#endif

View File

@ -1,10 +1,11 @@
#include "sim_snapshot.h"
#include "sim.h"
#include "sim_ent.h"
#include "sim_client.h"
#include "arena.h"
#include "scratch.h"
#define TICK_LOOKUP_BUCKETS 31
#define TICK_LOOKUP_BUCKETS 127
#define CLIENT_LOOKUP_BUCKETS 127
struct sim_snapshot_lookup_bucket {
@ -94,15 +95,19 @@ struct sim_snapshot_store *sim_snapshot_store_alloc(void)
void sim_snapshot_store_release(struct sim_snapshot_store *store)
{
/* Release snapshot internal memory */
struct sim_snapshot *ss = sim_snapshot_from_tick(store, store->first_tick);
while (ss->valid) {
struct sim_snapshot *next = sim_snapshot_from_tick(store, ss->next_tick);
for (u64 i = 0; i < store->num_lookup_buckets; ++i) {
struct sim_snapshot_lookup_bucket *bucket = &store->lookup_buckets[i];
struct sim_snapshot *ss = bucket->first;
while (ss) {
struct sim_snapshot *next = ss->next_in_bucket;
arena_release(&ss->clients_arena);
arena_release(&ss->ents_arena);
arena_release(&ss->arena);
ss = next;
}
ss = store->first_free_snapshot;
}
{
struct sim_snapshot *ss = store->first_free_snapshot;
while (ss) {
struct sim_snapshot *next = ss->next_free;
arena_release(&ss->clients_arena);
@ -110,6 +115,7 @@ void sim_snapshot_store_release(struct sim_snapshot_store *store)
arena_release(&ss->arena);
ss = next;
}
}
/* Release store */
arena_release(&store->arena);
}
@ -140,9 +146,9 @@ struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct
arena = ss->arena;
} else {
/* Arenas allocated here will be released along with the entire snasphot store */
arena = arena_alloc(GIGABYTE(64));
clients_arena = arena_alloc(GIGABYTE(64));
ents_arena = arena_alloc(GIGABYTE(64));
arena = arena_alloc(GIGABYTE(8));
clients_arena = arena_alloc(GIGABYTE(8));
ents_arena = arena_alloc(GIGABYTE(8));
}
}
arena_reset(&arena);
@ -401,3 +407,136 @@ struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *sto
return ss;
}
/* ========================== *
* Encode
* ========================== */
void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, struct sim_snapshot *ss1)
{
__prof;
struct byte_writer *bw = &enc->bw;
struct sim_client *client = enc->client;
bw_write_var_uint(bw, ss1->continuity_gen);
bw_write_var_sint(bw, ss1->real_dt_ns);
bw_write_var_sint(bw, ss1->real_time_ns);
bw_write_f64(bw, ss1->world_timescale);
bw_write_var_sint(bw, ss1->world_dt_ns);
bw_write_var_sint(bw, ss1->world_time_ns);
bw_write_var_uint(bw, client->handle.gen);
bw_write_var_uint(bw, client->handle.idx);
/* Clients */
if (ss1->num_clients_allocated == ss0->num_clients_allocated) {
bw_write_u8(bw, 0);
} else {
bw_write_u8(bw, 1);
bw_write_var_uint(bw, ss1->num_clients_allocated);
}
if (ss1->num_clients_reserved == ss0->num_clients_reserved) {
bw_write_u8(bw, 0);
} else {
bw_write_u8(bw, 1);
bw_write_var_uint(bw, ss1->num_clients_reserved);
}
for (u64 i = 0; i < ss1->num_clients_reserved; ++i) {
struct sim_client *c0 = sim_client_nil();
if (i < ss0->num_clients_reserved) {
c0 = &ss0->clients[i];
}
struct sim_client *c1 = &ss1->clients[i];
sim_client_encode(enc, c0, c1);
}
/* Ents */
if (ss1->num_ents_allocated == ss0->num_ents_allocated) {
bw_write_u8(bw, 0);
} else {
bw_write_u8(bw, 1);
bw_write_var_uint(bw, ss1->num_ents_allocated);
}
if (ss1->num_ents_reserved == ss0->num_ents_reserved) {
bw_write_u8(bw, 0);
} else {
bw_write_u8(bw, 1);
bw_write_var_uint(bw, ss1->num_ents_reserved);
}
for (u64 i = 0; i < ss1->num_ents_reserved; ++i) {
struct sim_ent *e0 = sim_ent_nil();
if (i < ss0->num_ents_reserved) {
e0 = &ss0->ents[i];
}
struct sim_ent *e1 = &ss1->ents[i];
sim_ent_encode(enc, e0, e1);
}
}
/* ========================== *
* Decode
* ========================== */
void sim_snapshot_decode(struct sim_decoder *dec, struct sim_snapshot *ss)
{
__prof;
struct byte_reader *br = &dec->br;
ss->continuity_gen = br_read_var_uint(br);
ss->real_dt_ns = br_read_var_sint(br);
ss->real_time_ns = br_read_var_sint(br);
ss->world_timescale = br_read_f64(br);
ss->world_dt_ns = br_read_var_sint(br);
ss->world_time_ns = br_read_var_sint(br);
ss->local_client.gen = br_read_var_uint(br);
ss->local_client.idx = br_read_var_uint(br);
/* Clients */
if (br_read_u8(br)) {
ss->num_clients_allocated = br_read_var_uint(br);
}
if (br_read_u8(br)) {
u64 old_num_clients_reserved = ss->num_clients_reserved;
ss->num_clients_reserved = br_read_var_uint(br);
i64 reserve_diff = (i64)ss->num_clients_reserved - (i64)old_num_clients_reserved;
if (reserve_diff > 0) {
arena_push_array(&ss->clients_arena, struct sim_client, reserve_diff);
for (u64 i = old_num_clients_reserved; i < ss->num_clients_reserved; ++i) {
struct sim_client *c = &ss->clients[i];
*c = *sim_client_nil();
c->ss = ss;
}
}
}
for (u64 i = 0; i < ss->num_clients_reserved; ++i) {
struct sim_client *c = &ss->clients[i];
sim_client_decode(dec, c);
}
/* Ents */
if (br_read_u8(br)) {
ss->num_ents_allocated = br_read_var_uint(br);
}
if (br_read_u8(br)) {
u64 old_num_ents_reserved = ss->num_ents_reserved;
ss->num_ents_reserved = br_read_var_uint(br);
i64 reserve_diff = (i64)ss->num_ents_reserved - (i64)old_num_ents_reserved;
if (reserve_diff > 0) {
arena_push_array(&ss->ents_arena, struct sim_ent, reserve_diff);
for (u64 i = old_num_ents_reserved; i < ss->num_ents_reserved; ++i) {
struct sim_ent *e = &ss->ents[i];
*e = *sim_ent_nil();
e->ss = ss;
}
}
}
for (u64 i = 0; i < ss->num_ents_reserved; ++i) {
struct sim_ent *e = &ss->ents[i];
sim_ent_decode(dec, e);
}
}

View File

@ -4,6 +4,9 @@
#include "sim_ent.h"
#include "sim_client.h"
struct sim_encoder;
struct sim_decoder;
struct sim_snapshot_store {
b32 valid;
struct arena arena;
@ -21,8 +24,6 @@ struct sim_snapshot_store {
};
struct sim_snapshot {
/* Managed by store */
struct arena arena;
b32 valid;
u64 tick;
struct sim_snapshot_store *store;
@ -32,6 +33,8 @@ struct sim_snapshot {
u64 prev_tick;
u64 next_tick;
struct arena arena;
/* Real time (increases with clock assuming no lag) */
i64 real_dt_ns;
i64 real_time_ns;
@ -103,11 +106,19 @@ INLINE struct sim_snapshot_store *sim_snapshot_store_nil(void)
* Snapshot
* ========================== */
/* Alloc */
struct sim_snapshot *sim_snapshot_alloc(struct sim_snapshot_store *store, struct sim_snapshot *src, u64 tick);
void sim_snapshot_release(struct sim_snapshot *sim_snapshot);
/* Lookup */
struct sim_snapshot *sim_snapshot_from_tick(struct sim_snapshot_store *store, u64 tick);
/* Lerp */
struct sim_snapshot *sim_snapshot_alloc_from_lerp(struct sim_snapshot_store *store, struct sim_snapshot *ss0, struct sim_snapshot *ss1, f64 blend);
/* Encode / decode */
void sim_snapshot_encode(struct sim_encoder *enc, struct sim_snapshot *ss0, struct sim_snapshot *ss1);
void sim_snapshot_decode(struct sim_decoder *dec, struct sim_snapshot *ss);
#endif

View File

@ -2,7 +2,6 @@
#include "app.h"
#include "sim.h"
#include "sim_ent.h"
#include "sim_encode.h"
#include "sim_snapshot.h"
#include "renderer.h"
#include "font.h"
@ -22,6 +21,7 @@
#include "log.h"
#include "sock.h"
#include "host.h"
#include "byteio.h"
struct bind_state {
b32 is_held; /* Is this bind held down this frame */
@ -422,7 +422,7 @@ INTERNAL void user_update(void)
}
for (struct sim_event *event = sim_events.first; event; event = event->next) {
u64 tick = event->tick;
u64 event_tick = event->tick;
enum sim_event_kind kind = event->kind;
switch (kind) {
@ -438,21 +438,42 @@ INTERNAL void user_update(void)
case SIM_EVENT_KIND_SNAPSHOT:
{
#if 1
struct sim_snapshot *existing = sim_snapshot_from_tick(G.sim_snapshot_store, tick);
if (!existing->valid && tick > G.world->tick) {
u64 delta_src_tick = 0;
struct sim_snapshot *delta_src = sim_snapshot_from_tick(G.sim_snapshot_store, delta_src_tick);
ASSERT(delta_src->tick == delta_src_tick); /* User should always have src tick present */
struct string encoded = event->snapshot_data;
struct sim_snapshot *ss = sim_snapshot_alloc(G.sim_snapshot_store, delta_src, tick);
sim_decode_snapshot(encoded, ss);
ss->received_at_ns = G.real_time_ns;
#if 0
if (event_tick > G.world->tick) {
u64 ss0_tick = event->snapshot_tick_start;
u64 ss1_tick = event->snapshot_tick_end;
struct sim_snapshot *ss0 = sim_snapshot_from_tick(G.sim_snapshot_store, ss0_tick);
struct sim_snapshot *ss1 = sim_snapshot_from_tick(G.sim_snapshot_store, ss1_tick);
if (ss0->tick == ss0_tick) {
if (!ss1->valid) {
ss1 = sim_snapshot_alloc(G.sim_snapshot_store, ss0, ss1_tick);
sim_snapshot_decode(event->snapshot_encoded, ss1);
ss1->received_at_ns = G.real_time_ns;
}
} else {
/* User should always have src tick present */
ASSERT(false);
}
}
#else
struct string encoded = event->snapshot_data;
sim_decode_snapshot(encoded, &G.world, tick);
if (event_tick > G.world->tick) {
u64 ss0_tick = event->snapshot_tick_start;
u64 ss1_tick = event->snapshot_tick_end;
struct sim_snapshot *ss0 = sim_snapshot_from_tick(G.sim_snapshot_store, ss0_tick);
struct sim_snapshot *ss1 = sim_snapshot_from_tick(G.sim_snapshot_store, ss1_tick);
if (ss0->tick == ss0_tick) {
if (!ss1->valid) {
ss1 = sim_snapshot_alloc(G.sim_snapshot_store, ss0, ss1_tick);
ss1->received_at_ns = G.real_time_ns;
struct sim_decoder decoder = ZI;
decoder.br = br_from_buffer(event->snapshot_encoded);
sim_snapshot_decode(&decoder, ss1);
}
} else {
/* User should always have src tick present */
ASSERT(false);
}
}
#endif
} break;
@ -470,6 +491,11 @@ INTERNAL void user_update(void)
G.local_sim_last_known_time_ns = newest_snapshot->real_time_ns;
G.local_sim_last_known_tick = newest_snapshot->tick;
/* This is the ack tick that we know the sim has received our ack of.
* Therefore we must keep it around or else risk the server sending us
* a snapshot delta for a snapshot we've released. */
u64 oldest_possible_delta_base_tick = sim_client_from_handle(newest_snapshot, newest_snapshot->local_client)->ack_tick;
/* Predict local sim time based on last received snapshot time,
* then smooth it out to prevent sudden jumps in rendering due
* to variance in snapshot receive time. */
@ -478,6 +504,7 @@ INTERNAL void user_update(void)
i64 time_since_newest_tick_ns = G.real_time_ns - newest_snapshot->received_at_ns;
G.local_sim_predicted_time_ns = newest_snapshot->real_time_ns + time_since_newest_tick_ns;
G.local_sim_predicted_time_smoothed_ns += G.real_dt_ns;
/* FIXME: Signed overflow check */
G.local_sim_predicted_time_smoothed_ns += (G.local_sim_predicted_time_ns - G.local_sim_predicted_time_smoothed_ns) * sim_time_smoothed_correction_rate;
#if USER_INTERP_ENABLED
@ -487,28 +514,24 @@ INTERNAL void user_update(void)
struct sim_snapshot *left_snapshot = sim_snapshot_nil();
struct sim_snapshot *right_snapshot = newest_snapshot;
{
struct sim_snapshot *snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick);
while (snapshot->valid) {
i64 ss_time_ns = snapshot->real_time_ns;
u64 next_tick = snapshot->next_tick;
struct sim_snapshot *ss = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick);
while (ss->valid) {
u64 next_tick = ss->next_tick;
i64 ss_time_ns = ss->real_time_ns;
if (ss_time_ns < render_time_ns && ss_time_ns > left_snapshot->real_time_ns) {
if (left_snapshot->valid) {
/* Snapshot no longer needed since render time has passed, release it */
if (left_snapshot->valid && left_snapshot->tick < oldest_possible_delta_base_tick) {
/* Snapshot no longer needed since render time has passed & it's older than any delta's we may receive, release it. */
sim_snapshot_release(left_snapshot);
}
left_snapshot = snapshot;
left_snapshot = ss;
}
if (ss_time_ns > render_time_ns && ss_time_ns < right_snapshot->real_time_ns) {
right_snapshot = snapshot;
right_snapshot = ss;
}
snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick);
ss = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick);
}
}
if (G.world->valid) {
sim_snapshot_release(G.world);
}
/* Create world from blended snapshots */
if (left_snapshot->valid && right_snapshot->valid) {
f64 blend = (f64)(render_time_ns - left_snapshot->real_time_ns) / (f64)(right_snapshot->real_time_ns - left_snapshot->real_time_ns);
@ -518,17 +541,30 @@ INTERNAL void user_update(void)
} else if (right_snapshot->valid) {
G.world = sim_snapshot_alloc(G.world_snapshot_store, right_snapshot, right_snapshot->tick);
}
/* Release all other render snapshots */
{
struct sim_snapshot *ss = sim_snapshot_from_tick(G.world_snapshot_store, G.world_snapshot_store->first_tick);
while (ss->valid) {
u64 next_tick = ss->next_tick;
if (ss != G.world) {
sim_snapshot_release(ss);
}
ss = sim_snapshot_from_tick(G.world_snapshot_store, next_tick);
}
}
#else
/* Release sim snapshots all except for newest tick */
{
struct sim_snapshot *snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick);
while (snapshot->valid) {
u64 next_tick = snapshot->next_tick;
if (snapshot->tick != newest_snapshot->tick) {
sim_snapshot_release(snapshot);
struct sim_snapshot *ss = sim_snapshot_from_tick(G.sim_snapshot_store, G.sim_snapshot_store->first_tick);
while (ss->valid) {
u64 next_tick = ss->next_tick;
if (ss->tick != newest_snapshot->tick && ss->tick < oldest_possible_delta_base_tick) {
sim_snapshot_release(ss);
}
snapshot = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick);
ss = sim_snapshot_from_tick(G.sim_snapshot_store, next_tick);
}
}
@ -1491,10 +1527,10 @@ INTERNAL void user_update(void)
if (font) {
struct temp_arena temp = arena_temp_begin(scratch.arena);
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 2)));
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Read from local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_read.last_second * 8 / 1000 / 1000, 3)));
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Sent to local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 2)));
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Sent to local sim: %F mbit/s"), FMT_FLOAT_P((f64)G.client_bytes_sent.last_second * 8 / 1000 / 1000, 3)));
pos.y += spacing;
pos.y += spacing;
@ -1521,10 +1557,10 @@ INTERNAL void user_update(void)
pos.y += spacing;
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 2)));
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Memory usage: %F MiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_committed) / 1024 / 1024, 3)));
pos.y += spacing;
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Virtual memory usage: %F TiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_reserved) / 1024 / 1024 / 1024 / 1024, 2)));
draw_text(G.ui_cmd_buffer, font, pos, string_format(temp.arena, LIT("Virtual memory usage: %F TiB"), FMT_FLOAT_P((f64)atomic_u64_eval(&app_statistics()->memory_reserved) / 1024 / 1024 / 1024 / 1024, 3)));
pos.y += spacing;
pos.y += spacing;
@ -1589,7 +1625,7 @@ INTERNAL void user_update(void)
{
struct temp_arena temp = arena_temp_begin(scratch.arena);
struct string cmds_str = sim_string_from_cmds(temp.arena, cmd_list);
struct string cmds_str = sim_string_from_cmds(temp.arena, cmd_list, G.local_sim_last_known_tick);
host_queue_write(G.host, HOST_CHANNEL_ID_ALL, cmds_str, 0);
arena_temp_end(temp);