stream world data from pxl8d to pxl8
This commit is contained in:
parent
39ee0fefb7
commit
a71a9840b2
55 changed files with 5290 additions and 2131 deletions
325
pxl8d/src/main.rs
Normal file
325
pxl8d/src/main.rs
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use pxl8d::*;
|
||||
use pxl8d::chunk::ChunkId;
|
||||
use pxl8d::chunk::stream::ClientChunkState;
|
||||
|
||||
const TICK_RATE: u64 = 30;
|
||||
const TICK_NS: u64 = 1_000_000_000 / TICK_RATE;
|
||||
|
||||
#[cfg(unix)]
|
||||
fn get_time_ns() -> u64 {
|
||||
let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
|
||||
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) };
|
||||
(ts.tv_sec as u64) * 1_000_000_000 + (ts.tv_nsec as u64)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_time_ns() -> u64 {
|
||||
use windows_sys::Win32::System::Performance::*;
|
||||
static mut FREQ: i64 = 0;
|
||||
unsafe {
|
||||
if FREQ == 0 {
|
||||
QueryPerformanceFrequency(&mut FREQ);
|
||||
}
|
||||
let mut count: i64 = 0;
|
||||
QueryPerformanceCounter(&mut count);
|
||||
((count as u128 * 1_000_000_000) / FREQ as u128) as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn sleep_ms(ms: u64) {
|
||||
let ts = libc::timespec {
|
||||
tv_sec: (ms / 1000) as i64,
|
||||
tv_nsec: ((ms % 1000) * 1_000_000) as i64,
|
||||
};
|
||||
unsafe { libc::nanosleep(&ts, core::ptr::null_mut()) };
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn sleep_ms(ms: u64) {
|
||||
use windows_sys::Win32::System::Threading::Sleep;
|
||||
unsafe { Sleep(ms as u32) };
|
||||
}
|
||||
|
||||
fn extract_spawn_position(payload: &[u8]) -> (f32, f32, f32, f32, f32) {
|
||||
let x = f32::from_be_bytes([payload[0], payload[1], payload[2], payload[3]]);
|
||||
let y = f32::from_be_bytes([payload[4], payload[5], payload[6], payload[7]]);
|
||||
let z = f32::from_be_bytes([payload[8], payload[9], payload[10], payload[11]]);
|
||||
let yaw = f32::from_be_bytes([payload[12], payload[13], payload[14], payload[15]]);
|
||||
let pitch = f32::from_be_bytes([payload[16], payload[17], payload[18], payload[19]]);
|
||||
(x, y, z, yaw, pitch)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 {
|
||||
log::init();
|
||||
let mut transport = match transport::Transport::bind(transport::DEFAULT_PORT) {
|
||||
Some(t) => {
|
||||
pxl8_info!("[SERVER] Bound to port 7777");
|
||||
t
|
||||
}
|
||||
None => {
|
||||
pxl8_error!("[SERVER] Failed to bind to port 7777");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
let mut sim = Simulation::new();
|
||||
let mut player_id: Option<u64> = None;
|
||||
let mut last_client_tick: u64 = 0;
|
||||
let mut client_chunks = ClientChunkState::new();
|
||||
|
||||
let mut sequence: u32 = 0;
|
||||
let mut last_tick = get_time_ns();
|
||||
let mut entities_buf = [protocol::pxl8_entity_state {
|
||||
entity_id: 0,
|
||||
userdata: [0u8; 56],
|
||||
}; 64];
|
||||
let mut inputs_buf: [protocol::pxl8_input_msg; 16] = unsafe { core::mem::zeroed() };
|
||||
|
||||
pxl8_debug!("[SERVER] Entering main loop");
|
||||
loop {
|
||||
let now = get_time_ns();
|
||||
let elapsed = now.saturating_sub(last_tick);
|
||||
|
||||
if elapsed >= TICK_NS {
|
||||
last_tick = now;
|
||||
let dt = (elapsed as f32) / 1_000_000_000.0;
|
||||
|
||||
let mut latest_input: Option<protocol::pxl8_input_msg> = None;
|
||||
while let Some(msg_type) = transport.recv() {
|
||||
match msg_type {
|
||||
x if x == protocol::pxl8_msg_type::PXL8_MSG_INPUT as u8 => {
|
||||
latest_input = Some(transport.get_input());
|
||||
}
|
||||
x if x == protocol::pxl8_msg_type::PXL8_MSG_COMMAND as u8 => {
|
||||
let cmd = transport.get_command();
|
||||
if cmd.cmd_type == protocol::pxl8_cmd_type::PXL8_CMD_SPAWN_ENTITY as u16 {
|
||||
let (x, y, z, _yaw, _pitch) = extract_spawn_position(&cmd.payload);
|
||||
player_id = Some(sim.spawn_player(x, y, z) as u64);
|
||||
|
||||
pxl8_debug!("[SERVER] Spawn command received, generating BSP...");
|
||||
sim.world.generate_bsp(1, None);
|
||||
sim.world.set_active(ChunkId::Bsp(1));
|
||||
client_chunks.request(ChunkId::Bsp(1));
|
||||
pxl8_debug!("[SERVER] Sending CHUNK_ENTER for BSP id=1");
|
||||
transport.send_chunk_enter(1, transport::CHUNK_TYPE_BSP, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(input) = latest_input {
|
||||
last_client_tick = input.tick;
|
||||
inputs_buf[0] = input;
|
||||
sim.step(&inputs_buf[..1], dt);
|
||||
} else {
|
||||
sim.step(&[], dt);
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
sim.generate_snapshot(player_id.unwrap_or(u64::MAX), |state| {
|
||||
if count < entities_buf.len() {
|
||||
entities_buf[count] = *state;
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
let header = protocol::pxl8_snapshot_header {
|
||||
entity_count: count as u16,
|
||||
event_count: 0,
|
||||
player_id: player_id.unwrap_or(0),
|
||||
tick: last_client_tick,
|
||||
time: sim.time,
|
||||
};
|
||||
|
||||
transport.send_snapshot(&header, &entities_buf[..count], sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
|
||||
if let Some(pid) = player_id {
|
||||
if let Some(player) = sim.get_player_position(pid) {
|
||||
let pos = Vec3 { x: player.0, y: player.1, z: player.2 };
|
||||
|
||||
if sim.world.active().is_some() {
|
||||
while let Some(chunk_id) = client_chunks.next_pending() {
|
||||
match chunk_id {
|
||||
ChunkId::Bsp(id) => {
|
||||
pxl8_debug!("[SERVER] Processing pending BSP chunk");
|
||||
if let Some(chunk) = sim.world.get(&chunk_id) {
|
||||
if let Some(bsp) = chunk.as_bsp() {
|
||||
let msgs = bsp_to_messages(bsp, id, chunk.version());
|
||||
pxl8_debug!("[SERVER] BSP serialized, queueing messages");
|
||||
client_chunks.queue_messages(msgs);
|
||||
client_chunks.mark_sent(chunk_id, chunk.version());
|
||||
}
|
||||
}
|
||||
}
|
||||
ChunkId::Vxl(cx, cy, cz) => {
|
||||
if let Some(chunk) = sim.voxels.get_chunk(cx, cy, cz) {
|
||||
let msgs = transport::ChunkMessage::from_voxel(chunk, 1);
|
||||
client_chunks.queue_messages(msgs);
|
||||
client_chunks.mark_sent(chunk_id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..4 {
|
||||
if let Some(msg) = client_chunks.next_message() {
|
||||
transport.send_chunk(&msg, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client_chunks.request_vxl_radius(pos, 2, &sim.voxels);
|
||||
|
||||
for _ in 0..2 {
|
||||
if let Some(chunk_id) = client_chunks.next_pending() {
|
||||
if let ChunkId::Vxl(cx, cy, cz) = chunk_id {
|
||||
if let Some(chunk) = sim.voxels.get_chunk(cx, cy, cz) {
|
||||
let msgs = transport::ChunkMessage::from_voxel(chunk, 1);
|
||||
for msg in &msgs {
|
||||
transport.send_chunk(msg, sequence);
|
||||
sequence = sequence.wrapping_add(1);
|
||||
}
|
||||
client_chunks.mark_sent(chunk_id, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn bsp_to_messages(bsp: &bsp::Bsp, id: u32, version: u32) -> alloc::vec::Vec<transport::ChunkMessage> {
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pxl8_debug!("[SERVER] bsp_to_messages: serializing BSP");
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
let num_verts = bsp.vertices.len();
|
||||
let num_edges = bsp.edges.len();
|
||||
let num_faces = bsp.faces.len();
|
||||
let num_planes = bsp.planes.len();
|
||||
let num_nodes = bsp.nodes.len();
|
||||
let num_leafs = bsp.leafs.len();
|
||||
let num_surfedges = bsp.surfedges.len();
|
||||
|
||||
data.extend_from_slice(&(num_verts as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(num_edges as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(num_faces as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(num_planes as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(num_nodes as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(num_leafs as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(num_surfedges as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(bsp.marksurfaces.len() as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(bsp.cell_portals.len() as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(bsp.visdata.len() as u32).to_be_bytes());
|
||||
data.extend_from_slice(&(bsp.vertex_lights.len() as u32).to_be_bytes());
|
||||
|
||||
for v in &bsp.vertices {
|
||||
data.extend_from_slice(&v.position.x.to_be_bytes());
|
||||
data.extend_from_slice(&v.position.y.to_be_bytes());
|
||||
data.extend_from_slice(&v.position.z.to_be_bytes());
|
||||
}
|
||||
|
||||
for e in &bsp.edges {
|
||||
data.extend_from_slice(&e.vertex[0].to_be_bytes());
|
||||
data.extend_from_slice(&e.vertex[1].to_be_bytes());
|
||||
}
|
||||
|
||||
for se in &bsp.surfedges {
|
||||
data.extend_from_slice(&se.to_be_bytes());
|
||||
}
|
||||
|
||||
for p in &bsp.planes {
|
||||
data.extend_from_slice(&p.normal.x.to_be_bytes());
|
||||
data.extend_from_slice(&p.normal.y.to_be_bytes());
|
||||
data.extend_from_slice(&p.normal.z.to_be_bytes());
|
||||
data.extend_from_slice(&p.dist.to_be_bytes());
|
||||
data.extend_from_slice(&p.plane_type.to_be_bytes());
|
||||
}
|
||||
|
||||
for f in &bsp.faces {
|
||||
data.extend_from_slice(&f.first_edge.to_be_bytes());
|
||||
data.extend_from_slice(&f.lightmap_offset.to_be_bytes());
|
||||
data.extend_from_slice(&f.num_edges.to_be_bytes());
|
||||
data.extend_from_slice(&f.plane_id.to_be_bytes());
|
||||
data.extend_from_slice(&f.side.to_be_bytes());
|
||||
data.extend_from_slice(&f.styles);
|
||||
data.extend_from_slice(&f.material_id.to_be_bytes());
|
||||
data.extend_from_slice(&f.aabb_min.x.to_be_bytes());
|
||||
data.extend_from_slice(&f.aabb_min.y.to_be_bytes());
|
||||
data.extend_from_slice(&f.aabb_min.z.to_be_bytes());
|
||||
data.extend_from_slice(&f.aabb_max.x.to_be_bytes());
|
||||
data.extend_from_slice(&f.aabb_max.y.to_be_bytes());
|
||||
data.extend_from_slice(&f.aabb_max.z.to_be_bytes());
|
||||
}
|
||||
|
||||
for n in &bsp.nodes {
|
||||
data.extend_from_slice(&n.children[0].to_be_bytes());
|
||||
data.extend_from_slice(&n.children[1].to_be_bytes());
|
||||
data.extend_from_slice(&n.first_face.to_be_bytes());
|
||||
data.extend_from_slice(&n.maxs[0].to_be_bytes());
|
||||
data.extend_from_slice(&n.maxs[1].to_be_bytes());
|
||||
data.extend_from_slice(&n.maxs[2].to_be_bytes());
|
||||
data.extend_from_slice(&n.mins[0].to_be_bytes());
|
||||
data.extend_from_slice(&n.mins[1].to_be_bytes());
|
||||
data.extend_from_slice(&n.mins[2].to_be_bytes());
|
||||
data.extend_from_slice(&n.num_faces.to_be_bytes());
|
||||
data.extend_from_slice(&n.plane_id.to_be_bytes());
|
||||
}
|
||||
|
||||
for l in &bsp.leafs {
|
||||
data.extend_from_slice(&l.ambient_level);
|
||||
data.extend_from_slice(&l.contents.to_be_bytes());
|
||||
data.extend_from_slice(&l.first_marksurface.to_be_bytes());
|
||||
data.extend_from_slice(&l.maxs[0].to_be_bytes());
|
||||
data.extend_from_slice(&l.maxs[1].to_be_bytes());
|
||||
data.extend_from_slice(&l.maxs[2].to_be_bytes());
|
||||
data.extend_from_slice(&l.mins[0].to_be_bytes());
|
||||
data.extend_from_slice(&l.mins[1].to_be_bytes());
|
||||
data.extend_from_slice(&l.mins[2].to_be_bytes());
|
||||
data.extend_from_slice(&l.num_marksurfaces.to_be_bytes());
|
||||
data.extend_from_slice(&l.visofs.to_be_bytes());
|
||||
}
|
||||
|
||||
for ms in &bsp.marksurfaces {
|
||||
data.extend_from_slice(&ms.to_be_bytes());
|
||||
}
|
||||
|
||||
for cp in &bsp.cell_portals {
|
||||
data.push(cp.num_portals);
|
||||
data.push(0);
|
||||
data.push(0);
|
||||
data.push(0);
|
||||
for i in 0..4 {
|
||||
data.extend_from_slice(&cp.portals[i].x0.to_be_bytes());
|
||||
data.extend_from_slice(&cp.portals[i].z0.to_be_bytes());
|
||||
data.extend_from_slice(&cp.portals[i].x1.to_be_bytes());
|
||||
data.extend_from_slice(&cp.portals[i].z1.to_be_bytes());
|
||||
data.extend_from_slice(&cp.portals[i].target_leaf.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
data.extend_from_slice(&bsp.visdata);
|
||||
|
||||
for vl in &bsp.vertex_lights {
|
||||
data.extend_from_slice(&vl.to_be_bytes());
|
||||
}
|
||||
|
||||
transport::ChunkMessage::from_bsp(data, id, version)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue