feat(gui): add toolbar widget

This commit is contained in:
asrael 2026-03-07 07:12:32 -06:00
parent afc063b2ab
commit b42bf74472
51 changed files with 2191 additions and 1461 deletions

View file

@ -46,17 +46,15 @@ struct pxl8_net {
socket_t sock;
pxl8_world_chunk_cache* chunk_cache;
u32 chunk_id;
u8 chunk_type;
i32 chunk_cx;
i32 chunk_cz;
bool has_chunk;
pxl8_world* world;
f32 dt;
u64 highest_tick;
f32 interp_time;
u32 sequence;
pxl8_entity_state entities[PXL8_MAX_SNAPSHOT_ENTITIES];
pxl8_event_msg events[PXL8_MAX_SNAPSHOT_EVENTS];
pxl8_entity_state prev_entities[PXL8_MAX_SNAPSHOT_ENTITIES];
pxl8_snapshot_header prev_snapshot;
pxl8_snapshot_header snapshot;
@ -109,6 +107,9 @@ pxl8_result pxl8_net_connect(pxl8_net* net) {
fcntl(net->sock, F_SETFL, flags | O_NONBLOCK);
#endif
int rcvbuf = 1024 * 1024;
setsockopt(net->sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
memset(&net->server_addr, 0, sizeof(net->server_addr));
#ifdef __APPLE__
net->server_addr.sin_len = sizeof(net->server_addr);
@ -131,9 +132,6 @@ pxl8_net* pxl8_net_create(const pxl8_net_config* config) {
net->port = config->port ? config->port : PXL8_NET_DEFAULT_PORT;
net->sock = INVALID_SOCK;
net->connected = false;
net->sequence = 0;
net->highest_tick = 0;
if (config->address) {
strncpy(net->address, config->address, sizeof(net->address) - 1);
@ -179,11 +177,6 @@ const u8* pxl8_net_entity_userdata(const pxl8_net* net, u64 entity_id) {
return e ? e->userdata : NULL;
}
const pxl8_event_msg* pxl8_net_events(const pxl8_net* net) {
if (!net) return NULL;
return net->events;
}
const pxl8_input_msg* pxl8_net_input_at(const pxl8_net* net, u64 tick) {
if (!net) return NULL;
for (u64 i = 0; i < PXL8_NET_INPUT_HISTORY_SIZE; i++) {
@ -231,22 +224,19 @@ u64 pxl8_net_player_id(const pxl8_net* net) {
return net->snapshot.player_id;
}
bool pxl8_net_poll(pxl8_net* net) {
if (!net || !net->connected) return false;
usize len = pxl8_net_recv(net, net->recv_buf, sizeof(net->recv_buf));
static bool dispatch_message(pxl8_net* net, const u8* data, usize len) {
if (len < sizeof(pxl8_msg_header)) return false;
pxl8_msg_header hdr;
usize offset = pxl8_protocol_deserialize_header(net->recv_buf, len, &hdr);
usize offset = pxl8_protocol_deserialize_header(data, len, &hdr);
if (hdr.type == PXL8_MSG_CHUNK) {
if (!net->chunk_cache) return false;
pxl8_chunk_msg_header chunk_hdr;
offset += pxl8_protocol_deserialize_chunk_msg_header(net->recv_buf + offset, len - offset, &chunk_hdr);
offset += pxl8_protocol_deserialize_chunk_msg_header(data + offset, len - offset, &chunk_hdr);
const u8* payload = net->recv_buf + offset;
const u8* payload = data + offset;
usize payload_len = chunk_hdr.payload_size;
if (payload_len > len - offset) {
payload_len = len - offset;
@ -258,23 +248,23 @@ bool pxl8_net_poll(pxl8_net* net) {
if (hdr.type == PXL8_MSG_CHUNK_ENTER) {
pxl8_chunk_enter_msg chunk_msg;
pxl8_protocol_deserialize_chunk_enter(net->recv_buf + offset, len - offset, &chunk_msg);
net->chunk_id = chunk_msg.chunk_id;
net->chunk_type = chunk_msg.chunk_type;
pxl8_debug("[CLIENT] Received CHUNK_ENTER type=%u id=%u", chunk_msg.chunk_type, chunk_msg.chunk_id);
pxl8_protocol_deserialize_chunk_enter(data + offset, len - offset, &chunk_msg);
net->chunk_cx = chunk_msg.cx;
net->chunk_cz = chunk_msg.cz;
net->has_chunk = true;
pxl8_debug("[CLIENT] Received CHUNK_ENTER cx=%d cz=%d", chunk_msg.cx, chunk_msg.cz);
return true;
}
if (hdr.type == PXL8_MSG_CHUNK_EXIT) {
net->chunk_id = 0;
net->chunk_type = 0;
net->has_chunk = false;
return true;
}
if (hdr.type != PXL8_MSG_SNAPSHOT) return false;
pxl8_snapshot_header snap;
offset += pxl8_protocol_deserialize_snapshot_header(net->recv_buf + offset, len - offset, &snap);
offset += pxl8_protocol_deserialize_snapshot_header(data + offset, len - offset, &snap);
if (snap.tick <= net->highest_tick) return false;
@ -290,10 +280,20 @@ bool pxl8_net_poll(pxl8_net* net) {
for (u16 i = 0; i < count; i++) {
offset += pxl8_protocol_deserialize_entity_state(
net->recv_buf + offset, len - offset, &net->entities[i]);
data + offset, len - offset, &net->entities[i]);
}
if (net->world) {
return true;
}
bool pxl8_net_poll(pxl8_net* net) {
if (!net || !net->connected) return false;
usize len = pxl8_net_recv(net, net->recv_buf, sizeof(net->recv_buf));
u64 prev_tick = net->highest_tick;
if (!dispatch_message(net, net->recv_buf, len)) return false;
if (net->highest_tick > prev_tick && net->world) {
pxl8_world_reconcile(net->world, net, 1.0f / 30.0f);
}
@ -377,7 +377,6 @@ u64 pxl8_net_tick(const pxl8_net* net) {
void pxl8_net_update(pxl8_net* net, f32 dt) {
if (!net) return;
net->dt = dt;
net->interp_time += dt;
}
@ -396,14 +395,19 @@ void pxl8_net_set_world(pxl8_net* net, pxl8_world* world) {
net->world = world;
}
u32 pxl8_net_chunk_id(const pxl8_net* net) {
i32 pxl8_net_chunk_cx(const pxl8_net* net) {
if (!net) return 0;
return net->chunk_id;
return net->chunk_cx;
}
u8 pxl8_net_chunk_type(const pxl8_net* net) {
if (!net) return PXL8_CHUNK_TYPE_BSP;
return net->chunk_type;
i32 pxl8_net_chunk_cz(const pxl8_net* net) {
if (!net) return 0;
return net->chunk_cz;
}
bool pxl8_net_has_chunk(const pxl8_net* net) {
if (!net) return false;
return net->has_chunk;
}
pxl8_result pxl8_net_spawn(pxl8_net* net, f32 x, f32 y, f32 z, f32 yaw, f32 pitch) {
@ -422,22 +426,6 @@ pxl8_result pxl8_net_spawn(pxl8_net* net, f32 x, f32 y, f32 z, f32 yaw, f32 pitc
return pxl8_net_send_command(net, &cmd);
}
pxl8_result pxl8_net_enter_scene(pxl8_net* net, u8 chunk_type, u32 chunk_id, f32 x, f32 y, f32 z) {
if (!net) return PXL8_ERROR_NULL_POINTER;
if (!net->connected) return PXL8_ERROR_NOT_CONNECTED;
pxl8_command_msg cmd = {0};
cmd.cmd_type = PXL8_CMD_ENTER_SCENE;
pxl8_pack_u32_be(cmd.payload, 0, chunk_id);
cmd.payload[4] = chunk_type;
pxl8_pack_f32_be(cmd.payload, 8, x);
pxl8_pack_f32_be(cmd.payload, 12, y);
pxl8_pack_f32_be(cmd.payload, 16, z);
cmd.payload_size = 20;
return pxl8_net_send_command(net, &cmd);
}
#ifdef PXL8_ASYNC_THREADS
static int pxl8_net_recv_thread(void* data) {
@ -504,64 +492,8 @@ void pxl8_net_packet_free(pxl8_packet* pkt) {
}
bool pxl8_net_process_packet(pxl8_net* net, const pxl8_packet* pkt) {
if (!net || !pkt || pkt->len < sizeof(pxl8_msg_header)) return false;
pxl8_msg_header hdr;
usize offset = pxl8_protocol_deserialize_header(pkt->data, pkt->len, &hdr);
if (hdr.type == PXL8_MSG_CHUNK) {
if (!net->chunk_cache) return false;
pxl8_chunk_msg_header chunk_hdr;
offset += pxl8_protocol_deserialize_chunk_msg_header(pkt->data + offset, pkt->len - offset, &chunk_hdr);
const u8* payload = pkt->data + offset;
usize payload_len = chunk_hdr.payload_size;
if (payload_len > pkt->len - offset) {
payload_len = pkt->len - offset;
}
pxl8_world_chunk_cache_receive(net->chunk_cache, &chunk_hdr, payload, payload_len);
return true;
}
if (hdr.type == PXL8_MSG_CHUNK_ENTER) {
pxl8_chunk_enter_msg chunk_msg;
pxl8_protocol_deserialize_chunk_enter(pkt->data + offset, pkt->len - offset, &chunk_msg);
net->chunk_id = chunk_msg.chunk_id;
net->chunk_type = chunk_msg.chunk_type;
return true;
}
if (hdr.type == PXL8_MSG_CHUNK_EXIT) {
net->chunk_id = 0;
net->chunk_type = 0;
return true;
}
if (hdr.type != PXL8_MSG_SNAPSHOT) return false;
pxl8_snapshot_header snap;
offset += pxl8_protocol_deserialize_snapshot_header(pkt->data + offset, pkt->len - offset, &snap);
if (snap.tick <= net->highest_tick) return false;
memcpy(net->prev_entities, net->entities, sizeof(net->entities));
net->prev_snapshot = net->snapshot;
net->highest_tick = snap.tick;
net->snapshot = snap;
net->interp_time = 0.0f;
u16 count = snap.entity_count;
if (count > PXL8_MAX_SNAPSHOT_ENTITIES) count = PXL8_MAX_SNAPSHOT_ENTITIES;
for (u16 i = 0; i < count; i++) {
offset += pxl8_protocol_deserialize_entity_state(
pkt->data + offset, pkt->len - offset, &net->entities[i]);
}
return true;
if (!net || !pkt) return false;
return dispatch_message(net, pkt->data, pkt->len);
}
#endif