diff --git a/src/byteio.c b/src/byteio.c index 673d112c..3cbff301 100644 --- a/src/byteio.c +++ b/src/byteio.c @@ -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); } /* ========================== * diff --git a/src/byteio.h b/src/byteio.h index 7cc45f5c..48a521bf 100644 --- a/src/byteio.h +++ b/src/byteio.h @@ -25,7 +25,7 @@ struct byte_writer bw_branch(struct byte_writer *bw, u64 size); void bw_seek(struct byte_writer *bw, u64 amount); void bw_seek_to(struct byte_writer *bw, u64 pos); -void 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); diff --git a/src/host.c b/src/host.c index 6db45c2b..2d166cc1 100644 --- a/src/host.c +++ b/src/host.c @@ -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; diff --git a/src/sim.c b/src/sim.c index 9f809f56..6f151b6b 100644 --- a/src/sim.c +++ b/src/sim.c @@ -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; + } +} diff --git a/src/sim.h b/src/sim.h index dfbd39ae..290ef547 100644 --- a/src/sim.h +++ b/src/sim.h @@ -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 diff --git a/src/sim_client.c b/src/sim_client.c index 5394aa49..d978f587 100644 --- a/src/sim_client.c +++ b/src/sim_client.c @@ -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; + } +} diff --git a/src/sim_client.h b/src/sim_client.h index 40fa6af7..f2dfa84c 100644 --- a/src/sim_client.h +++ b/src/sim_client.h @@ -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 diff --git a/src/sim_encode.c b/src/sim_encode.c deleted file mode 100644 index 46a6c789..00000000 --- a/src/sim_encode.c +++ /dev/null @@ -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 diff --git a/src/sim_encode.h b/src/sim_encode.h deleted file mode 100644 index 42e5f175..00000000 --- a/src/sim_encode.h +++ /dev/null @@ -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 diff --git a/src/sim_ent.c b/src/sim_ent.c index 7e36f73d..2c59923f 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -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; + } +} diff --git a/src/sim_ent.h b/src/sim_ent.h index ff925ff7..7d7fb7bc 100644 --- a/src/sim_ent.h +++ b/src/sim_ent.h @@ -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 diff --git a/src/sim_snapshot.c b/src/sim_snapshot.c index d3bac0ba..02074274 100644 --- a/src/sim_snapshot.c +++ b/src/sim_snapshot.c @@ -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,21 +95,26 @@ 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); - arena_release(&ss->clients_arena); - arena_release(&ss->ents_arena); - arena_release(&ss->arena); - ss = next; + 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; - while (ss) { - struct sim_snapshot *next = ss->next_free; - arena_release(&ss->clients_arena); - arena_release(&ss->ents_arena); - arena_release(&ss->arena); - ss = next; + { + struct sim_snapshot *ss = store->first_free_snapshot; + while (ss) { + struct sim_snapshot *next = ss->next_free; + arena_release(&ss->clients_arena); + arena_release(&ss->ents_arena); + 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); + } +} diff --git a/src/sim_snapshot.h b/src/sim_snapshot.h index 5fbfe1ea..1af9b565 100644 --- a/src/sim_snapshot.h +++ b/src/sim_snapshot.h @@ -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 diff --git a/src/user.c b/src/user.c index 934d7561..4fc52beb 100644 --- a/src/user.c +++ b/src/user.c @@ -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);