pxl8/pxl8d/src/sim.rs

238 lines
6.4 KiB
Rust
Raw Normal View History

2026-01-25 09:26:30 -06:00
extern crate alloc;
use alloc::vec::Vec;
2026-01-31 09:31:17 -06:00
use crate::math::{Vec3, Vec3Ext, VEC3_ZERO};
use crate::pxl8::*;
2026-01-25 09:26:30 -06:00
use crate::voxel::VoxelWorld;
use crate::world::World;
2026-01-31 09:31:17 -06:00
pub type Entity = pxl8_sim_entity;
2026-01-25 09:26:30 -06:00
2026-01-31 09:31:17 -06:00
const ALIVE: u32 = PXL8_SIM_FLAG_ALIVE;
const PLAYER: u32 = PXL8_SIM_FLAG_PLAYER;
2026-01-25 09:26:30 -06:00
2026-01-31 09:31:17 -06:00
const MAX_ENTITIES: usize = 1024;
2026-01-25 09:26:30 -06:00
impl Default for Entity {
fn default() -> Self {
Self {
2026-01-31 09:31:17 -06:00
pos: VEC3_ZERO,
vel: VEC3_ZERO,
2026-01-25 09:26:30 -06:00
yaw: 0.0,
pitch: 0.0,
2026-01-31 09:31:17 -06:00
flags: 0,
kind: 0,
_pad: 0,
2026-01-25 09:26:30 -06:00
}
}
}
2026-02-02 17:48:25 -06:00
impl Clone for Entity {
fn clone(&self) -> Self {
Self {
pos: self.pos,
vel: self.vel,
yaw: self.yaw,
pitch: self.pitch,
flags: self.flags,
kind: self.kind,
_pad: self._pad,
}
}
}
2026-01-25 09:26:30 -06:00
pub struct Simulation {
pub entities: Vec<Entity>,
pub free_list: Vec<u32>,
pub player: Option<u32>,
pub tick: u64,
pub time: f32,
pub voxels: VoxelWorld,
pub world: World,
}
impl Simulation {
pub fn new() -> Self {
let mut entities = Vec::with_capacity(MAX_ENTITIES);
entities.resize(MAX_ENTITIES, Entity::default());
let mut free_list = Vec::with_capacity(MAX_ENTITIES);
for i in (0..MAX_ENTITIES).rev() {
free_list.push(i as u32);
}
Self {
entities,
free_list,
player: None,
tick: 0,
time: 0.0,
voxels: VoxelWorld::new(12345),
world: World::new(12345),
}
}
pub fn spawn(&mut self) -> Option<u32> {
let idx = self.free_list.pop()?;
self.entities[idx as usize] = Entity {
flags: ALIVE,
..Default::default()
};
Some(idx)
}
pub fn despawn(&mut self, id: u32) {
if (id as usize) < self.entities.len() {
self.entities[id as usize].flags = 0;
self.free_list.push(id);
}
}
pub fn spawn_player(&mut self, x: f32, y: f32, z: f32) -> u32 {
let id = self.spawn().unwrap_or(0);
let ent = &mut self.entities[id as usize];
ent.flags = ALIVE | PLAYER;
ent.pos = Vec3::new(x, y, z);
self.player = Some(id);
self.voxels.load_chunks_around(ent.pos, 2);
id
}
2026-01-31 09:31:17 -06:00
pub fn teleport_player(&mut self, x: f32, y: f32, z: f32) {
if let Some(id) = self.player {
let ent = &mut self.entities[id as usize];
ent.pos = Vec3::new(x, y, z);
}
}
2026-01-25 09:26:30 -06:00
pub fn step(&mut self, inputs: &[pxl8_input_msg], dt: f32) {
self.tick += 1;
self.time += dt;
for input in inputs {
self.move_player(input, dt);
}
self.integrate(dt);
}
2026-01-31 09:31:17 -06:00
fn make_sim_world(&self, pos: Vec3) -> pxl8_sim_world {
if let Some(chunk) = self.world.active() {
if let Some(bsp) = chunk.as_bsp() {
return pxl8_sim_world {
bsp: bsp.as_c_bsp(),
vxl: core::ptr::null(),
vxl_cx: 0,
vxl_cy: 0,
vxl_cz: 0,
};
}
}
let cx = VoxelWorld::world_to_chunk(pos.x);
let cy = VoxelWorld::world_to_chunk(pos.y);
let cz = VoxelWorld::world_to_chunk(pos.z);
if let Some(chunk) = self.voxels.get_chunk(cx, cy, cz) {
pxl8_sim_world {
bsp: core::ptr::null(),
vxl: chunk.chunk as *const _,
vxl_cx: cx,
vxl_cy: cy,
vxl_cz: cz,
}
} else {
pxl8_sim_world {
bsp: core::ptr::null(),
vxl: core::ptr::null(),
vxl_cx: 0,
vxl_cy: 0,
vxl_cz: 0,
}
2026-01-25 09:26:30 -06:00
}
}
fn integrate(&mut self, dt: f32) {
for i in 0..self.entities.len() {
let ent = &self.entities[i];
if ent.flags & ALIVE == 0 || ent.flags & PLAYER != 0 {
continue;
}
2026-01-31 09:31:17 -06:00
let world = self.make_sim_world(ent.pos);
2026-01-25 09:26:30 -06:00
let ent = &mut self.entities[i];
2026-01-31 09:31:17 -06:00
unsafe {
pxl8_sim_integrate(ent, &world, dt);
}
2026-01-25 09:26:30 -06:00
}
}
fn move_player(&mut self, input: &pxl8_input_msg, dt: f32) {
let Some(id) = self.player else { return };
2026-01-31 09:31:17 -06:00
let ent = &self.entities[id as usize];
2026-01-25 09:26:30 -06:00
if ent.flags & ALIVE == 0 {
return;
}
2026-01-31 09:31:17 -06:00
let world = self.make_sim_world(ent.pos);
2026-01-25 09:26:30 -06:00
let ent = &mut self.entities[id as usize];
2026-01-31 09:31:17 -06:00
unsafe {
pxl8_sim_move_player(ent, input, &world, dt);
2026-01-25 09:26:30 -06:00
}
}
pub fn generate_snapshot<F>(&self, _player_id: u64, mut writer: F)
where
F: FnMut(&pxl8_entity_state),
{
for (i, ent) in self.entities.iter().enumerate() {
if ent.flags & ALIVE == 0 {
continue;
}
let mut state = pxl8_entity_state {
entity_id: i as u64,
userdata: [0u8; 56],
};
let bytes = &mut state.userdata;
bytes[0..4].copy_from_slice(&ent.pos.x.to_be_bytes());
bytes[4..8].copy_from_slice(&ent.pos.y.to_be_bytes());
bytes[8..12].copy_from_slice(&ent.pos.z.to_be_bytes());
bytes[12..16].copy_from_slice(&ent.yaw.to_be_bytes());
bytes[16..20].copy_from_slice(&ent.pitch.to_be_bytes());
bytes[20..24].copy_from_slice(&ent.vel.x.to_be_bytes());
bytes[24..28].copy_from_slice(&ent.vel.y.to_be_bytes());
bytes[28..32].copy_from_slice(&ent.vel.z.to_be_bytes());
bytes[32..36].copy_from_slice(&ent.flags.to_be_bytes());
bytes[36..38].copy_from_slice(&ent.kind.to_be_bytes());
writer(&state);
}
}
pub fn entity_count(&self) -> usize {
self.entities.iter().filter(|e| e.flags & ALIVE != 0).count()
}
pub fn get_player_position(&self, player_id: u64) -> Option<(f32, f32, f32)> {
let idx = player_id as usize;
if idx < self.entities.len() {
let ent = &self.entities[idx];
if ent.flags & ALIVE != 0 && ent.flags & PLAYER != 0 {
return Some((ent.pos.x, ent.pos.y, ent.pos.z));
}
}
None
}
}
impl Default for Simulation {
fn default() -> Self {
Self::new()
}
}