diff --git a/src/level.rs b/src/level.rs index e07d5ba..f7c0e30 100644 --- a/src/level.rs +++ b/src/level.rs @@ -1,3 +1,4 @@ +pub mod block; pub mod generation; /// a classic level diff --git a/src/level/block.rs b/src/level/block.rs new file mode 100644 index 0000000..1fd25dd --- /dev/null +++ b/src/level/block.rs @@ -0,0 +1,156 @@ +use std::{collections::BTreeMap, sync::LazyLock}; + +use crate::player::PlayerType; + +/// information about all blocks implemented +pub static BLOCK_INFO: LazyLock> = LazyLock::new(|| { + [ + (0x00, BlockInfo::new("air").block_type(BlockType::NonSolid)), + (0x01, BlockInfo::new("stone")), + (0x02, BlockInfo::new("grass")), + (0x03, BlockInfo::new("dirt")), + (0x04, BlockInfo::new("cobblestone")), + (0x05, BlockInfo::new("planks")), + ( + 0x06, + BlockInfo::new("sapling").block_type(BlockType::NonSolid), + ), + ( + 0x07, + BlockInfo::new("bedrock").perm(PlayerType::Operator, PlayerType::Operator), + ), + ( + 0x08, + BlockInfo::new("water_flowing") + .block_type(BlockType::FluidFlowing { stationary: 0x09 }) + .perm(PlayerType::Operator, PlayerType::Normal), + ), + ( + 0x09, + BlockInfo::new("water_stationary") + .block_type(BlockType::FluidStationary { moving: 0x08 }) + .perm(PlayerType::Operator, PlayerType::Normal), + ), + ( + 0x0a, + BlockInfo::new("lava_flowing") + .block_type(BlockType::FluidFlowing { stationary: 0x0b }) + .perm(PlayerType::Operator, PlayerType::Normal), + ), + ( + 0x0b, + BlockInfo::new("lava_stationary") + .block_type(BlockType::FluidStationary { moving: 0x0a }) + .perm(PlayerType::Operator, PlayerType::Normal), + ), + (0x0c, BlockInfo::new("sand")), + (0x0d, BlockInfo::new("gravel")), + (0x0e, BlockInfo::new("gold_ore")), + (0x0f, BlockInfo::new("iron_ore")), + (0x10, BlockInfo::new("coal_ore")), + (0x11, BlockInfo::new("wood")), + (0x12, BlockInfo::new("leaves")), + (0x13, BlockInfo::new("sponge")), + (0x14, BlockInfo::new("glass")), + (0x15, BlockInfo::new("cloth_red")), + (0x16, BlockInfo::new("cloth_orange")), + (0x17, BlockInfo::new("cloth_yellow")), + (0x18, BlockInfo::new("cloth_chartreuse")), + (0x19, BlockInfo::new("cloth_green")), + (0x1a, BlockInfo::new("cloth_spring_green")), + (0x1b, BlockInfo::new("cloth_cyan")), + (0x1c, BlockInfo::new("cloth_capri")), + (0x1d, BlockInfo::new("cloth_ultramarine")), + (0x1e, BlockInfo::new("cloth_violet")), + (0x1f, BlockInfo::new("cloth_purple")), + (0x20, BlockInfo::new("cloth_magenta")), + (0x21, BlockInfo::new("cloth_rose")), + (0x22, BlockInfo::new("cloth_dark_gray")), + (0x23, BlockInfo::new("cloth_light_gray")), + (0x24, BlockInfo::new("cloth_white")), + ( + 0x25, + BlockInfo::new("flower").block_type(BlockType::NonSolid), + ), + (0x26, BlockInfo::new("rose").block_type(BlockType::NonSolid)), + ( + 0x27, + BlockInfo::new("brown_mushroom").block_type(BlockType::NonSolid), + ), + ( + 0x28, + BlockInfo::new("red_mushroom").block_type(BlockType::NonSolid), + ), + (0x29, BlockInfo::new("gold_block")), + (0x2a, BlockInfo::new("iron_block")), + (0x2b, BlockInfo::new("double_slab")), + (0x2c, BlockInfo::new("slab").block_type(BlockType::Slab)), + (0x2d, BlockInfo::new("bricks")), + (0x2e, BlockInfo::new("tnt")), + (0x2f, BlockInfo::new("bookshelf")), + (0x30, BlockInfo::new("mossy_cobblestone")), + (0x31, BlockInfo::new("obsidian")), + ] + .into() +}); + +/// map of block string ids to their byte ids +pub static BLOCK_STRING_ID_MAP: LazyLock> = LazyLock::new(|| { + BLOCK_INFO + .iter() + .map(|(id, info)| (info.str_id, *id)) + .collect() +}); + +/// information about a block type +#[derive(Debug)] +pub struct BlockInfo { + /// the block's string id + pub str_id: &'static str, + /// the type of block + pub block_type: BlockType, + /// permissions needed to place this block + pub place_permissions: PlayerType, + /// permissions needed to break this block (includes replacing fluids) + pub break_permissions: PlayerType, +} + +impl BlockInfo { + /// creates a new block info + pub const fn new(str_id: &'static str) -> Self { + Self { + str_id, + block_type: BlockType::Solid, + place_permissions: PlayerType::Normal, + break_permissions: PlayerType::Normal, + } + } + + /// sets the info's block type + pub const fn block_type(mut self, block_type: BlockType) -> Self { + self.block_type = block_type; + self + } + + /// sets placement and breaking permissions for the info + pub const fn perm(mut self, place: PlayerType, brk: PlayerType) -> Self { + self.place_permissions = place; + self.break_permissions = brk; + self + } +} + +/// types of blocks +#[derive(Debug)] +pub enum BlockType { + /// a regular solid block + Solid, + /// a block which has no collision + NonSolid, + /// a slab + Slab, + /// fluid which is actively flowing + FluidFlowing { stationary: u8 }, + /// fluid which is stationary + FluidStationary { moving: u8 }, +} diff --git a/src/main.rs b/src/main.rs index 9f7d464..46b9613 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![feature(lazy_cell)] + use std::path::PathBuf; use server::{config::ServerConfig, Server}; diff --git a/src/player.rs b/src/player.rs index 7ad5a6a..6269e74 100644 --- a/src/player.rs +++ b/src/player.rs @@ -31,7 +31,7 @@ pub struct Player { } /// enum describing types of players -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(u8)] pub enum PlayerType { /// a normal player diff --git a/src/server/network.rs b/src/server/network.rs index d4ec921..5cf2933 100644 --- a/src/server/network.rs +++ b/src/server/network.rs @@ -9,7 +9,7 @@ use tokio::{ }; use crate::{ - level::Level, + level::{block::BLOCK_INFO, Level}, packet::{ client::ClientPacket, server::ServerPacket, PacketReader, PacketWriter, ARRAY_LENGTH, }, @@ -226,19 +226,41 @@ async fn handle_stream_inner( } => { let block_type = if mode == 0x00 { 0 } else { block_type }; let mut data = data.write().await; + let new_block_info = BLOCK_INFO.get(&block_type); + let mut cancel = new_block_info.is_none(); let block = data.level.get_block(x as usize, y as usize, z as usize); - // check if bedrock - // TODO: genericize this - if (block == 0x07 || block_type == 0x07) - && data + let block_info = BLOCK_INFO + .get(&block) + .expect("missing block information for block!"); + + // check if player has ability to place/break these blocks + if let Some(new_block_info) = new_block_info { + let player_type = data .players .iter() .find_map(|p| { (p.id == *own_id).then_some(p.player_type) }) - .unwrap_or_default() != PlayerType::Operator - { + .unwrap_or_default(); + if player_type < new_block_info.place_permissions { + cancel = true; + reply_queue.push_back(ServerPacket::Message { + player_id: -1, + message: "Not allowed to place this block." + .to_string(), + }); + } else if player_type < block_info.break_permissions { + cancel = true; + reply_queue.push_back(ServerPacket::Message { + player_id: -1, + message: "Not allowed to break this block." + .to_string(), + }); + } + } + + if cancel { reply_queue.push_back(ServerPacket::SetBlock { x, y,