refactor separate framework from game code, add demo3d

This commit is contained in:
asrael 2026-04-14 01:28:38 -05:00
parent 19ae869769
commit 40f5cdcaa5
92 changed files with 2665 additions and 6547 deletions

View file

@ -0,0 +1,297 @@
#include "pxl8_platform.h"
#include "demo3d_net.h"
#include <string.h>
#include "pxl8_bytes.h"
#include "pxl8_log.h"
#include "pxl8_mem.h"
#include "demo3d_world.h"
#include "demo3d_chunk_cache.h"
static const demo3d_entity_state* find_entity(const demo3d_entity_state* entities, u16 count, u64 id) {
for (u16 i = 0; i < count; i++) {
if (entities[i].entity_id == id) return &entities[i];
}
return NULL;
}
static bool dispatch_message(demo3d_net* ng, const u8* data, usize len) {
if (len < sizeof(demo3d_msg_header)) return false;
demo3d_msg_header hdr;
usize offset = demo3d_protocol_deserialize_header(data, len, &hdr);
if (hdr.type == DEMO3D_MSG_CHUNK) {
if (!ng->chunk_cache) return false;
demo3d_chunk_msg_header chunk_hdr;
offset += demo3d_protocol_deserialize_chunk_msg_header(data + offset, len - offset, &chunk_hdr);
const u8* payload = data + offset;
usize payload_len = chunk_hdr.payload_size;
if (payload_len > len - offset) payload_len = len - offset;
demo3d_chunk_cache_receive(ng->chunk_cache, &chunk_hdr, payload, payload_len);
return true;
}
if (hdr.type == DEMO3D_MSG_CHUNK_ENTER) {
demo3d_chunk_enter_msg chunk_msg;
demo3d_protocol_deserialize_chunk_enter(data + offset, len - offset, &chunk_msg);
ng->chunk_cx = chunk_msg.cx;
ng->chunk_cz = chunk_msg.cz;
ng->has_chunk = true;
pxl8_debug("Received CHUNK_ENTER cx=%d cz=%d", chunk_msg.cx, chunk_msg.cz);
return true;
}
if (hdr.type == DEMO3D_MSG_CHUNK_EXIT) {
ng->has_chunk = false;
return true;
}
if (hdr.type != DEMO3D_MSG_SNAPSHOT) return false;
demo3d_snapshot_header snap;
offset += demo3d_protocol_deserialize_snapshot_header(data + offset, len - offset, &snap);
if (snap.tick <= ng->highest_tick) return false;
memcpy(ng->prev_entities, ng->entities, sizeof(ng->entities));
ng->prev_snapshot = ng->snapshot;
ng->highest_tick = snap.tick;
ng->snapshot = snap;
ng->interp_time = 0.0f;
u16 count = snap.entity_count;
if (count > DEMO3D_MAX_SNAPSHOT_ENTITIES) count = DEMO3D_MAX_SNAPSHOT_ENTITIES;
for (u16 i = 0; i < count; i++) {
offset += demo3d_protocol_deserialize_entity_state(data + offset, len - offset, &ng->entities[i]);
}
return true;
}
demo3d_net* demo3d_net_create(const pxl8_net_config* config) {
demo3d_net* ng = pxl8_calloc(1, sizeof(demo3d_net));
if (!ng) return NULL;
ng->transport = pxl8_net_create(config);
if (!ng->transport) {
pxl8_free(ng);
return NULL;
}
return ng;
}
void demo3d_net_destroy(demo3d_net* ng) {
if (!ng) return;
pxl8_net_destroy(ng->transport);
pxl8_free(ng);
}
pxl8_result demo3d_net_connect(demo3d_net* ng) {
if (!ng) return PXL8_ERROR_INVALID_ARGUMENT;
return pxl8_net_connect(ng->transport);
}
bool demo3d_net_connected(const demo3d_net* ng) {
return ng && pxl8_net_connected(ng->transport);
}
void demo3d_net_disconnect(demo3d_net* ng) {
if (!ng) return;
pxl8_net_disconnect(ng->transport);
}
const demo3d_entity_state* demo3d_net_entities(const demo3d_net* ng) {
if (!ng) return NULL;
return ng->entities;
}
const u8* demo3d_net_entity_prev_userdata(const demo3d_net* ng, u64 entity_id) {
if (!ng) return NULL;
const demo3d_entity_state* e = find_entity(ng->prev_entities, ng->prev_snapshot.entity_count, entity_id);
return e ? e->userdata : NULL;
}
const u8* demo3d_net_entity_userdata(const demo3d_net* ng, u64 entity_id) {
if (!ng) return NULL;
const demo3d_entity_state* e = find_entity(ng->entities, ng->snapshot.entity_count, entity_id);
return e ? e->userdata : NULL;
}
const demo3d_input_msg* demo3d_net_input_at(const demo3d_net* ng, u64 tick) {
if (!ng) return NULL;
if (tick < ng->input_oldest_tick) return NULL;
u64 age = tick - ng->input_oldest_tick;
if (age >= DEMO3D_NET_INPUT_HISTORY_SIZE) return NULL;
u64 idx = tick % DEMO3D_NET_INPUT_HISTORY_SIZE;
const demo3d_input_msg* msg = &ng->input_history[idx];
if (msg->tick != tick) return NULL;
return msg;
}
u64 demo3d_net_input_oldest_tick(const demo3d_net* ng) {
if (!ng) return 0;
return ng->input_oldest_tick;
}
void demo3d_net_input_push(demo3d_net* ng, const demo3d_input_msg* input) {
if (!ng || !input) return;
u64 idx = input->tick % DEMO3D_NET_INPUT_HISTORY_SIZE;
ng->input_history[idx] = *input;
ng->input_head = input->tick;
if (input->tick >= DEMO3D_NET_INPUT_HISTORY_SIZE) {
ng->input_oldest_tick = input->tick - DEMO3D_NET_INPUT_HISTORY_SIZE + 1;
}
}
f32 demo3d_net_lerp_alpha(const demo3d_net* ng) {
if (!ng) return 1.0f;
return ng->interp_time / (1.0f / DEMO3D_NET_TICK_RATE);
}
bool demo3d_net_needs_correction(const demo3d_net* ng) {
if (!ng) return false;
if (ng->snapshot.tick == 0) return false;
if (ng->predicted_tick == 0) return false;
u64 player_id = ng->snapshot.player_id;
const demo3d_entity_state* server = find_entity(ng->entities, ng->snapshot.entity_count, player_id);
if (!server) return false;
return memcmp(server->userdata, ng->predicted_state, DEMO3D_NET_USERDATA_SIZE) != 0;
}
u64 demo3d_net_player_id(const demo3d_net* ng) {
if (!ng) return 0;
return ng->snapshot.player_id;
}
bool demo3d_net_poll(demo3d_net* ng) {
if (!ng || !pxl8_net_connected(ng->transport)) return false;
u8 recv_buf[4096];
usize len = pxl8_net_recv(ng->transport, recv_buf, sizeof(recv_buf));
u64 prev_tick = ng->highest_tick;
if (!dispatch_message(ng, recv_buf, len)) return false;
if (ng->highest_tick > prev_tick && ng->world) {
demo3d_world_reconcile(ng->world, ng, 1.0f / 30.0f);
}
return true;
}
u8* demo3d_net_predicted_state(demo3d_net* ng) {
if (!ng) return NULL;
return ng->predicted_state;
}
void demo3d_net_predicted_tick_set(demo3d_net* ng, u64 tick) {
if (!ng) return;
ng->predicted_tick = tick;
}
pxl8_result demo3d_net_send_command(demo3d_net* ng, const demo3d_command_msg* cmd) {
if (!ng || !cmd) return PXL8_ERROR_INVALID_ARGUMENT;
if (!pxl8_net_connected(ng->transport)) return PXL8_ERROR_INVALID_ARGUMENT;
u8 buf[512];
demo3d_msg_header hdr = {.type = DEMO3D_MSG_COMMAND, .version = DEMO3D_PROTOCOL_VERSION};
usize offset = demo3d_protocol_serialize_header(&hdr, buf, sizeof(buf));
offset += demo3d_protocol_serialize_command(cmd, buf + offset, sizeof(buf) - offset);
return pxl8_net_send(ng->transport, buf, offset);
}
pxl8_result demo3d_net_send_input(demo3d_net* ng, const demo3d_input_msg* input) {
if (!ng || !input) return PXL8_ERROR_INVALID_ARGUMENT;
if (!pxl8_net_connected(ng->transport)) return PXL8_ERROR_INVALID_ARGUMENT;
demo3d_net_input_push(ng, input);
u8 buf[512];
demo3d_msg_header hdr = {.type = DEMO3D_MSG_INPUT, .version = DEMO3D_PROTOCOL_VERSION};
usize offset = demo3d_protocol_serialize_header(&hdr, buf, sizeof(buf));
offset += demo3d_protocol_serialize_input(input, buf + offset, sizeof(buf) - offset);
return pxl8_net_send(ng->transport, buf, offset);
}
const demo3d_snapshot_header* demo3d_net_snapshot(const demo3d_net* ng) {
if (!ng) return NULL;
return &ng->snapshot;
}
u64 demo3d_net_tick(const demo3d_net* ng) {
if (!ng) return 0;
return ng->snapshot.tick;
}
void demo3d_net_update(demo3d_net* ng, f32 dt) {
if (!ng) return;
ng->interp_time += dt;
}
void demo3d_net_set_chunk_cache(demo3d_net* ng, demo3d_chunk_cache* cache) {
if (!ng) return;
ng->chunk_cache = cache;
}
demo3d_chunk_cache* demo3d_net_chunk_cache(demo3d_net* ng) {
if (!ng) return NULL;
return ng->chunk_cache;
}
void demo3d_net_set_world(demo3d_net* ng, demo3d_world* world) {
if (!ng) return;
ng->world = world;
}
i32 demo3d_net_chunk_cx(const demo3d_net* ng) {
if (!ng) return 0;
return ng->chunk_cx;
}
i32 demo3d_net_chunk_cz(const demo3d_net* ng) {
if (!ng) return 0;
return ng->chunk_cz;
}
bool demo3d_net_has_chunk(const demo3d_net* ng) {
if (!ng) return false;
return ng->has_chunk;
}
pxl8_result demo3d_net_spawn(demo3d_net* ng, f32 x, f32 y, f32 z, f32 yaw, f32 pitch) {
if (!ng) return PXL8_ERROR_INVALID_ARGUMENT;
demo3d_command_msg cmd = {0};
cmd.cmd_type = DEMO3D_CMD_SPAWN_ENTITY;
u8* p = cmd.payload;
memcpy(p, &x, 4); p += 4;
memcpy(p, &y, 4); p += 4;
memcpy(p, &z, 4); p += 4;
memcpy(p, &yaw, 4); p += 4;
memcpy(p, &pitch, 4);
cmd.payload_size = 20;
return demo3d_net_send_command(ng, &cmd);
}
#ifdef PXL8_ASYNC_THREADS
void demo3d_net_start_thread(demo3d_net* ng) {
if (!ng) return;
pxl8_net_start_thread(ng->transport);
}
void demo3d_net_stop_thread(demo3d_net* ng) {
if (!ng) return;
pxl8_net_stop_thread(ng->transport);
}
bool demo3d_net_dispatch_packet(demo3d_net* ng, const pxl8_packet* pkt) {
if (!ng || !pkt) return false;
return dispatch_message(ng, pkt->data, pkt->len);
}
#endif

View file

@ -0,0 +1,74 @@
#pragma once
#include "pxl8_net.h"
#include "demo3d_protocol.h"
#include "pxl8_types.h"
#define DEMO3D_NET_INPUT_HISTORY_SIZE 64
#define DEMO3D_NET_USERDATA_SIZE 56
#define DEMO3D_NET_TICK_RATE 30.0f
typedef struct demo3d_world demo3d_world;
typedef struct demo3d_chunk_cache demo3d_chunk_cache;
typedef struct demo3d_net {
pxl8_net* transport;
demo3d_chunk_cache* chunk_cache;
i32 chunk_cx;
i32 chunk_cz;
bool has_chunk;
demo3d_world* world;
u64 highest_tick;
f32 interp_time;
demo3d_entity_state entities[DEMO3D_MAX_SNAPSHOT_ENTITIES];
demo3d_entity_state prev_entities[DEMO3D_MAX_SNAPSHOT_ENTITIES];
demo3d_snapshot_header prev_snapshot;
demo3d_snapshot_header snapshot;
u64 input_head;
demo3d_input_msg input_history[DEMO3D_NET_INPUT_HISTORY_SIZE];
u64 input_oldest_tick;
u8 predicted_state[DEMO3D_NET_USERDATA_SIZE];
u64 predicted_tick;
} demo3d_net;
demo3d_net* demo3d_net_create(const pxl8_net_config* config);
void demo3d_net_destroy(demo3d_net* ng);
pxl8_result demo3d_net_connect(demo3d_net* ng);
bool demo3d_net_connected(const demo3d_net* ng);
void demo3d_net_disconnect(demo3d_net* ng);
const demo3d_entity_state* demo3d_net_entities(const demo3d_net* ng);
const u8* demo3d_net_entity_prev_userdata(const demo3d_net* ng, u64 entity_id);
const u8* demo3d_net_entity_userdata(const demo3d_net* ng, u64 entity_id);
const demo3d_input_msg* demo3d_net_input_at(const demo3d_net* ng, u64 tick);
u64 demo3d_net_input_oldest_tick(const demo3d_net* ng);
void demo3d_net_input_push(demo3d_net* ng, const demo3d_input_msg* input);
f32 demo3d_net_lerp_alpha(const demo3d_net* ng);
bool demo3d_net_needs_correction(const demo3d_net* ng);
u64 demo3d_net_player_id(const demo3d_net* ng);
bool demo3d_net_poll(demo3d_net* ng);
u8* demo3d_net_predicted_state(demo3d_net* ng);
void demo3d_net_predicted_tick_set(demo3d_net* ng, u64 tick);
pxl8_result demo3d_net_send_command(demo3d_net* ng, const demo3d_command_msg* cmd);
pxl8_result demo3d_net_send_input(demo3d_net* ng, const demo3d_input_msg* input);
const demo3d_snapshot_header* demo3d_net_snapshot(const demo3d_net* ng);
u64 demo3d_net_tick(const demo3d_net* ng);
void demo3d_net_update(demo3d_net* ng, f32 dt);
void demo3d_net_set_chunk_cache(demo3d_net* ng, demo3d_chunk_cache* cache);
demo3d_chunk_cache* demo3d_net_chunk_cache(demo3d_net* ng);
void demo3d_net_set_world(demo3d_net* ng, demo3d_world* world);
i32 demo3d_net_chunk_cx(const demo3d_net* ng);
i32 demo3d_net_chunk_cz(const demo3d_net* ng);
bool demo3d_net_has_chunk(const demo3d_net* ng);
pxl8_result demo3d_net_spawn(demo3d_net* ng, f32 x, f32 y, f32 z, f32 yaw, f32 pitch);
#ifdef PXL8_ASYNC_THREADS
void demo3d_net_start_thread(demo3d_net* ng);
void demo3d_net_stop_thread(demo3d_net* ng);
bool demo3d_net_dispatch_packet(demo3d_net* ng, const pxl8_packet* pkt);
#endif

View file

@ -0,0 +1,197 @@
#include "demo3d_protocol.h"
#include "pxl8_bytes.h"
usize demo3d_protocol_serialize_header(const demo3d_msg_header* msg, u8* buf, usize len) {
if (len < sizeof(demo3d_msg_header)) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u32_be(&s, msg->sequence);
pxl8_write_u16_be(&s, msg->size);
pxl8_write_u8(&s, msg->type);
pxl8_write_u8(&s, msg->version);
return s.offset;
}
usize demo3d_protocol_deserialize_header(const u8* buf, usize len, demo3d_msg_header* msg) {
if (len < sizeof(demo3d_msg_header)) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
msg->sequence = pxl8_read_u32_be(&s);
msg->size = pxl8_read_u16_be(&s);
msg->type = pxl8_read_u8(&s);
msg->version = pxl8_read_u8(&s);
return s.offset;
}
usize demo3d_protocol_serialize_input(const demo3d_input_msg* msg, u8* buf, usize len) {
if (len < sizeof(demo3d_input_msg)) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u32_be(&s, msg->buttons);
pxl8_write_f32_be(&s, msg->look_dx);
pxl8_write_f32_be(&s, msg->look_dy);
pxl8_write_f32_be(&s, msg->move_x);
pxl8_write_f32_be(&s, msg->move_y);
pxl8_write_f32_be(&s, msg->yaw);
pxl8_write_u64_be(&s, msg->tick);
pxl8_write_u64_be(&s, msg->timestamp);
return s.offset;
}
usize demo3d_protocol_deserialize_input(const u8* buf, usize len, demo3d_input_msg* msg) {
if (len < sizeof(demo3d_input_msg)) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
msg->buttons = pxl8_read_u32_be(&s);
msg->look_dx = pxl8_read_f32_be(&s);
msg->look_dy = pxl8_read_f32_be(&s);
msg->move_x = pxl8_read_f32_be(&s);
msg->move_y = pxl8_read_f32_be(&s);
msg->yaw = pxl8_read_f32_be(&s);
msg->tick = pxl8_read_u64_be(&s);
msg->timestamp = pxl8_read_u64_be(&s);
return s.offset;
}
usize demo3d_protocol_serialize_command(const demo3d_command_msg* msg, u8* buf, usize len) {
if (len < sizeof(demo3d_command_msg)) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u16_be(&s, msg->cmd_type);
pxl8_write_bytes(&s, msg->payload, DEMO3D_COMMAND_PAYLOAD_SIZE);
pxl8_write_u16_be(&s, msg->payload_size);
pxl8_write_u64_be(&s, msg->tick);
return s.offset;
}
usize demo3d_protocol_deserialize_command(const u8* buf, usize len, demo3d_command_msg* msg) {
if (len < sizeof(demo3d_command_msg)) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
msg->cmd_type = pxl8_read_u16_be(&s);
pxl8_read_bytes(&s, msg->payload, DEMO3D_COMMAND_PAYLOAD_SIZE);
msg->payload_size = pxl8_read_u16_be(&s);
msg->tick = pxl8_read_u64_be(&s);
return s.offset;
}
usize demo3d_protocol_serialize_entity_state(const demo3d_entity_state* state, u8* buf, usize len) {
if (len < sizeof(demo3d_entity_state)) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u64_be(&s, state->entity_id);
pxl8_write_bytes(&s, state->userdata, 56);
return s.offset;
}
usize demo3d_protocol_deserialize_entity_state(const u8* buf, usize len, demo3d_entity_state* state) {
if (len < sizeof(demo3d_entity_state)) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
state->entity_id = pxl8_read_u64_be(&s);
pxl8_read_bytes(&s, state->userdata, 56);
return s.offset;
}
usize demo3d_protocol_serialize_event(const demo3d_event_msg* msg, u8* buf, usize len) {
if (len < sizeof(demo3d_event_msg)) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u8(&s, msg->event_type);
pxl8_write_bytes(&s, msg->payload, DEMO3D_EVENT_PAYLOAD_SIZE);
return s.offset;
}
usize demo3d_protocol_deserialize_event(const u8* buf, usize len, demo3d_event_msg* msg) {
if (len < sizeof(demo3d_event_msg)) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
msg->event_type = pxl8_read_u8(&s);
pxl8_read_bytes(&s, msg->payload, DEMO3D_EVENT_PAYLOAD_SIZE);
return s.offset;
}
usize demo3d_protocol_serialize_snapshot_header(const demo3d_snapshot_header* hdr, u8* buf, usize len) {
if (len < sizeof(demo3d_snapshot_header)) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u16_be(&s, hdr->entity_count);
pxl8_write_u16_be(&s, hdr->event_count);
pxl8_write_u64_be(&s, hdr->player_id);
pxl8_write_u64_be(&s, hdr->tick);
pxl8_write_f32_be(&s, hdr->time);
return s.offset;
}
usize demo3d_protocol_deserialize_snapshot_header(const u8* buf, usize len, demo3d_snapshot_header* hdr) {
if (len < sizeof(demo3d_snapshot_header)) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
hdr->entity_count = pxl8_read_u16_be(&s);
hdr->event_count = pxl8_read_u16_be(&s);
hdr->player_id = pxl8_read_u64_be(&s);
hdr->tick = pxl8_read_u64_be(&s);
hdr->time = pxl8_read_f32_be(&s);
return s.offset;
}
usize demo3d_protocol_serialize_chunk_msg_header(const demo3d_chunk_msg_header* hdr, u8* buf, usize len) {
if (len < 24) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u8(&s, hdr->chunk_type);
pxl8_write_u8(&s, hdr->flags);
pxl8_write_u8(&s, hdr->fragment_idx);
pxl8_write_u8(&s, hdr->fragment_count);
pxl8_write_u32_be(&s, hdr->id);
pxl8_write_u32_be(&s, (u32)hdr->cx);
pxl8_write_u32_be(&s, (u32)hdr->cy);
pxl8_write_u32_be(&s, (u32)hdr->cz);
pxl8_write_u32_be(&s, hdr->version);
pxl8_write_u16_be(&s, hdr->payload_size);
pxl8_write_u16_be(&s, hdr->reserved);
return s.offset;
}
usize demo3d_protocol_deserialize_chunk_msg_header(const u8* buf, usize len, demo3d_chunk_msg_header* hdr) {
if (len < 24) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
hdr->chunk_type = pxl8_read_u8(&s);
hdr->flags = pxl8_read_u8(&s);
hdr->fragment_idx = pxl8_read_u8(&s);
hdr->fragment_count = pxl8_read_u8(&s);
hdr->id = pxl8_read_u32_be(&s);
hdr->cx = (i32)pxl8_read_u32_be(&s);
hdr->cy = (i32)pxl8_read_u32_be(&s);
hdr->cz = (i32)pxl8_read_u32_be(&s);
hdr->version = pxl8_read_u32_be(&s);
hdr->payload_size = pxl8_read_u16_be(&s);
hdr->reserved = pxl8_read_u16_be(&s);
return s.offset;
}
usize demo3d_protocol_deserialize_bsp_wire_header(const u8* buf, usize len, demo3d_bsp_wire_header* hdr) {
if (len < 48) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
hdr->num_vertices = pxl8_read_u32_be(&s);
hdr->num_edges = pxl8_read_u32_be(&s);
hdr->num_faces = pxl8_read_u32_be(&s);
hdr->num_planes = pxl8_read_u32_be(&s);
hdr->num_nodes = pxl8_read_u32_be(&s);
hdr->num_leafs = pxl8_read_u32_be(&s);
hdr->num_surfedges = pxl8_read_u32_be(&s);
hdr->num_marksurfaces = pxl8_read_u32_be(&s);
hdr->num_cell_portals = pxl8_read_u32_be(&s);
hdr->visdata_size = pxl8_read_u32_be(&s);
hdr->num_vertex_lights = pxl8_read_u32_be(&s);
hdr->num_heightfield = pxl8_read_u32_be(&s);
return s.offset;
}
usize demo3d_protocol_serialize_chunk_enter(const demo3d_chunk_enter_msg* msg, u8* buf, usize len) {
if (len < 8) return 0;
pxl8_write_stream s = pxl8_write_stream_create(buf, (u32)len);
pxl8_write_u32_be(&s, (u32)msg->cx);
pxl8_write_u32_be(&s, (u32)msg->cz);
return s.offset;
}
usize demo3d_protocol_deserialize_chunk_enter(const u8* buf, usize len, demo3d_chunk_enter_msg* msg) {
if (len < 8) return 0;
pxl8_stream s = pxl8_stream_create(buf, (u32)len);
msg->cx = (i32)pxl8_read_u32_be(&s);
msg->cz = (i32)pxl8_read_u32_be(&s);
return s.offset;
}
u32 demo3d_chunk_hash(i32 cx, i32 cz) {
u32 h = (u32)cx * 374761393u + (u32)cz * 668265263u;
return h ^ (h >> 16);
}

View file

@ -0,0 +1,143 @@
#pragma once
#include "pxl8_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEMO3D_PROTOCOL_VERSION 1
#define DEMO3D_MAX_SNAPSHOT_ENTITIES 256
#define DEMO3D_MAX_SNAPSHOT_EVENTS 32
#define DEMO3D_COMMAND_PAYLOAD_SIZE 64
#define DEMO3D_EVENT_PAYLOAD_SIZE 15
typedef enum demo3d_msg_type {
DEMO3D_MSG_NONE = 0,
DEMO3D_MSG_CHUNK,
DEMO3D_MSG_CHUNK_ENTER,
DEMO3D_MSG_CHUNK_EXIT,
DEMO3D_MSG_COMMAND,
DEMO3D_MSG_CONNECT,
DEMO3D_MSG_DISCONNECT,
DEMO3D_MSG_EVENT,
DEMO3D_MSG_INPUT,
DEMO3D_MSG_SNAPSHOT
} demo3d_msg_type;
typedef struct demo3d_msg_header {
u32 sequence;
u16 size;
u8 type;
u8 version;
} demo3d_msg_header;
typedef enum demo3d_cmd_type {
DEMO3D_CMD_NONE = 0,
DEMO3D_CMD_SPAWN_ENTITY,
} demo3d_cmd_type;
typedef struct demo3d_input_msg {
u32 buttons;
f32 look_dx;
f32 look_dy;
f32 move_x;
f32 move_y;
f32 yaw;
u64 tick;
u64 timestamp;
} demo3d_input_msg;
typedef struct demo3d_command_msg {
u16 cmd_type;
u8 payload[DEMO3D_COMMAND_PAYLOAD_SIZE];
u16 payload_size;
u64 tick;
} demo3d_command_msg;
typedef struct demo3d_entity_state {
u64 entity_id;
u8 userdata[56];
} demo3d_entity_state;
typedef struct demo3d_event_msg {
u8 event_type;
u8 payload[DEMO3D_EVENT_PAYLOAD_SIZE];
} demo3d_event_msg;
typedef struct demo3d_snapshot_header {
u16 entity_count;
u16 event_count;
u64 player_id;
u64 tick;
f32 time;
} demo3d_snapshot_header;
#define DEMO3D_CHUNK_TYPE_BSP 1
#define DEMO3D_CHUNK_FLAG_FINAL 0x04
#define DEMO3D_CHUNK_MAX_PAYLOAD 1400
typedef struct demo3d_chunk_msg_header {
u8 chunk_type;
u8 flags;
u8 fragment_idx;
u8 fragment_count;
u32 id;
i32 cx, cy, cz;
u32 version;
u16 payload_size;
u16 reserved;
} demo3d_chunk_msg_header;
typedef struct demo3d_bsp_wire_header {
u32 num_vertices;
u32 num_edges;
u32 num_faces;
u32 num_planes;
u32 num_nodes;
u32 num_leafs;
u32 num_surfedges;
u32 num_marksurfaces;
u32 num_cell_portals;
u32 visdata_size;
u32 num_vertex_lights;
u32 num_heightfield;
} demo3d_bsp_wire_header;
usize demo3d_protocol_serialize_header(const demo3d_msg_header* msg, u8* buf, usize len);
usize demo3d_protocol_deserialize_header(const u8* buf, usize len, demo3d_msg_header* msg);
usize demo3d_protocol_serialize_input(const demo3d_input_msg* msg, u8* buf, usize len);
usize demo3d_protocol_deserialize_input(const u8* buf, usize len, demo3d_input_msg* msg);
usize demo3d_protocol_serialize_command(const demo3d_command_msg* msg, u8* buf, usize len);
usize demo3d_protocol_deserialize_command(const u8* buf, usize len, demo3d_command_msg* msg);
usize demo3d_protocol_serialize_entity_state(const demo3d_entity_state* state, u8* buf, usize len);
usize demo3d_protocol_deserialize_entity_state(const u8* buf, usize len, demo3d_entity_state* state);
usize demo3d_protocol_serialize_event(const demo3d_event_msg* msg, u8* buf, usize len);
usize demo3d_protocol_deserialize_event(const u8* buf, usize len, demo3d_event_msg* msg);
usize demo3d_protocol_serialize_snapshot_header(const demo3d_snapshot_header* hdr, u8* buf, usize len);
usize demo3d_protocol_deserialize_snapshot_header(const u8* buf, usize len, demo3d_snapshot_header* hdr);
usize demo3d_protocol_serialize_chunk_msg_header(const demo3d_chunk_msg_header* hdr, u8* buf, usize len);
usize demo3d_protocol_deserialize_chunk_msg_header(const u8* buf, usize len, demo3d_chunk_msg_header* hdr);
usize demo3d_protocol_deserialize_bsp_wire_header(const u8* buf, usize len, demo3d_bsp_wire_header* hdr);
typedef struct demo3d_chunk_enter_msg {
i32 cx;
i32 cz;
} demo3d_chunk_enter_msg;
usize demo3d_protocol_serialize_chunk_enter(const demo3d_chunk_enter_msg* msg, u8* buf, usize len);
usize demo3d_protocol_deserialize_chunk_enter(const u8* buf, usize len, demo3d_chunk_enter_msg* msg);
u32 demo3d_chunk_hash(i32 cx, i32 cz);
#ifdef __cplusplus
}
#endif