1
Fork 0

fix the fact that I barely use any memory (in progress)

This commit is contained in:
Andy Killorin 2023-12-25 22:04:58 -06:00
parent 48d7ea164d
commit 8adcfdea38
Signed by: ank
GPG key ID: B6241CA3B552BCA4
5 changed files with 113 additions and 30 deletions

View file

@ -40,3 +40,4 @@ opentelemetry = "0.21.0"
tracing-opentelemetry = "0.22"
opentelemetry-jaeger = { version = "0.20", features = ["rt-tokio"] }
opentelemetry_sdk = { version = "0.21.1", features = ["trace"] }
memoize = "0.4.2"

View file

@ -1,46 +1,84 @@
use std::{sync::Arc, ops::Sub};
use anyhow::Ok;
use nalgebra::Vector3;
use rstar::{PointDistance, RTree, RTreeObject, AABB};
use rstar::{PointDistance, RTree, RTreeObject, AABB, Envelope};
use serde::{Deserialize, Serialize};
use tokio::sync::{RwLock, OwnedRwLockReadGuard};
use memoize::memoize;
use crate::{turtle::TurtleCommand, paths::{self, TRANSPARENT}};
pub type WorldReadLock = OwnedRwLockReadGuard<RTree<Block>>;
const CHUNK_SIZE: usize = 16;
const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
const CHUNK_VEC: Vec3 = Vec3::new(CHUNK_SIZE as i32, CHUNK_SIZE as i32, CHUNK_SIZE as i32);
#[derive(Clone)]
pub struct World {
state: Arc<RwLock<RTree<Block>>>, // interior mutability to get around the
// questionable architecture of this project
}
pub struct World(RTree<Chunk>);
impl World {
pub fn get(&self, block: Vec3) -> Option<Block> {
let chunk = block.component_div(&CHUNK_VEC);
let chunk = self.get_chunk(chunk)?;
chunk.get(block)
}
pub fn set(&mut self, block: Block) -> Option<Block> {
let chunk = block.pos.component_div(&CHUNK_VEC);
let chunk = self.get_chunk(chunk)?;
chunk.set(block)
}
fn get_chunk(&self, block: Vec3) -> &Chunk {
let block = block.component_div(&CHUNK_VEC);
if let Some(chunk) = self.0.locate_at_point(&block.into()) {
return chunk;
}
self.0.insert(Chunk::new(block));
&Chunk::new(block)
}
pub fn get_bulk<const COUNT:usize>(&self, blocks: [Vec3;COUNT]) -> [Option<&Block>;COUNT] {
let mut chunk: Option<&Chunk> = None;
blocks.iter().map(|b|{
if !chunk.is_some_and(|c| c.contains(b)) {
chunk = Some(self.get_chunk(b));
}
chunk.unwrap().get(b)
}).collect()
}
}
#[derive(Clone)]
pub struct SharedWorld {
state: Arc<RwLock<World>>, // interior mutability to get around the
// questionable architecture of this project
}
impl SharedWorld {
pub fn new() -> Self { Self { state: Arc::new(RwLock::new(RTree::new())) } }
pub fn from_tree(tree: RTree<Block>) -> Self { Self { state: Arc::new(RwLock::new(tree)) } }
pub async fn to_tree(self) -> RTree<Block> { self.state.write().await.to_owned() }
pub async fn tree(&self) -> RTree<Block> { self.state.read().await.clone() }
pub fn from_world(tree: World) -> Self { Self { state: Arc::new(RwLock::new(tree)) } }
pub async fn get(&self, block: Vec3) -> Option<Block> {
self.state.read().await.locate_at_point(&block.into()).map(|b| b.to_owned())
self.state.read().await.get(block)
}
pub async fn set(&self, block: Block) {
self.state.write().await.remove_at_point(&block.pos.into());
self.state.write().await.insert(block);
self.state.write().await.set(block);
}
/// Returns true if a known non-traversable block exists at the point
pub async fn occupied(&self, block: Vec3) -> bool {
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| !TRANSPARENT.contains(&b.name.as_str()))
self.get(block).await.is_some_and(|b| !TRANSPARENT.contains(&b.name.as_str()))
}
/// Returns true if a "garbage" block exists at the given point which you are free to destroy
pub async fn garbage(&self, block: Vec3) -> bool {
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| paths::difficulty(&b.name).is_some())
self.get(block).await.is_some_and(|b| paths::difficulty(&b.name).is_some())
}
pub async fn lock(self) -> WorldReadLock {
pub async fn lock(self) -> OwnedRwLockReadGuard<World> {
self.state.read_owned().await
}
}
@ -51,7 +89,50 @@ pub struct Block {
pub pos: Vec3,
}
impl RTreeObject for Block {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Chunk {
pos: Vec3, /// position in chunk coordinates (world/16)
data: [[[Option<Block>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]
}
impl Chunk {
fn new(pos: Vec3) -> Self {
Self {
pos,
data:[[[None;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]
}
}
fn set(&mut self, pos: Block) -> Result<(), ()> {
let chunk = self.pos.component_mul(&CHUNK_VEC);
let local = pos.pos - chunk;
if !self.contains(&pos) {
return Err(());
}
self.data[local.x][local.y][local.z] = pos;
Ok(())
}
fn get(&self, pos: Position) -> Option<&Block> {
let chunk = self.pos.component_mul(&CHUNK_VEC);
let local = pos.pos - chunk;
if !self.contains(&pos) {
return None;
}
Ok(self.data[local.x][local.y][local.z])
}
fn contains(&self, pos:&Position) -> bool {
let chunk = self.pos.component_mul(&CHUNK_VEC);
let local = pos.pos - chunk;
AABB::from_corners(chunk, chunk+CHUNK_VEC).contains_point(&local)
}
}
impl RTreeObject for Chunk {
type Envelope = AABB<[i32; 3]>;
fn envelope(&self) -> Self::Envelope {
@ -59,7 +140,7 @@ impl RTreeObject for Block {
}
}
impl PointDistance for Block {
impl PointDistance for Chunk {
fn distance_2(&self, point: &[i32; 3]) -> i32 {
(self.pos - Vec3::from(*point)).abs().sum()
}

View file

@ -8,7 +8,7 @@ use axum::{
routing::{get},
Router,
};
use blocks::{World, Position, };
use blocks::{SharedWorld, Position, };
use depot::Depots;
use opentelemetry::global;
use opentelemetry_sdk::{runtime::Tokio, trace::BatchConfig};
@ -220,7 +220,7 @@ struct SavedState {
struct LiveState {
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
tasks: Scheduler,
world: blocks::World,
world: blocks::SharedWorld,
depots: Depots,
started: Instant,
kill: watch::Sender<bool>,
@ -244,7 +244,7 @@ impl LiveState {
};
let depots = Depots::from_vec(save.depots);
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: scheduler, world: World::from_tree(save.world),
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: scheduler, world: SharedWorld::from_tree(save.world),
depots,
started: Instant::now(),
kill:sender,

View file

@ -1,6 +1,7 @@
use crate::{
blocks::{World, Position, Direction, Vec3, WorldReadLock},
blocks::{SharedWorld, Position, Direction, Vec3, World, nearest, Block},
};
use rstar::{AABB, Envelope};
use tokio::task::spawn_blocking;
use tracing::{trace, error};
use pathfinding::prelude::astar;
@ -8,7 +9,7 @@ use pathfinding::prelude::astar;
const LOOKUP_LIMIT: usize = 10_000_000;
#[tracing::instrument(skip(world))]
pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option<Vec<Position>> {
pub async fn route_facing(from: Position, to: Vec3, world: &SharedWorld) -> Option<Vec<Position>> {
let facing = move |p: &Position| {
let ahead = p.dir.unit() + p.pos;
let above = Vec3::y() + p.pos;
@ -19,7 +20,7 @@ pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option<Vec
}
#[tracing::instrument(skip(world))]
pub async fn route(from: Position, to: Position, world: &World) -> Option<Vec<Position>> {
pub async fn route(from: Position, to: Position, world: &SharedWorld) -> Option<Vec<Position>> {
trace!("routing from {from:?} to {to:?}");
// attempt at not crashing by looking infinitely into the abyss
if world.get(to.pos).await
@ -30,7 +31,7 @@ pub async fn route(from: Position, to: Position, world: &World) -> Option<Vec<Po
route_to(from, to.pos, move |p| p == &to, world).await
}
async fn route_to<D>(from: Position, to: Vec3, mut done: D, world: &World) -> Option<Vec<Position>>
async fn route_to<D>(from: Position, to: Vec3, mut done: D, world: &SharedWorld) -> Option<Vec<Position>>
where D: FnMut(&Position) -> bool + Send + 'static {
// lock once, we'll be doing a lot of lookups
let world = world.clone().lock().await;
@ -62,14 +63,14 @@ where D: FnMut(&Position) -> bool + Send + 'static {
}
}
fn next(from: &Position, world: &WorldReadLock) -> Vec<(Position, u32)> {
fn next(from: &Position, world: &World) -> Vec<(Position, u32)> {
let mut vec: Vec<(Position, u32)> = Vec::new();
fn insert(
vec: &mut Vec<(Position, u32)>,
point: Vec3,
orientation: Direction,
world: &WorldReadLock,
world: &World,
unknown: Option<u32>,
) {
world

View file

@ -2,7 +2,7 @@ use crate::blocks::Block;
use crate::blocks::Direction;
use crate::blocks::Position;
use crate::blocks::Vec3;
use crate::blocks::World;
use crate::blocks::SharedWorld;
use crate::depot::Depots;
use crate::paths::route_facing;
@ -144,7 +144,7 @@ impl Turtle {
#[derive(Clone)]
pub struct TurtleCommander {
sender: Arc<Sender>,
world: World,
world: SharedWorld,
depots: Depots,
// everything below is best-effort
// TODO: make not bad
@ -221,7 +221,7 @@ impl TurtleCommander {
self.max_fuel.load(std::sync::atomic::Ordering::SeqCst)
}
pub fn world(&self) -> World {
pub fn world(&self) -> SharedWorld {
self.world.clone()
}