#include "sim_client.h" #include "sim.h" #include "sim_snapshot.h" #include "host.h" #include "arena.h" #include "util.h" #include "arena.h" /* ========================== * * Client * ========================== */ INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id) { return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id)); } struct sim_client *sim_client_from_handle(struct sim_snapshot *ss, struct sim_client_handle handle) { if (handle.gen != 0 && handle.idx < ss->num_clients_reserved) { struct sim_client *client = &ss->clients[handle.idx]; if (client->handle.gen == handle.gen) { return client; } } return sim_client_nil(); } struct sim_client *sim_client_from_channel_id(struct sim_snapshot *ss, struct host_channel_id channel_id) { struct sim_client *res = sim_client_nil(); u64 channel_hash = hash_from_channel_id(channel_id); u64 bucket_index = channel_hash % ss->num_client_lookup_buckets; struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; for (struct sim_client *client = sim_client_from_handle(ss, bucket->first); client->valid; client = sim_client_from_handle(ss, client->next_in_bucket)) { if (client->channel_hash == channel_hash) { res = client; break; } } return res; } struct sim_client *sim_client_alloc(struct sim_snapshot *ss, struct host_channel_id channel_id, enum sim_client_kind kind) { struct sim_client_handle handle = ZI; struct sim_client *client = sim_client_from_handle(ss, ss->first_free_client); if (client->valid) { ss->first_free_client = client->next_free; handle = client->handle; ++handle.gen; } else { client = arena_push(&ss->clients_arena, struct sim_client); handle.gen = 1; handle.idx = ss->num_clients_reserved; ++ss->num_clients_reserved; } ++ss->num_clients_allocated; *client = *sim_client_nil(); client->valid = true; client->kind = kind; client->handle = handle; if (kind == SIM_CLIENT_KIND_NETSIM) { ASSERT(!host_channel_id_is_nil(channel_id)); u64 channel_hash = hash_from_channel_id(channel_id); client->channel_id = channel_id; client->channel_hash = channel_hash; /* Insert into channel lookup */ u64 bucket_index = channel_hash % ss->num_client_lookup_buckets; struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; { struct sim_client *prev_in_bucket = sim_client_from_handle(ss, bucket->last); if (prev_in_bucket->valid) { prev_in_bucket->next_in_bucket = client->handle; client->prev_in_bucket = prev_in_bucket->handle; } else { bucket->first = client->handle; } bucket->last = client->handle; } } return client; } void sim_client_release(struct sim_client *client) { struct sim_snapshot *ss = client->ss; client->valid = false; ++client->handle.gen; client->next_free = ss->first_free_client; ss->first_free_client = client->handle; --ss->num_clients_allocated; /* Remove from channel lookup */ if (client->kind == SIM_CLIENT_KIND_NETSIM) { u64 bucket_index = client->channel_hash % ss->num_client_lookup_buckets; struct sim_client_lookup_bucket *bucket = &ss->client_lookup_buckets[bucket_index]; struct sim_client *prev = sim_client_from_handle(ss, client->prev_in_bucket); struct sim_client *next = sim_client_from_handle(ss, client->next_in_bucket); if (prev->valid) { prev->next_in_bucket = next->handle; } else { bucket->first = next->handle; } if (next->valid) { next->prev_in_bucket = prev->handle; } else { bucket->last = prev->handle; } } } /* ========================== * * Lerp * ========================== */ void sim_client_lerp(struct sim_client *c, struct sim_client *c0, struct sim_client *c1, f64 blend) { if (c0->valid && c1->valid && c0->handle.gen == c1->handle.gen) { c0->cursor_pos = v2_lerp(c0->cursor_pos, c1->cursor_pos, blend); c->control.move = v2_lerp(c0->control.move, c1->control.move, blend); c->control.focus = v2_lerp(c0->control.focus, c1->control.focus, blend); } } /* ========================== * * Encode * ========================== */ void sim_client_encode(struct bitbuff_writer *bw, struct sim_client *c0, struct sim_client *c1) { struct sim_snapshot *ss = c1->ss; /* TODO: Granular delta encoding */ u64 pos = 0; c1->ss = c0->ss; while (pos < sizeof(*c1)) { u64 chunk_size = min_u64(pos + 8, sizeof(*c1)) - pos; u8 *chunk0 = (u8 *)c0 + pos; u8 *chunk1 = (u8 *)c1 + 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; } c1->ss = ss; } /* ========================== * * Decode * ========================== */ void sim_client_decode(struct bitbuff_reader *br, struct sim_client *e) { struct sim_snapshot *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 = ss; }