power_play/src/sim_ent.c

808 lines
25 KiB
C

#include "sim_ent.h"
#include "sim.h"
#include "math.h"
#include "bitbuff.h"
#include "uid.h"
#include "rand.h"
/* Id magic number constants (to be used in conjunction with ent ids in deterministic id combinations) */
#define SIM_ENT_CONTACT_BASIS_UID (UID(0x6a2a5d2dbecf534f, 0x0a8ca7c372a015af))
#define SIM_ENT_COLLISION_DEBUG_BASIS_UID (UID(0x302c01182013bb02, 0x570bd270399d11a5))
#define SIM_ENT_TILE_CHUNK_BASIS_UID (UID(0x3ce42de071dd226b, 0x9b566f7df30c813a))
INTERNAL u32 index_from_ent(struct sim_snapshot *ss, struct sim_ent *ent)
{
return ent - ss->ents;
}
INTERNAL struct sim_ent *ent_from_index(struct sim_snapshot *ss, u32 index)
{
if (index > 0 && index < ss->num_ents_reserved) {
return &ss->ents[index];
} else {
return sim_ent_nil();
}
}
/* ========================== *
* Ent allocation
* ========================== */
struct sim_ent *sim_ent_alloc_raw(struct sim_snapshot *ss, struct sim_ent *parent, struct sim_ent_id id)
{
ASSERT(parent->valid);
ASSERT(ss->valid);
ASSERT(ss == parent->ss);
struct sim_ent *ent;
if (ss->first_free_ent > 0 && ss->first_free_ent < ss->num_ents_reserved) {
/* Reuse from free list */
ent = &ss->ents[ss->first_free_ent];
ss->first_free_ent = ent->next_free;
} else {
/* Make new */
ent = arena_push_no_zero(ss->ents_arena, struct sim_ent);
++ss->num_ents_reserved;
}
*ent = *sim_ent_nil();
ent->ss = ss;
ent->valid = 1;
ent->owner = ss->client->player_id;
ent->_is_xform_dirty = 1;
++ss->num_ents_allocated;
sim_ent_set_id(ent, id);
sim_ent_link_parent(ent, parent);
return ent;
}
/* Allocates a new entity that will not sync */
struct sim_ent *sim_ent_alloc_local(struct sim_ent *parent)
{
struct sim_snapshot *ss = parent->ss;
struct sim_ent *e = sim_ent_alloc_raw(ss, parent, sim_ent_random_id());
e->owner = ss->local_player;
return e;
}
struct sim_ent *sim_ent_alloc_local_with_id(struct sim_ent *parent, struct sim_ent_id id)
{
struct sim_snapshot *ss = parent->ss;
struct sim_ent *e = sim_ent_alloc_raw(ss, parent, id);
e->owner = ss->local_player;
return e;
}
/* Allocates a new entity to be synced to clients */
struct sim_ent *sim_ent_alloc_sync_src(struct sim_ent *parent)
{
struct sim_snapshot *ss = parent->ss;
struct sim_ent *e = sim_ent_alloc_raw(ss, parent, sim_ent_random_id());
sim_ent_enable_prop(e, SEPROP_SYNC_SRC);
e->owner = ss->local_player;
return e;
}
struct sim_ent *sim_ent_alloc_sync_src_with_id(struct sim_ent *parent, struct sim_ent_id id)
{
struct sim_snapshot *ss = parent->ss;
struct sim_ent *e = sim_ent_alloc_raw(ss, parent, id);
sim_ent_enable_prop(e, SEPROP_SYNC_SRC);
e->owner = ss->local_player;
return e;
}
/* Allocates a new entity that will sync with incoming net src ents containing id, and coming from the specified owner */
struct sim_ent *sim_ent_alloc_sync_dst(struct sim_ent *parent, struct sim_ent_id ent_id, struct sim_ent_id owner_id)
{
struct sim_snapshot *ss = parent->ss;
struct sim_ent *e = sim_ent_alloc_raw(ss, parent, ent_id);
sim_ent_enable_prop(e, SEPROP_SYNC_DST);
e->owner = owner_id;
return e;
}
void sim_ent_release_raw(struct sim_ent *ent)
{
struct sim_snapshot *ss = ent->ss;
/* Release children */
struct sim_ent *child = sim_ent_from_id(ss, ent->first);
while (child->valid) {
struct sim_ent *next = sim_ent_from_id(ss, child->next);
sim_ent_release_raw(child);
child = next;
}
/* Release uid */
sim_ent_set_id(ent, SIM_ENT_NIL_ID);
/* Release */
ent->valid = 0;
ent->next_free = ss->first_free_ent;
ss->first_free_ent = index_from_ent(ss, ent);
--ss->num_ents_allocated;
}
void sim_ent_release(struct sim_ent *ent)
{
struct sim_snapshot *ss = ent->ss;
struct sim_ent *parent = sim_ent_from_id(ss, ent->parent);
if (parent->valid) {
sim_ent_unlink_from_parent(ent);
}
sim_ent_release_raw(ent);
}
void sim_ent_release_all_with_prop(struct sim_snapshot *ss, enum sim_ent_prop prop)
{
struct arena_temp scratch = scratch_begin_no_conflict();
struct sim_ent **ents_to_release = arena_push_dry(scratch.arena, struct sim_ent *);
u64 ents_to_release_count = 0;
for (u64 ent_index = 0; ent_index < ss->num_ents_reserved; ++ent_index) {
struct sim_ent *ent = &ss->ents[ent_index];
if (ent->valid && sim_ent_has_prop(ent, prop)) {
*arena_push_no_zero(scratch.arena, struct sim_ent *) = ent;
++ents_to_release_count;
}
}
/* Release from snapshot */
/* TODO: Breadth first iteration to only release parent entities (since
* 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->valid && !ent->is_root && !sim_ent_has_prop(ent, SEPROP_CMD) && !sim_ent_has_prop(ent, SEPROP_PLAYER)) {
sim_ent_release(ent);
}
}
scratch_end(scratch);
}
/* ========================== *
* Activate
* ========================== */
void sim_ent_activate(struct sim_ent *ent, u64 current_tick)
{
sim_ent_enable_prop(ent, SEPROP_ACTIVE);
ent->activation_tick = current_tick;
++ent->continuity_gen;
}
/* ========================== *
* Ent id
* ========================== */
INTERNAL struct sim_ent_bin *bin_from_id(struct sim_snapshot *ss, struct sim_ent_id id)
{
return &ss->id_bins[id.uid.lo % ss->num_id_bins];
}
/* NOTE: This should only really happen during ent allocation (it doesn't make sense for an allocated ent's id to change) */
void sim_ent_set_id(struct sim_ent *ent, struct sim_ent_id id)
{
struct sim_snapshot *ss = ent->ss;
struct sim_ent_id old_id = ent->id;
if (!sim_ent_id_eq(old_id, id)) {
/* Release old from lookup */
if (!sim_ent_id_is_nil(old_id)) {
struct sim_ent_bin *bin = bin_from_id(ss, old_id);
u32 prev_index = 0;
u32 next_index = 0;
u32 search_index = bin->first;
struct sim_ent *prev = sim_ent_nil();
struct sim_ent *next = sim_ent_nil();
struct sim_ent *search = ent_from_index(ss, search_index);
while (search->valid) {
next_index = search->next_in_id_bin;
next = ent_from_index(ss, next_index);
if (sim_ent_id_eq(search->id, old_id)) {
break;
}
prev_index = search_index;
prev = search;
search_index = next_index;
search = next;
}
/* Old id not in bin, this should be impossible. */
ASSERT(search->valid);
if (prev->valid) {
prev->next_in_id_bin = next_index;
} else {
bin->first = next_index;
}
if (next->valid) {
next->prev_in_id_bin = prev_index;
} else {
bin->last = prev_index;
}
}
/* Insert new id into lookup */
if (!sim_ent_id_is_nil(id)) {
#if RTC
{
struct sim_ent *existing = sim_ent_from_id(ss, id);
/* Collision should be extremely unlikely under normal circumstances, there's probably a logic error somewhere. */
ASSERT(!existing->valid);
}
#endif
struct sim_ent_bin *bin = bin_from_id(ss, id);
u32 ent_index = index_from_ent(ss, ent);
struct sim_ent *last = ent_from_index(ss, bin->last);
if (last->valid) {
last->next_in_id_bin = ent_index;
ent->prev_in_id_bin = bin->last;
} else {
bin->first = ent_index;
ent->prev_in_id_bin = 0;
}
bin->last = ent_index;
}
ent->id = id;
}
}
struct sim_ent *sim_ent_from_id(struct sim_snapshot *ss, struct sim_ent_id id)
{
struct sim_ent *res = sim_ent_nil();
if (!sim_ent_id_is_nil(id) && ss->valid) {
struct sim_ent_bin *bin = bin_from_id(ss, id);
for (struct sim_ent *e = ent_from_index(ss, bin->first); e->valid; e = ent_from_index(ss, e->next_in_id_bin)) {
if (sim_ent_id_eq(e->id, id)) {
res = e;
break;
}
}
}
return res;
}
struct sim_ent_id sim_ent_random_id(void)
{
struct sim_ent_id res = ZI;
res.uid = uid_true_rand();
return res;
}
/* Returns the deterministic id of the contact constraint ent id that should be produced from e0 & e1 colliding */
struct sim_ent_id sim_ent_contact_constraint_id_from_contacting_ids(struct sim_ent_id player_id, struct sim_ent_id id0, struct sim_ent_id id1)
{
struct sim_ent_id res = ZI;
res.uid = SIM_ENT_CONTACT_BASIS_UID;
res.uid = uid_combine(res.uid, player_id.uid);
res.uid = uid_combine(res.uid, id0.uid);
res.uid = uid_combine(res.uid, id1.uid);
return res;
}
/* Returns the deterministic id of the debug contact constraint ent id that should be produced from e0 & e1 colliding */
struct sim_ent_id sim_ent_collision_debug_id_from_ids(struct sim_ent_id player_id, struct sim_ent_id id0, struct sim_ent_id id1)
{
struct sim_ent_id res = ZI;
res.uid = SIM_ENT_COLLISION_DEBUG_BASIS_UID;
res.uid = uid_combine(res.uid, player_id.uid);
res.uid = uid_combine(res.uid, id0.uid);
res.uid = uid_combine(res.uid, id1.uid);
return res;
}
/* Returns the deterministic id of the tile chunk that should be produced at chunk pos */
struct sim_ent_id sim_ent_tile_chunk_id_from_tile_chunk_index(struct v2i32 chunk_index)
{
struct sim_ent_id res = ZI;
res.uid = SIM_ENT_TILE_CHUNK_BASIS_UID;
res.uid = uid_combine(res.uid, UID(rand_u64_from_seed(chunk_index.x), rand_u64_from_seed(chunk_index.y)));
return res;
}
/* ========================== *
* Ent query
* ========================== */
struct sim_ent *sim_ent_find_first_match_one(struct sim_snapshot *ss, enum sim_ent_prop prop)
{
u64 count = ss->num_ents_reserved;
struct sim_ent *entities = ss->ents;
for (u64 ent_index = 0; ent_index < count; ++ent_index) {
struct sim_ent *ent = &entities[ent_index];
if (ent->valid && sim_ent_has_prop(ent, prop)) {
return ent;
}
}
return sim_ent_nil();
}
struct sim_ent *sim_ent_find_first_match_all(struct sim_snapshot *ss, struct sim_ent_prop_array props)
{
u64 count = ss->num_ents_reserved;
struct sim_ent *entities = ss->ents;
for (u64 ent_index = 0; ent_index < count; ++ent_index) {
struct sim_ent *ent = &entities[ent_index];
if (ent->valid) {
b32 all = 1;
for (u64 i = 0; i < props.count; ++i) {
if (!sim_ent_has_prop(ent, props.props[i])) {
all = 0;
break;
}
}
if (all) {
return ent;
}
}
}
return sim_ent_nil();
}
/* ========================== *
* Ent tree
* ========================== */
void sim_ent_link_parent(struct sim_ent *ent, struct sim_ent *parent)
{
struct sim_snapshot *ss = ent->ss;
struct sim_ent *old_parent = sim_ent_from_id(ss, ent->parent);
if (old_parent->valid) {
/* Unlink from current parent */
sim_ent_unlink_from_parent(ent);
}
struct sim_ent_id ent_id = ent->id;
struct sim_ent_id last_child_id = parent->last;
struct sim_ent *last_child = sim_ent_from_id(ss, last_child_id);
if (last_child->valid) {
ent->prev = last_child_id;
last_child->next = ent_id;
} else {
parent->first = ent_id;
}
parent->last = ent_id;
if (parent->is_root) {
ent->is_top = 1;
ent->top = ent_id;
} else {
ent->top = parent->top;
}
ent->parent = parent->id;
}
/* NOTE: Entity will be dangling after calling this, should re-link to root ent. */
void sim_ent_unlink_from_parent(struct sim_ent *ent)
{
struct sim_snapshot *ss = ent->ss;
struct sim_ent_id parent_id = ent->parent;
struct sim_ent *parent = sim_ent_from_id(ss, parent_id);
struct sim_ent *prev = sim_ent_from_id(ss, ent->prev);
struct sim_ent *next = sim_ent_from_id(ss, ent->next);
/* Unlink from parent & siblings */
if (prev->valid) {
prev->next = next->id;
} else {
parent->first = next->id;
}
if (next->valid) {
next->prev = prev->id;
} else {
parent->last = prev->id;
}
ent->prev = SIM_ENT_NIL_ID;
ent->next = SIM_ENT_NIL_ID;
}
/* ========================== *
* Ent xform
* ========================== */
INTERNAL void sim_ent_mark_child_xforms_dirty(struct sim_snapshot *ss, struct sim_ent *ent)
{
for (struct sim_ent *child = sim_ent_from_id(ss, ent->first); child->valid; child = sim_ent_from_id(ss, child->next)) {
if (child->_is_xform_dirty) {
break;
} else {
child->_is_xform_dirty = 1;
sim_ent_mark_child_xforms_dirty(ss, child);
}
}
}
INTERNAL struct xform sim_ent_get_xform_internal(struct sim_snapshot *ss, struct sim_ent *ent)
{
struct xform xf;
if (ent->_is_xform_dirty) {
if (ent->is_top) {
xf = ent->_local_xform;
} else {
struct sim_ent *parent = sim_ent_from_id(ss, ent->parent);
xf = sim_ent_get_xform_internal(ss, parent);
xf = xform_mul(xf, ent->_local_xform);
ent->_xform = xf;
ent->_is_xform_dirty = 0;
}
ent->_xform = xf;
ent->_is_xform_dirty = 0;
} else {
xf = ent->_xform;
}
return xf;
}
struct xform sim_ent_get_xform(struct sim_ent *ent)
{
struct xform xf;
if (ent->_is_xform_dirty) {
if (ent->is_top) {
xf = ent->_local_xform;
} else {
struct sim_snapshot *ss = ent->ss;
struct sim_ent *parent = sim_ent_from_id(ss, ent->parent);
xf = sim_ent_get_xform_internal(ss, parent);
xf = xform_mul(xf, ent->_local_xform);
ent->_xform = xf;
ent->_is_xform_dirty = 0;
}
ent->_xform = xf;
ent->_is_xform_dirty = 0;
} else {
xf = ent->_xform;
}
return xf;
}
struct xform sim_ent_get_local_xform(struct sim_ent *ent)
{
return ent->_local_xform;
}
void sim_ent_set_xform(struct sim_ent *ent, struct xform xf)
{
if (!xform_eq(xf, ent->_xform)) {
struct sim_snapshot *ss = ent->ss;
/* Update local xform */
if (ent->is_top) {
ent->_local_xform = xf;
} else {
struct sim_ent *parent = sim_ent_from_id(ss, ent->parent);
struct xform parent_global = sim_ent_get_xform_internal(ss, parent);
ent->_local_xform = xform_mul(xform_invert(parent_global), xf);
}
ent->_xform = xf;
ent->_is_xform_dirty = 0;
sim_ent_mark_child_xforms_dirty(ss, ent);
}
}
void sim_ent_set_local_xform(struct sim_ent *ent, struct xform xf)
{
if (!xform_eq(xf, ent->_local_xform)) {
ent->_local_xform = xf;
ent->_is_xform_dirty = 1;
sim_ent_mark_child_xforms_dirty(ent->ss, ent);
}
}
/* ========================== *
* Ent movement
* ========================== */
void sim_ent_set_linear_velocity(struct sim_ent *ent, struct v2 velocity)
{
if (sim_ent_has_prop(ent, SEPROP_KINEMATIC) || sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
ent->linear_velocity = v2_clamp_len(velocity, SIM_MAX_LINEAR_VELOCITY);
}
}
void sim_ent_set_angular_velocity(struct sim_ent *ent, f32 velocity)
{
if (sim_ent_has_prop(ent, SEPROP_KINEMATIC) || sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
ent->angular_velocity = clamp_f32(velocity, -SIM_MAX_ANGULAR_VELOCITY, SIM_MAX_ANGULAR_VELOCITY);
}
}
void sim_ent_apply_linear_impulse(struct sim_ent *ent, struct v2 impulse, struct v2 point)
{
if (sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
struct xform xf = sim_ent_get_xform(ent);
struct v2 center = xf.og;
f32 scale = math_fabs(xform_get_determinant(xf));
f32 inv_mass = 1.f / (ent->mass_unscaled * scale);
f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale);
struct v2 vcp = v2_sub(point, center);
sim_ent_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass)));
sim_ent_set_angular_velocity(ent, v2_wedge(vcp, impulse) * inv_inertia);
}
}
void sim_ent_apply_linear_impulse_to_center(struct sim_ent *ent, struct v2 impulse)
{
if (sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
struct xform xf = sim_ent_get_xform(ent);
f32 scale = math_fabs(xform_get_determinant(xf));
f32 inv_mass = 1.f / (ent->mass_unscaled * scale);
sim_ent_set_linear_velocity(ent, v2_add(ent->linear_velocity, v2_mul(impulse, inv_mass)));
}
}
void sim_ent_apply_force_to_center(struct sim_ent *ent, struct v2 force)
{
if (sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
ent->force = v2_add(ent->force, force);
}
}
void sim_ent_apply_angular_impulse(struct sim_ent *ent, f32 impulse)
{
if (sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
struct xform xf = sim_ent_get_xform(ent);
f32 scale = math_fabs(xform_get_determinant(xf));
f32 inv_inertia = 1.f / (ent->inertia_unscaled * scale);
sim_ent_set_angular_velocity(ent, ent->angular_velocity + impulse * inv_inertia);
}
}
void sim_ent_apply_torque(struct sim_ent *ent, f32 torque)
{
if (sim_ent_has_prop(ent, SEPROP_DYNAMIC)) {
ent->torque += torque;
}
}
/* ========================== *
* Tile
* ========================== */
struct sim_ent *sim_tile_chunk_from_chunk_index(struct sim_snapshot *ss, struct v2i32 chunk_index)
{
struct sim_ent_id chunk_id = sim_ent_tile_chunk_id_from_tile_chunk_index(chunk_index);
struct sim_ent *chunk_ent = sim_ent_from_id(ss, chunk_id);
return chunk_ent;
}
struct sim_ent *sim_tile_chunk_from_world_tile_index(struct sim_snapshot *ss, struct v2i32 world_tile_index)
{
struct v2i32 chunk_index = sim_tile_chunk_index_from_world_tile_index(world_tile_index);
struct sim_ent *chunk_ent = sim_tile_chunk_from_chunk_index(ss, chunk_index);
return chunk_ent;
}
enum sim_tile_kind sim_get_chunk_tile(struct sim_ent *chunk_ent, struct v2i32 local_tile_index)
{
enum sim_tile_kind res = chunk_ent->tile_chunk_tiles[local_tile_index.x + (local_tile_index.y * SIM_TILES_PER_CHUNK_SQRT)];
return res;
}
/* ========================== *
* Ent lerp
* ========================== */
void sim_ent_lerp(struct sim_ent *e, struct sim_ent *e0, struct sim_ent *e1, f64 blend)
{
if (sim_ent_is_valid_and_active(e0) && sim_ent_is_valid_and_active(e1)
&& sim_ent_id_eq(e0->id, e1->id)
&& e0->continuity_gen == e1->continuity_gen) {
e->_local_xform = xform_lerp(e0->_local_xform, e1->_local_xform, blend);
if (e->is_top) {
/* TODO: Cache parent & child xforms in sim */
struct xform e0_xf = sim_ent_get_xform(e0);
struct xform e1_xf = sim_ent_get_xform(e1);
sim_ent_set_xform(e, xform_lerp(e0_xf, e1_xf, blend));
}
e->control_force = math_lerp_f32(e0->control_force, e1->control_force, blend);
e->control_torque = math_lerp_f32(e0->control_torque, e1->control_torque, blend);
e->linear_velocity = v2_lerp(e0->linear_velocity, e1->linear_velocity, blend);
e->angular_velocity = math_lerp_angle(e0->angular_velocity, e1->angular_velocity, blend);
e->control.move = v2_lerp(e0->control.move, e1->control.move, blend);
e->control.focus = v2_lerp(e0->control.focus, e1->control.focus, blend);
e->sprite_local_xform = xform_lerp(e0->sprite_local_xform, e1->sprite_local_xform, blend);
e->animation_last_frame_change_time_ns = math_lerp_i64(e0->animation_last_frame_change_time_ns, e1->animation_last_frame_change_time_ns, (f64)blend);
e->animation_frame = (u32)math_round_to_int(math_lerp_f32(e0->animation_frame, e1->animation_frame, blend));
e->camera_quad_xform = xform_lerp(e0->camera_quad_xform, e1->camera_quad_xform, blend);
e->camera_xform_target = xform_lerp(e0->camera_xform_target, e1->camera_xform_target, blend);
e->shake = math_lerp_f32(e0->shake, e1->shake, blend);
e->tracer_gradient_start = v2_lerp(e0->tracer_gradient_start, e1->tracer_gradient_start, blend);
e->tracer_gradient_end = v2_lerp(e0->tracer_gradient_end, e1->tracer_gradient_end, blend);
}
}
/* ========================== *
* Ent sync
* ========================== */
/* Walks a local & remote ent tree and allocates any missing net dst ents from remote src ents */
void sim_ent_sync_alloc_tree(struct sim_ent *local_parent, struct sim_ent *remote, struct sim_ent_id remote_player)
{
__prof;
if (sim_ent_has_prop(remote, SEPROP_SYNC_SRC)) {
struct sim_snapshot *local_ss = local_parent->ss;
struct sim_snapshot *remote_ss = remote->ss;
struct sim_ent_id id = remote->id;
struct sim_ent *local_ent = sim_ent_from_id(local_ss, id);
if (!local_ent->valid) {
local_ent = sim_ent_alloc_sync_dst(local_parent, id, remote_player);
}
for (struct sim_ent *remote_child = sim_ent_from_id(remote_ss, remote->first); remote_child->valid; remote_child = sim_ent_from_id(remote_ss, remote_child->next)) {
sim_ent_sync_alloc_tree(local_ent, remote_child, remote_player);
}
}
}
/* Copies data between two synced entities */
void sim_ent_sync(struct sim_ent *local, struct sim_ent *remote)
{
struct sim_ent old = *local;
MEMCPY_STRUCT(local, remote);
/* Why would 2 ents w/ different uids ever be synced? */
ASSERT(sim_ent_id_eq(local->id, old.id));
local->ss = old.ss;
local->id = old.id;
/* Keep local tree */
local->parent = old.parent;
local->prev = old.prev;
local->next = old.next;
local->first = old.first;
local->last = old.last;
local->top = old.top;
local->owner = old.owner;
/* Keep indices */
local->next_in_id_bin = old.next_in_id_bin;
local->prev_in_id_bin = old.prev_in_id_bin;
local->next_free = old.next_free;
sim_ent_disable_prop(local, SEPROP_SYNC_SRC);
sim_ent_enable_prop(local, SEPROP_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. */
/* 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 (bw_write_bit(bw, !MEMEQ(chunk0, chunk1, chunk_size))) {
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
* ========================== */
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_ent decoded = *e;
{
u64 pos = 0;
while (pos < sizeof(decoded)) {
u8 *chunk = (u8 *)&decoded + pos;
if (br_read_bit(br)) {
u64 chunk_size = min_u64(pos + 8, sizeof(decoded)) - pos;
u64 bits = br_read_ubits(br, 64);
MEMCPY(chunk, &bits, chunk_size);
}
pos += 8;
}
}
decoded.ss = e->ss;
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