refactor separate framework from game code, add demo3d
This commit is contained in:
parent
19ae869769
commit
40f5cdcaa5
92 changed files with 2665 additions and 6547 deletions
297
demo3d/client/net/demo3d_net.c
Normal file
297
demo3d/client/net/demo3d_net.c
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue