change: All the work from the coffee shops and started implementing the first server
This commit is contained in:
parent
3692f78ba0
commit
914290fe9e
@ -1,3 +1,4 @@
|
|||||||
|
mod simple_server;
|
||||||
mod storage;
|
mod storage;
|
||||||
mod storage_server;
|
mod storage_server;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
@ -21,5 +22,6 @@ fn main() {
|
|||||||
println!("Proxy was enabled");
|
println!("Proxy was enabled");
|
||||||
storage_server::main();
|
storage_server::main();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Hello, world!");
|
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;
|
pub mod world;
|
||||||
mod world;
|
|
||||||
|
@ -1,15 +1,36 @@
|
|||||||
type ChunkCoordinate = isize;
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
const SECTIONS_PER_CHUNK: usize = 16;
|
const SECTIONS_PER_CHUNK: usize = 16;
|
||||||
|
const SLICE_SIZE: usize = 16 * 16;
|
||||||
|
|
||||||
struct ChunkData {
|
#[derive(Debug)]
|
||||||
x: ChunkCoordinate,
|
pub struct ChunkPos {
|
||||||
y: ChunkCoordinate,
|
pub x: isize,
|
||||||
sections: [ChunkSection; SECTIONS_PER_CHUNK],
|
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
|
// https://wiki.vg/Chunk_Format
|
||||||
struct ChunkSection {
|
pub struct ChunkSection {
|
||||||
/// The number of non-empty blocks in the section. If completely full, the
|
/// 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
|
/// section contains a 16 x 16 x 16 cube of blocks = 4096 blocks
|
||||||
/// If the section is empty, this is skipped
|
/// If the section is empty, this is skipped
|
||||||
@ -17,18 +38,100 @@ struct ChunkSection {
|
|||||||
/// The data for all the blocks in the chunk
|
/// The data for all the blocks in the chunk
|
||||||
/// The representation for this may be different based on the number of
|
/// The representation for this may be different based on the number of
|
||||||
/// non-empty blocks
|
/// 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
|
/// `BlockPos` represents the location of a block in world space
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct BlockPos {
|
pub struct BlockPos {
|
||||||
x: isize,
|
pub x: isize,
|
||||||
y: isize,
|
pub y: usize,
|
||||||
z: isize,
|
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
|
/// BlockID represents the type of block stored
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum BlockID {
|
pub enum BlockID {
|
||||||
Empty,
|
Empty,
|
||||||
Generic,
|
Generic,
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
|
use crate::storage::world::{BlockID, BlockPos};
|
||||||
use axum::{routing::get, Router};
|
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]
|
#[tokio::main]
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
let app = Router::new().route("/", get(|| async { "Hello World" }));
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::{storage::world::BlockID, storage_server::StorageServer};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn add_one_block() {
|
fn within_two_dimensions() {
|
||||||
assert_eq!(1, 1);
|
// 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
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user