179 lines
5.6 KiB
C
179 lines
5.6 KiB
C
#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;
|
|
}
|