use std::ops::{Deref, DerefMut};
use std::sync::{Arc, RwLock, Mutex};
use std::time::{Instant, Duration};
use std::error::Error;
use std::fmt::{Debug, Formatter};
use crossbeam_channel::{Sender, Receiver, unbounded, bounded};
use hecs::EntityBuilder;
use thiserror::Error;
use super::chunk::{Chunk, ChunkHeight};
use crate::world::chunk::ChunkStatus;
use crate::world::level::LevelEnv;
use crate::block::BlockState;
#[derive(Error, Debug)]
pub enum LevelSourceError {
#[error("The required chunk position is not supported by the source.")]
UnsupportedChunkPosition,
#[error("Chunk loading is not supported by the targeted source.")]
UnsupportedChunkLoad,
#[error("Chunk saving is not supported by the targeted source.")]
UnsupportedChunkSave,
#[error("Custom source error: {0}")]
Custom(Box<dyn Error + Send>)
}
impl LevelSourceError {
pub fn new_custom(err: impl Error + Send + 'static) -> Self {
Self::Custom(Box::new(err))
}
}
pub trait LevelSource {
fn request_chunk_load(&mut self, req: ChunkLoadRequest) -> Result<(), (LevelSourceError, ChunkLoadRequest)> {
Err((LevelSourceError::UnsupportedChunkLoad, req))
}
fn poll_chunk(&mut self) -> Option<Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)>> {
None
}
#[allow(unused_variables)]
fn request_chunk_save(&mut self, req: ChunkSaveRequest) -> Result<(), LevelSourceError> {
Err(LevelSourceError::UnsupportedChunkSave)
}
}
#[derive(Clone, Debug)]
pub struct ChunkLoadRequest {
pub env: Arc<LevelEnv>,
pub height: ChunkHeight,
pub cx: i32,
pub cz: i32,
}
impl ChunkLoadRequest {
pub fn build_chunk(&self) -> Chunk {
Chunk::new(Arc::clone(&self.env), self.height, self.cx, self.cz)
}
pub fn build_proto_chunk(&self) -> ProtoChunk {
ProtoChunk {
inner: Box::new(self.build_chunk()),
proto_entities: Vec::new(),
dirty: false
}
}
}
#[derive(Clone)]
pub struct ChunkSaveRequest {
pub cx: i32,
pub cz: i32,
pub chunk: Arc<RwLock<Chunk>>
}
pub struct ProtoChunk {
pub(super) inner: Box<Chunk>,
pub(super) proto_entities: Vec<(EntityBuilder, Vec<usize>)>,
pub dirty: bool
}
impl ProtoChunk {
pub fn add_proto_entity(&mut self, entity_builder: EntityBuilder) -> usize {
let idx = self.proto_entities.len();
self.proto_entities.push((entity_builder, Vec::new()));
idx
}
pub fn add_proto_entity_passengers(&mut self, host_index: usize, passenger_index: usize) {
self.proto_entities[host_index].1.push(passenger_index);
}
}
impl Deref for ProtoChunk {
type Target = Chunk;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for ProtoChunk {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Debug for ProtoChunk {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProtoChunk")
.field("dirty", &self.dirty)
.field("proto_entities", &self.proto_entities.len())
.finish_non_exhaustive()
}
}
pub struct NullLevelSource;
impl LevelSource for NullLevelSource {}
pub struct LoadOrGenLevelSource<L, G> {
loader: L,
generator: G
}
impl<L, G> LoadOrGenLevelSource<L, G>
where
L: LevelSource,
G: LevelSource,
{
pub fn new(loader: L, generator: G) -> Self {
Self {
loader,
generator
}
}
}
impl<L, G> LevelSource for LoadOrGenLevelSource<L, G>
where
L: LevelSource,
G: LevelSource,
{
fn request_chunk_load(&mut self, req: ChunkLoadRequest) -> Result<(), (LevelSourceError, ChunkLoadRequest)> {
match self.loader.request_chunk_load(req) {
Err((LevelSourceError::UnsupportedChunkPosition, info)) => {
self.generator.request_chunk_load(info)
}
Err(e) => Err(e),
_ => Ok(())
}
}
fn poll_chunk(&mut self) -> Option<Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)>> {
while let Some(res) = self.loader.poll_chunk() {
match res {
Err((LevelSourceError::UnsupportedChunkPosition, chunk_info)) => {
match self.generator.request_chunk_load(chunk_info) {
Err(e) => return Some(Err(e)),
Ok(_) => {}
}
},
res => return Some(res)
}
}
let mut res = self.generator.poll_chunk();
if let Some(Ok(ref mut proto_chunk)) = res {
proto_chunk.dirty = true;
}
res
}
fn request_chunk_save(&mut self, req: ChunkSaveRequest) -> Result<(), LevelSourceError> {
self.loader.request_chunk_save(req)
}
}
pub trait LevelGenerator {
fn generate(&mut self, info: ChunkLoadRequest) -> Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)>;
}
pub trait LevelGeneratorBuilder {
type Generator: LevelGenerator;
fn build(&mut self) -> Self::Generator;
}
pub struct WorkerGenLevelSource {
request_sender: Sender<ChunkLoadRequest>,
result_receiver: Receiver<Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)>>,
}
impl WorkerGenLevelSource {
pub fn new<B>(generator_builder: B, workers_count: usize) -> Self
where
B: LevelGeneratorBuilder + Send + Sync + 'static
{
let (
request_sender,
request_receiver
) = unbounded();
let (
result_sender,
result_receiver
) = bounded(workers_count * 128);
let generator_builder = Arc::new(Mutex::new(generator_builder));
for i in 0..workers_count {
let request_receiver = request_receiver.clone();
let result_sender = result_sender.clone();
let generator_builder = Arc::clone(&generator_builder);
std::thread::Builder::new()
.name(format!("Level generator worker #{}", i))
.spawn(move || {
let worker = {
LevelGeneratorSourceWorker {
generator: generator_builder.lock().unwrap().build(),
request_receiver,
result_sender,
total_count: 0,
total_duration: Duration::default(),
}
};
worker.run()
})
.unwrap();
}
Self {
request_sender,
result_receiver
}
}
}
impl LevelSource for WorkerGenLevelSource {
fn request_chunk_load(&mut self, req: ChunkLoadRequest) -> Result<(), (LevelSourceError, ChunkLoadRequest)> {
self.request_sender.send(req).unwrap();
Ok(())
}
fn poll_chunk(&mut self) -> Option<Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)>> {
self.result_receiver.try_recv().ok()
}
}
struct LevelGeneratorSourceWorker<G> {
generator: G,
request_receiver: Receiver<ChunkLoadRequest>,
result_sender: Sender<Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)>>,
total_count: u32,
total_duration: Duration
}
impl<G> LevelGeneratorSourceWorker<G>
where
G: LevelGenerator
{
fn run(mut self) {
loop {
match self.request_receiver.recv() {
Ok(chunk_info) => {
let begin = Instant::now();
let res = self.generator.generate(chunk_info);
self.total_duration += begin.elapsed();
self.total_count += 1;
if let Err(_) = self.result_sender.send(res) {
break
}
},
Err(_) => break
}
}
}
}
#[derive(Debug, Clone)]
pub struct SuperFlatGenerator {
layers: Vec<(&'static BlockState, i32, u32)>
}
impl SuperFlatGenerator {
pub fn new() -> Self {
Self {
layers: Vec::new()
}
}
pub fn add_layer(&mut self, state: &'static BlockState, y: i32, height: u32) {
self.layers.push((state, y, height));
}
}
impl LevelGenerator for SuperFlatGenerator {
fn generate(&mut self, info: ChunkLoadRequest) -> Result<ProtoChunk, (LevelSourceError, ChunkLoadRequest)> {
let mut chunk = info.build_proto_chunk();
for &(state, y, height) in &self.layers {
for y in y..(y + height as i32) {
for x in 0..16 {
for z in 0..16 {
let _ = chunk.set_block(x, y, z, state);
}
}
}
}
chunk.set_status(ChunkStatus::Full);
Ok(chunk)
}
}