#![no_std] #![no_main] extern crate alloc; mod allocator; use core::panic::PanicInfo; use pxl8_server::*; #[global_allocator] static ALLOCATOR: allocator::Allocator = allocator::Allocator; #[panic_handler] fn panic(_info: &PanicInfo) -> ! { loop {} } 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 { let mut transport = match transport::Transport::bind(transport::DEFAULT_PORT) { Some(t) => t, None => return 1, }; let mut sim = Simulation::new(); let mut player_id: u64 = 0; let mut last_client_tick: u64 = 0; 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() }; 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 = 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); let player = sim.spawn_player(x, y, z); player_id = player.to_bits(); } } _ => {} } } 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, |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, tick: last_client_tick, time: sim.time, }; transport.send_snapshot(&header, &entities_buf[..count], sequence); sequence = sequence.wrapping_add(1); } sleep_ms(1); } }