change: All the work from the coffee shops and started implementing the first server
This commit is contained in:
		@@ -1,3 +1,4 @@
 | 
			
		||||
mod simple_server;
 | 
			
		||||
mod storage;
 | 
			
		||||
mod storage_server;
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
@@ -21,5 +22,6 @@ fn main() {
 | 
			
		||||
        println!("Proxy was enabled");
 | 
			
		||||
        storage_server::main();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("Hello, world!");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								src/simple_server/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/simple_server/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
pub mod server;
 | 
			
		||||
							
								
								
									
										73
									
								
								src/simple_server/server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/simple_server/server.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
use crate::storage::world::{BlockID, BlockPos, BlockRange, ChunkData, ChunkPos};
 | 
			
		||||
use crate::storage_server::StorageServer;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct SingleBlock {
 | 
			
		||||
    id: BlockID,
 | 
			
		||||
    position: BlockPos,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct MultipleBlocks {
 | 
			
		||||
    id: BlockID,
 | 
			
		||||
    range: BlockRange,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct SimpleServer {
 | 
			
		||||
    chunks: Vec<ChunkData>,
 | 
			
		||||
    single_blocks: Vec<SingleBlock>,
 | 
			
		||||
    block_ranges: Vec<MultipleBlocks>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SimpleServer {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        SimpleServer {
 | 
			
		||||
            chunks: Vec::new(),
 | 
			
		||||
            single_blocks: Vec::new(),
 | 
			
		||||
            block_ranges: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StorageServer for SimpleServer {
 | 
			
		||||
    fn change_block(&mut self, target_state: BlockID, world_position: BlockPos) {
 | 
			
		||||
        let chunk_pos = ChunkPos::from(&world_position);
 | 
			
		||||
 | 
			
		||||
        println!("Chunk position: {:?}", chunk_pos);
 | 
			
		||||
 | 
			
		||||
        for chunk in self.chunks.iter_mut() {
 | 
			
		||||
            if chunk.pos.same_location(&chunk_pos) {
 | 
			
		||||
                let current_section = &mut chunk.sections[world_position.y / 16];
 | 
			
		||||
                let chunk_array_index = current_section.index_of_block(&world_position);
 | 
			
		||||
                current_section.update_block_at_index(&target_state, chunk_array_index);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.single_blocks.push(SingleBlock {
 | 
			
		||||
            id: target_state,
 | 
			
		||||
            position: world_position,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn change_block_range(&mut self, target_stage: BlockID, start: BlockPos, end: BlockPos) {
 | 
			
		||||
        self.block_ranges.push(MultipleBlocks {
 | 
			
		||||
            id: target_stage,
 | 
			
		||||
            range: BlockRange { start, end },
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn read_block_at(&self, pos: BlockPos) -> BlockID {
 | 
			
		||||
        for block in self.single_blocks.iter() {
 | 
			
		||||
            if block.position == pos {
 | 
			
		||||
                return block.id.clone();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for blocks in self.block_ranges.iter() {
 | 
			
		||||
            if blocks.range.within_range(&pos) {
 | 
			
		||||
                return blocks.id.clone();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        BlockID::Empty
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,11 +0,0 @@
 | 
			
		||||
use crate::storage::world::{BlockID, BlockPos};
 | 
			
		||||
 | 
			
		||||
enum StorageInterface {
 | 
			
		||||
    /// `ChangeBlock` changes the block at the world position given by `world_position` to the
 | 
			
		||||
    /// target block id `BlockID`
 | 
			
		||||
    ChangeBlock {
 | 
			
		||||
        target_state: BlockID,
 | 
			
		||||
        world_position: BlockPos,
 | 
			
		||||
    },
 | 
			
		||||
    ChangeBlockRange(BlockID, BlockPos, BlockPos),
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
mod interface;
 | 
			
		||||
mod world;
 | 
			
		||||
pub mod world;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,36 @@
 | 
			
		||||
type ChunkCoordinate = isize;
 | 
			
		||||
use std::cmp::{max, min};
 | 
			
		||||
 | 
			
		||||
const SECTIONS_PER_CHUNK: usize = 16;
 | 
			
		||||
const SLICE_SIZE: usize = 16 * 16;
 | 
			
		||||
 | 
			
		||||
struct ChunkData {
 | 
			
		||||
    x: ChunkCoordinate,
 | 
			
		||||
    y: ChunkCoordinate,
 | 
			
		||||
    sections: [ChunkSection; SECTIONS_PER_CHUNK],
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ChunkPos {
 | 
			
		||||
    pub x: isize,
 | 
			
		||||
    pub z: isize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&BlockPos> for ChunkPos {
 | 
			
		||||
    fn from(value: &BlockPos) -> Self {
 | 
			
		||||
        ChunkPos {
 | 
			
		||||
            x: value.x / 16,
 | 
			
		||||
            z: value.z / 16,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ChunkPos {
 | 
			
		||||
    pub fn same_location(&self, other: &ChunkPos) -> bool {
 | 
			
		||||
        self.x == other.x && self.z == other.z
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ChunkData {
 | 
			
		||||
    pub pos: ChunkPos,
 | 
			
		||||
    pub sections: [ChunkSection; SECTIONS_PER_CHUNK],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// https://wiki.vg/Chunk_Format
 | 
			
		||||
struct ChunkSection {
 | 
			
		||||
pub struct ChunkSection {
 | 
			
		||||
    /// The number of non-empty blocks in the section. If completely full, the
 | 
			
		||||
    /// section contains a 16 x 16 x 16 cube of blocks = 4096 blocks
 | 
			
		||||
    /// If the section is empty, this is skipped
 | 
			
		||||
@@ -17,18 +38,100 @@ struct ChunkSection {
 | 
			
		||||
    /// The data for all the blocks in the chunk
 | 
			
		||||
    /// The representation for this may be different based on the number of
 | 
			
		||||
    /// non-empty blocks
 | 
			
		||||
    block_states: [BlockID; 4096],
 | 
			
		||||
    block_states: [BlockID; 16 * 16 * 16],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ChunkSection {
 | 
			
		||||
    pub fn index_of_block(&self, pos: &BlockPos) -> usize {
 | 
			
		||||
        let base_x = pos.x.rem_euclid(16) as usize;
 | 
			
		||||
        let base_y = pos.y.rem_euclid(16) as usize;
 | 
			
		||||
        let base_z = pos.z.rem_euclid(16) as usize;
 | 
			
		||||
 | 
			
		||||
        (base_y * SLICE_SIZE) + (base_z * 16) + base_x
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn update_block_at_index(&mut self, id: &BlockID, index: usize) {
 | 
			
		||||
        let existing_block = &self.block_states[index];
 | 
			
		||||
        match existing_block {
 | 
			
		||||
            BlockID::Empty => match id {
 | 
			
		||||
                BlockID::Generic => {
 | 
			
		||||
                    // If the existing block is empty, and the block that we
 | 
			
		||||
                    // are inserting is non-empty, increment the number of blocks
 | 
			
		||||
                    self.block_count += 1;
 | 
			
		||||
                }
 | 
			
		||||
                _ => {}
 | 
			
		||||
            },
 | 
			
		||||
            _ => match id {
 | 
			
		||||
                BlockID::Empty => {
 | 
			
		||||
                    // If the existing block is non-empty, and the block that
 | 
			
		||||
                    // we are inserting is empty, then decrement the number of
 | 
			
		||||
                    // blocks
 | 
			
		||||
                    self.block_count -= 1;
 | 
			
		||||
                }
 | 
			
		||||
                _ => {}
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.block_states[index] = id.clone();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// `BlockPos` represents the location of a block in world space
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub struct BlockPos {
 | 
			
		||||
    x: isize,
 | 
			
		||||
    y: isize,
 | 
			
		||||
    z: isize,
 | 
			
		||||
    pub x: isize,
 | 
			
		||||
    pub y: usize,
 | 
			
		||||
    pub z: isize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BlockPos {
 | 
			
		||||
    pub fn new(x: isize, y: usize, z: isize) -> Self {
 | 
			
		||||
        BlockPos { x, y, z }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// BlockRange represents a range of blocks that have been updated
 | 
			
		||||
pub struct BlockRange {
 | 
			
		||||
    pub start: BlockPos,
 | 
			
		||||
    pub end: BlockPos,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BlockRange {
 | 
			
		||||
    pub fn new(start: &BlockPos, end: &BlockPos) -> Self {
 | 
			
		||||
        BlockRange {
 | 
			
		||||
            start: start.clone(),
 | 
			
		||||
            end: end.clone(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn within_range(&self, pos: &BlockPos) -> bool {
 | 
			
		||||
        let minx = min(self.start.x, self.end.x);
 | 
			
		||||
        let maxx = max(self.start.x, self.end.x);
 | 
			
		||||
 | 
			
		||||
        if pos.x < minx || pos.x > maxx {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let miny = min(self.start.y, self.end.y);
 | 
			
		||||
        let maxy = max(self.start.y, self.end.y);
 | 
			
		||||
 | 
			
		||||
        if pos.y < miny || pos.y > maxy {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let minz = min(self.start.z, self.end.z);
 | 
			
		||||
        let maxz = max(self.start.z, self.end.z);
 | 
			
		||||
 | 
			
		||||
        if pos.z < minz || pos.z > maxz {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// BlockID represents the type of block stored
 | 
			
		||||
#[repr(u8)]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum BlockID {
 | 
			
		||||
    Empty,
 | 
			
		||||
    Generic,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,17 @@
 | 
			
		||||
use crate::storage::world::{BlockID, BlockPos};
 | 
			
		||||
use axum::{routing::get, Router};
 | 
			
		||||
 | 
			
		||||
pub trait StorageServer {
 | 
			
		||||
    /// `change_block` changes the block at the world position given by `world_position` to the
 | 
			
		||||
    /// target block id `BlockID`
 | 
			
		||||
    fn change_block(&mut self, target_state: BlockID, world_position: BlockPos);
 | 
			
		||||
    fn change_block_range(&mut self, target_stage: BlockID, start: BlockPos, end: BlockPos);
 | 
			
		||||
 | 
			
		||||
    /// `read_block_at` returns the id of the block at the location specified
 | 
			
		||||
    /// If no block is present, the returned id will be of the empty type
 | 
			
		||||
    fn read_block_at(&self, pos: BlockPos) -> BlockID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
pub async fn main() {
 | 
			
		||||
    let app = Router::new().route("/", get(|| async { "Hello World" }));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,45 @@
 | 
			
		||||
use crate::simple_server::server::SimpleServer;
 | 
			
		||||
use crate::storage::world::{BlockPos, BlockRange};
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::{storage::world::BlockID, storage_server::StorageServer};
 | 
			
		||||
 | 
			
		||||
    use super::*;
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn add_one_block() {
 | 
			
		||||
        assert_eq!(1, 1);
 | 
			
		||||
    fn within_two_dimensions() {
 | 
			
		||||
        // Get two points on the same z axis
 | 
			
		||||
        let first = BlockPos::new(0, 0, 0);
 | 
			
		||||
        let second = BlockPos::new(4, 4, 0);
 | 
			
		||||
 | 
			
		||||
        let range = BlockRange::new(&first, &second);
 | 
			
		||||
 | 
			
		||||
        let test1 = BlockPos::new(1, 1, 0);
 | 
			
		||||
        let test2 = BlockPos::new(0, 0, 0);
 | 
			
		||||
        let test3 = BlockPos::new(0, 4, 0);
 | 
			
		||||
        let test4 = BlockPos::new(4, 4, 0);
 | 
			
		||||
        let test5 = BlockPos::new(4, 0, 0);
 | 
			
		||||
 | 
			
		||||
        assert!(range.within_range(&test1));
 | 
			
		||||
        assert!(range.within_range(&test2));
 | 
			
		||||
        assert!(range.within_range(&test3));
 | 
			
		||||
        assert!(range.within_range(&test4));
 | 
			
		||||
        assert!(range.within_range(&test5));
 | 
			
		||||
 | 
			
		||||
        let test6 = BlockPos::new(-1, -1, 0);
 | 
			
		||||
 | 
			
		||||
        assert!(!range.within_range(&test6));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_simple_insert() {
 | 
			
		||||
        let mut server = SimpleServer::new();
 | 
			
		||||
 | 
			
		||||
        server.change_block(BlockID::Generic, BlockPos::new(0, 0, 0));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            server.read_block_at(BlockPos::new(0, 0, 0)),
 | 
			
		||||
            BlockID::Generic
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user