diff --git a/.natvis b/.natvis index 4de6c0ef..028336fd 100644 --- a/.natvis +++ b/.natvis @@ -57,9 +57,11 @@ - ~~~MISMATCH~~~ {id} - ~~~MISMATCH~~~ {id} - {id} + ~~~MISMATCH~~~ {id} <{this - this->ss->ents}> + ~~~MISMATCH~~~ {id} <{this - this->ss->ents}> + {id} + {id} <{this - this->ss->ents}> + diff --git a/src/config.h b/src/config.h index 4e23dacb..60a75aee 100644 --- a/src/config.h +++ b/src/config.h @@ -33,7 +33,7 @@ #define SPACE_CELL_BUCKETS_SQRT (256) #define SPACE_CELL_SIZE 1.0f -#define SIM_TICKS_PER_SECOND 100 +#define SIM_TICKS_PER_SECOND 50 #define SIM_TIMESCALE 1 #define SIM_PHYSICS_SUBSTEPS 4 diff --git a/src/sim.c b/src/sim.c index 2fdbd32a..55401c5d 100644 --- a/src/sim.c +++ b/src/sim.c @@ -622,6 +622,20 @@ void sim_snapshot_sync(struct sim_snapshot *local_ss, struct sim_snapshot *remot sim_ent_release_all_with_prop(local_ss, SIM_ENT_PROP_RELEASE); } + + + + + + + + + + + +#if 1 + + /* ========================== * * Snapshot encode * ========================== */ @@ -638,6 +652,38 @@ void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, bw_write_uid(bw, receiver->ent_id.uid); + /* Id buckets */ + /* TODO: Don't encode these */ + for (u64 i = 0; i < ss1->num_id_buckets; ++i) { + u32 old_first = 0; + u32 old_last = 0; + if (i < ss0->num_id_buckets) { + struct sim_ent_bucket *old_bucket = &ss0->id_buckets[i]; + old_first = old_bucket->first; + old_last = old_bucket->last; + } + struct sim_ent_bucket *bucket = &ss1->id_buckets[i]; + u32 new_first = bucket->first; + u32 new_last = bucket->last; + if (new_first != old_first || new_last != old_last) { + bw_write_bit(bw, 1); + bw_write_uv(bw, i); + if (old_first == bucket->first) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + bw_write_uv(bw, bucket->first); + } + if (old_last == bucket->last) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + bw_write_uv(bw, bucket->last); + } + } + } + bw_write_bit(bw, 0); + /* Ents */ if (ss1->num_ents_allocated == ss0->num_ents_allocated) { bw_write_bit(bw, 0); @@ -677,6 +723,29 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) ss->local_client_ent = (struct sim_ent_id) { .uid = br_read_uid(br) }; + /* Id buckets */ + /* TODO: Don't decode these, determine them implicitly from decoded ents */ + { + b32 bucket_changed = br_read_bit(br); + while (bucket_changed) { + u32 bucket_index = br_read_uv(br); + if (bucket_index < ss->num_id_buckets) { + struct sim_ent_bucket *bucket = &ss->id_buckets[bucket_index]; + if (br_read_bit(br)) { + bucket->first = br_read_uv(br); + } + if (br_read_bit(br)) { + bucket->last = br_read_uv(br); + } + } else { + /* Invalid bucket index */ + ASSERT(false); + } + + bucket_changed = br_read_bit(br); + } + } + /* Ents */ if (br_read_bit(br)) { ss->num_ents_allocated = br_read_uv(br); @@ -700,3 +769,244 @@ void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) sim_ent_decode(br, e); } } + + +#else + +/* ========================== * + * Snapshot encode + * ========================== */ + +void sim_snapshot_encode(struct bitbuff_writer *bw, struct sim_client *receiver, struct sim_snapshot *ss0, struct sim_snapshot *ss1) +{ + __prof; + + bw_write_iv(bw, ss1->sim_dt_ns); + bw_write_iv(bw, ss1->sim_time_ns); + + bw_write_uv(bw, ss1->continuity_gen); + bw_write_uv(bw, ss1->phys_iteration); + + bw_write_uid(bw, receiver->ent_id.uid); + + /* Ents */ + + + + if (ss1->num_ents_allocated == ss0->num_ents_allocated) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + bw_write_uv(bw, ss1->num_ents_allocated); + } + if (ss1->num_ents_reserved == ss0->num_ents_reserved) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + bw_write_uv(bw, ss1->num_ents_reserved); + } + + + bw_align(bw); + 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]; + } + + if (e0->valid != e1->valid) { + bw_write_bit(1); + bw_write_bit(e1->valid); + if (e1->valid) { + bw_write_uid(bw, e1->id.uid); + } + } else { + bw_write_bit(0); + } + + if (e1->valid) { + struct sim_ent *e1 = &ss1->ents[i]; + sim_ent_encode(bw, e0, e1); + } + } +} + +/* ========================== * + * Snapshot decode + * ========================== */ + +struct sim_ent_decode_node { + struct string tmp_encoded; +}; + +struct sim_ent_decode_queue { + struct sim_ent_decode_node *first; + struct sim_ent_decode_node *last; +}; + +void sim_snapshot_decode(struct bitbuff_reader *br, struct sim_snapshot *ss) +{ + __prof; + struct temp_arena scratch = scratch_begin_no_conflict(); + + ss->sim_dt_ns = br_read_iv(br); + ss->sim_time_ns = br_read_iv(br); + + ss->continuity_gen = br_read_uv(br); + ss->phys_iteration = br_read_uv(br); + + ss->local_client_ent = (struct sim_ent_id) { .uid = br_read_uid(br) }; + +#if 1 + + if (br_read_bit(br)) { + ss->num_ents_allocated = br_read_uv(br); + } + if (br_read_bit(br)) { + u64 old_num_ents_reserved = ss->num_ents_reserved; + ss->num_ents_reserved = br_read_uv(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; + } + } + } + + /* Build decode queue */ + struct sim_ent_decode_queue queue; + b32 should_read_ent = br_read_bit(br); + while (should_read_ent) { + /* TODO: Delta decode index based on last read index */ + u32 index = br_read_uv(br); + b32 allocation_changed = br_read_bit(br); + b32 released = false; + + u32 alloc_parent_index = ZI; + struct sim_ent_id alloc_ent_id = ZI; + if (allocation_changed) { + released = br_read_bit(br); + if (released) { + struct sim_ent *e = sim_ent_from_index(ss, e); + ASSERT(e->valid); /* An entity that we don't have allocated should never have been marked for release */ + if (e->valid) { + sim_ent_enable_prop(e, SIM_ENT_PROP_RELEASE); + } + } else { + alloc_parent_index = br_read_uv(); + alloc_ent_id = sim_ent_id_from_uid(br_read_uid(br)); + } + } + + if (!released) { + u64 num_ent_bits = br_read_uv(br); + struct bitbuff_reader ent_br = br_from_seek_bits(br, num_ent_bits); + if (br_num_bits_left(&ent_br) > 0) { + struct sim_ent_decode_node *n = arena_push_zero(scratch.arena, struct sim_ent_decode_node); + n->is_new = allocation_changed && !released; + n->index = index; + n->alloc_parent_ndex = alloc_parent_index; + n->alloc_ent_id = alloc_ent_id; + n->br = ent_br; + if (queue.last) { + queue.last->next = n; + } else { + queue.first = n; + } + queue.last = n; + } + } + + should_read_ent = br_read_bit(br); + } + + /* Allocate new ents from decode queue */ + for (struct sim_ent_decode_node *n = queue.first; n; n = n->next) { + if (n->is_new) { + u32 index = n->index; + struct sim_ent *parent = sim_ent_from_index(ss, n->alloc_parent_index); + ASSERT(!sim_ent_from_index(ss, index)->valid && !sim_ent_from_id(ss, alloc_ent_id)->valid); /* An entity that we have allocated already should never be marked for allocation */ + ASSERT(parent->valid); /* Parent for new entity allocation should always be valid */ + if (parent->valid && index < ss->num_ents_reserved) { + struct sim_ent *ent = &ss->ents[index]; + ent->valid = true; + sim_ent_set_id(ent, n->alloc_ent_id); + sim_ent_link_parent(parent, ent); + } else { + /* Received an invalid entity allocation */ + ASSERT(false); + } + } + } + + /* Decode ent data from decode queue */ + for (struct sim_ent_decode_node *n = queue.first; n; n = n->next) { + struct bitbuff_reader ent_br = n->br; + u32 index = n->index; + struct sim_ent *e = sim_ent_from_index(ss, index); + if (e->valid) { + sim_ent_decode(&ent_br, e); + } else { + /* Received delta for unallocated ent */ + ASSERT(false); + } + } +#else + /* Ents */ + if (br_read_bit(br)) { + ss->num_ents_allocated = br_read_uv(br); + } + if (br_read_bit(br)) { + u64 old_num_ents_reserved = ss->num_ents_reserved; + ss->num_ents_reserved = br_read_uv(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) { + b32 allocation_changed = br_read_bit(br); + if (allocation_changed) { + if (br_read_bit(br)) { + struct sim_ent_decode_node *n = arena_push_zero(scratch.arena, struct sim_ent_decode_node) + } else { + sim_ent_enable_prop(e, SIM_ENT_PROP_RELEASE); + } + } + } + + for (u64 i = 0; i < ss->num_ents_reserved; ++i) { + struct sim_ent *e = &ss->ents[i]; + e->ss = ss; + + b32 valid_changed = br_read_bit(br); + b32 allocated = true; + if (valid_changed) { + allocated = br_read_bit(br); + } + + if (!allocated) { + /* Why is an already released ent being marked as released? */ + ASSERT(e->valid); + if (e->valid) { + sim_ent_enable_prop(e, SIM_ENT_PROP_RELEASE); + } + } else { + sim_ent_decode(br, e); + } + } + sim_ent_release_all_with_prop(ss, SIM_ENT_PROP_RELEASE); +#endif + + scratch_end(scratch); +} +#endif diff --git a/src/sim_ent.c b/src/sim_ent.c index b935b61a..1f643369 100644 --- a/src/sim_ent.c +++ b/src/sim_ent.c @@ -93,11 +93,11 @@ struct sim_ent *sim_ent_alloc_sync_dst(struct sim_ent *parent, struct sim_client struct sim_ent *e = sim_ent_alloc_raw(ss); sim_ent_set_id(e, id); + sim_ent_link_parent(e, parent); + sim_ent_enable_prop(e, SIM_ENT_PROP_SYNC_DST); e->sync_src_client = client_handle; - sim_ent_link_parent(e, parent); - return e; } @@ -151,7 +151,7 @@ void sim_ent_release_all_with_prop(struct sim_snapshot *ss, enum sim_ent_prop pr * child entities will be released along with parent anyway) */ for (u64 i = 0; i < ents_to_release_count; ++i) { struct sim_ent *ent = ents_to_release[i]; - if (ent->is_top && !ent->is_root) { + if (ent->is_top && !ent->is_root && ent->valid) { sim_ent_release(ent); } } @@ -190,35 +190,35 @@ void sim_ent_set_id(struct sim_ent *ent, struct sim_ent_id id) struct sim_ent_bucket *bucket = bucket_from_id(ss, old_id); u32 prev_index = 0; u32 next_index = 0; - u32 e_index = bucket->first; + u32 search_index = bucket->first; struct sim_ent *prev = sim_ent_nil(); struct sim_ent *next = sim_ent_nil(); - struct sim_ent *e = ent_from_index(ss, e_index); - while (e->valid) { - next_index = e->next_in_id_bucket; + struct sim_ent *search = ent_from_index(ss, search_index); + while (search->valid) { + next_index = search->next_in_id_bucket; next = ent_from_index(ss, next_index); - if (sim_ent_id_eq(e->id, old_id)) { + if (sim_ent_id_eq(search->id, old_id)) { break; } - prev_index = e_index; - prev = e; - e_index = next_index; - e = next; + prev_index = search_index; + prev = search; + search_index = next_index; + search = next; } - if (e->valid) { - if (prev->valid) { - prev->next_in_id_bucket = next_index; - } else { - bucket->first = next_index; - } - if (next->valid) { - next->prev_in_id_bucket = prev_index; - } else { - bucket->last = prev_index; - } + + /* Old id not in bucket, this should be impossible. */ + ASSERT(search->valid); + + if (prev->valid) { + prev->next_in_id_bucket = next_index; } else { - /* Old id not in bucket, this should be impossible. */ - ASSERT(false); + bucket->first = next_index; + } + + if (next->valid) { + next->prev_in_id_bucket = prev_index; + } else { + bucket->last = prev_index; } } @@ -599,6 +599,74 @@ void sim_ent_sync(struct sim_ent *local, struct sim_ent *remote) sim_ent_enable_prop(local, SIM_ENT_PROP_SYNC_DST); } + + + + + + + + + +#if 1 + +/* ========================== * + * Ent encode + * ========================== */ + +void sim_ent_encode(struct bitbuff_writer *bw, struct sim_ent *e0, struct sim_ent *e1) +{ + struct sim_snapshot *ss = e1->ss; + + + /* FIXME: Things like xforms need to be retreived manually rather than memcopied. + * This will also be true for things like ent handles once uids are implemented. */ + + /* TODO: Granular delta encoding */ + + u64 pos = 0; + e1->ss = e0->ss; + while (pos < sizeof(*e1)) { + u64 chunk_size = min_u64(pos + 8, sizeof(*e1)) - pos; + u8 *chunk0 = (u8 *)e0 + pos; + u8 *chunk1 = (u8 *)e1 + pos; + if (MEMEQ(chunk0, chunk1, chunk_size)) { + bw_write_bit(bw, 0); + } else { + bw_write_bit(bw, 1); + u64 bits = 0; + MEMCPY(&bits, chunk1, chunk_size); + bw_write_ubits(bw, bits, 64); + } + pos += 8; + } + e1->ss = ss; +} + +/* ========================== * + * Ent decode + * ========================== */ + +void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e) +{ + struct sim_snapshot *old_ss = e->ss; + { + u64 pos = 0; + while (pos < sizeof(*e)) { + u8 *chunk = (u8 *)e + pos; + if (br_read_bit(br)) { + u64 chunk_size = min_u64(pos + 8, sizeof(*e)) - pos; + u64 bits = br_read_ubits(br, 64); + MEMCPY(chunk, &bits, chunk_size); + } + pos += 8; + } + } + e->ss = old_ss; +} + +#else + /* ========================== * * Ent encode * ========================== */ @@ -653,15 +721,12 @@ void sim_ent_decode(struct bitbuff_reader *br, struct sim_ent *e) } decoded.ss = e->ss; - if (e->valid && !decoded.valid) { - sim_ent_release(e); - } else { - struct sim_ent_id old_id = e->id; - struct sim_ent_id new_id = decoded.id; - MEMCPY_STRUCT(e, &decoded); - e->id = old_id; - if (!sim_ent_id_eq(old_id, new_id)) { - sim_ent_set_id(e, new_id); - } + struct sim_ent_id old_id = e->id; + struct sim_ent_id new_id = decoded.id; + MEMCPY_STRUCT(e, &decoded); + e->id = old_id; + if (!sim_ent_id_eq(old_id, new_id)) { + sim_ent_set_id(e, new_id); } } +#endif diff --git a/src/sim_step.c b/src/sim_step.c index 51c3b5a0..baee61ba 100644 --- a/src/sim_step.c +++ b/src/sim_step.c @@ -59,7 +59,7 @@ INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset) /* Enemy */ { - struct sim_ent *e = sim_ent_alloc_local(root); + struct sim_ent *e = sim_ent_alloc_sync_src(root); struct v2 pos = V2(1, -2); pos = v2_add(pos, offset); @@ -82,7 +82,7 @@ INTERNAL void spawn_test_entities(struct sim_snapshot *world, struct v2 offset) /* Big box */ #if 1 { - struct sim_ent *e = sim_ent_alloc_sync_src(root); + struct sim_ent *e = sim_ent_alloc_local(root); struct v2 pos = V2(1, -0.5); pos = v2_add(pos, offset); diff --git a/src/user.c b/src/user.c index 70be0ba2..2bb88fd3 100644 --- a/src/user.c +++ b/src/user.c @@ -383,19 +383,19 @@ INTERNAL struct string get_ent_debug_text(struct arena *arena, struct sim_ent *e } if (!sim_ent_id_eq(ent->parent, SIM_ENT_ROOT_ID)) { - res.len += string_format(arena, LIT("parent: <%F>\n"), FMT_UID(ent->parent.uid)).len; + res.len += string_format(arena, LIT("parent: [%F]\n"), FMT_UID(ent->parent.uid)).len; } if (!sim_ent_id_eq(ent->next, SIM_ENT_NIL_ID) || !sim_ent_id_eq(ent->prev, SIM_ENT_NIL_ID)) { - res.len += string_format(arena, LIT("prev: <%F>\n"), FMT_UID(ent->prev.uid)).len; - res.len += string_format(arena, LIT("next: <%F>\n"), FMT_UID(ent->next.uid)).len; + res.len += string_format(arena, LIT("prev: [%F]\n"), FMT_UID(ent->prev.uid)).len; + res.len += string_format(arena, LIT("next: [%F]\n"), FMT_UID(ent->next.uid)).len; } if (!sim_ent_id_eq(ent->first, SIM_ENT_NIL_ID) || !sim_ent_id_eq(ent->last, SIM_ENT_NIL_ID)) { struct sim_ent *child = sim_ent_from_id(ss, ent->first); if (!sim_ent_id_eq(ent->first, ent->last) || !child->valid) { - res.len += string_format(arena, LIT("first child: <%F>\n"), FMT_UID(ent->first.uid)).len; - res.len += string_format(arena, LIT("last child: <%F>\n"), FMT_UID(ent->last.uid)).len; + res.len += string_format(arena, LIT("first child: [%F]\n"), FMT_UID(ent->first.uid)).len; + res.len += string_format(arena, LIT("last child: [%F]\n"), FMT_UID(ent->last.uid)).len; } while (child->valid) { res.len += string_copy(arena, LIT("\nCHILD\n")).len; @@ -1836,17 +1836,17 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_thread_entry_point, arg) -struct sim_decode_node { +struct sim_ss_decode_node { struct sim_client *client; u64 tick; u64 base_tick; struct string tmp_encoded; - struct sim_decode_node *next; + struct sim_ss_decode_node *next; }; struct sim_decode_queue { - struct sim_decode_node *first; - struct sim_decode_node *last; + struct sim_ss_decode_node *first; + struct sim_ss_decode_node *last; }; @@ -1959,7 +1959,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) /* Decode incoming slave client snapshots */ b32 should_decode = tick == client->highest_received_tick + 1 || client->highest_received_tick == 0; if (should_decode) { - struct sim_decode_node *node = arena_push_zero(scratch.arena, struct sim_decode_node); + struct sim_ss_decode_node *node = arena_push_zero(scratch.arena, struct sim_ss_decode_node); node->client = client; node->tick = tick; node->base_tick = base_tick; @@ -1978,7 +1978,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) /* Decode incoming master client snapshots (only the newest one) */ b32 should_decode = tick > client->highest_received_tick; if (should_decode) { - struct sim_decode_node *node = queue.first ? queue.first : arena_push_zero(scratch.arena, struct sim_decode_node); + struct sim_ss_decode_node *node = queue.first ? queue.first : arena_push_zero(scratch.arena, struct sim_ss_decode_node); node->client = client; node->tick = tick; node->base_tick = base_tick; @@ -2006,7 +2006,7 @@ INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(user_local_sim_thread_entry_point, arg) } /* Decode incoming snapshots */ - for (struct sim_decode_node *n = queue.first; n; n = n->next) { + for (struct sim_ss_decode_node *n = queue.first; n; n = n->next) { struct sim_client *client = n->client; u64 base_tick = n->base_tick; u64 tick = n->tick;