From 5e577c18c39b039d83f7ab27fc334f6c7e60a0f4 Mon Sep 17 00:00:00 2001 From: user622628252416 Date: Tue, 12 Nov 2024 23:58:27 +0100 Subject: [PATCH 1/6] add /setblock command --- pumpkin/src/command/args/arg_block.rs | 84 ++++++++++++++++ pumpkin/src/command/args/arg_item.rs | 10 +- pumpkin/src/command/args/arg_position_2d.rs | 35 ++++--- pumpkin/src/command/args/arg_position_3d.rs | 37 +------- .../src/command/args/arg_postition_block.rs | 94 ++++++++++++++++++ pumpkin/src/command/args/coordinate.rs | 62 ++++++++++++ pumpkin/src/command/args/mod.rs | 7 +- pumpkin/src/command/commands/cmd_give.rs | 23 ++--- pumpkin/src/command/commands/cmd_setblock.rs | 95 +++++++++++++++++++ pumpkin/src/command/commands/mod.rs | 1 + pumpkin/src/command/mod.rs | 12 ++- 11 files changed, 391 insertions(+), 69 deletions(-) create mode 100644 pumpkin/src/command/args/arg_block.rs create mode 100644 pumpkin/src/command/args/arg_postition_block.rs create mode 100644 pumpkin/src/command/args/coordinate.rs create mode 100644 pumpkin/src/command/commands/cmd_setblock.rs diff --git a/pumpkin/src/command/args/arg_block.rs b/pumpkin/src/command/args/arg_block.rs new file mode 100644 index 000000000..55dcb67b6 --- /dev/null +++ b/pumpkin/src/command/args/arg_block.rs @@ -0,0 +1,84 @@ +use async_trait::async_trait; +use pumpkin_protocol::client::play::{ + CommandSuggestion, ProtoCmdArgParser, ProtoCmdArgSuggestionType, +}; +use pumpkin_world::block::block_registry::{self, Block}; + +use crate::{command::dispatcher::CommandError, server::Server}; + +use super::{ + super::{ + args::{ArgumentConsumer, RawArgs}, + CommandSender, + }, + Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, +}; + +pub(crate) struct BlockArgumentConsumer; + +impl GetClientSideArgParser for BlockArgumentConsumer { + fn get_client_side_parser(&self) -> ProtoCmdArgParser { + ProtoCmdArgParser::Resource { + identifier: "block", + } + } + + fn get_client_side_suggestion_type_override(&self) -> Option { + None + } +} + +#[async_trait] +impl ArgumentConsumer for BlockArgumentConsumer { + async fn consume<'a>( + &self, + _sender: &CommandSender<'a>, + _server: &'a Server, + args: &mut RawArgs<'a>, + ) -> Option> { + let s = args.pop()?; + + let name = if s.contains(':') { + s.to_string() + } else { + format!("minecraft:{s}") + }; + + Some(Arg::Block(name)) + } + + async fn suggest<'a>( + &self, + _sender: &CommandSender<'a>, + _server: &'a Server, + _input: &'a str, + ) -> Result>>, CommandError> { + Ok(None) + } +} + +impl DefaultNameArgConsumer for BlockArgumentConsumer { + fn default_name(&self) -> &'static str { + "item" + } + + fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { + self + } +} + +impl<'a> FindArg<'a> for BlockArgumentConsumer { + type Data = &'a Block; + + fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + match args.get(name) { + Some(Arg::Block(name)) => match block_registry::get_block(name) { + Some(item) => Ok(item), + None => Err(CommandError::GeneralCommandIssue(format!( + "Block {name} does not exist." + ))), + }, + _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), + } + } +} diff --git a/pumpkin/src/command/args/arg_item.rs b/pumpkin/src/command/args/arg_item.rs index 909c23d77..09d758346 100644 --- a/pumpkin/src/command/args/arg_item.rs +++ b/pumpkin/src/command/args/arg_item.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use pumpkin_protocol::client::play::{ CommandSuggestion, ProtoCmdArgParser, ProtoCmdArgSuggestionType, }; +use pumpkin_world::item::item_registry::{self, Item}; use crate::{command::dispatcher::CommandError, server::Server}; @@ -66,11 +67,16 @@ impl DefaultNameArgConsumer for ItemArgumentConsumer { } impl<'a> FindArg<'a> for ItemArgumentConsumer { - type Data = &'a str; + type Data = &'a Item; fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { match args.get(name) { - Some(Arg::Item(name)) => Ok(name), + Some(Arg::Item(name)) => match item_registry::get_item(name) { + Some(item) => Ok(item), + None => Err(CommandError::GeneralCommandIssue(format!( + "Item {name} does not exist." + ))), + }, _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), } } diff --git a/pumpkin/src/command/args/arg_position_2d.rs b/pumpkin/src/command/args/arg_position_2d.rs index 048e28ead..7dd8ada4f 100644 --- a/pumpkin/src/command/args/arg_position_2d.rs +++ b/pumpkin/src/command/args/arg_position_2d.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use pumpkin_core::math::vector2::Vector2; +use pumpkin_core::math::vector3::Vector3; use pumpkin_protocol::client::play::{ CommandSuggestion, ProtoCmdArgParser, ProtoCmdArgSuggestionType, }; @@ -10,6 +11,7 @@ use crate::command::CommandSender; use crate::server::Server; use super::super::args::ArgumentConsumer; +use super::coordinate::Coordinate; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; /// x and z coordinates only @@ -31,25 +33,15 @@ impl GetClientSideArgParser for Position2DArgumentConsumer { impl ArgumentConsumer for Position2DArgumentConsumer { async fn consume<'a>( &self, - _src: &CommandSender<'a>, + src: &CommandSender<'a>, _server: &'a Server, args: &mut RawArgs<'a>, ) -> Option> { - let x_str = args.pop()?; - let z_str = args.pop()?; + let pos = Position2D::try_new(args.pop()?, args.pop()?)?; - let mut x = x_str.parse::().ok()?; - let mut z = z_str.parse::().ok()?; + let vec2 = pos.try_get_values(src.position())?; - // set position to block center if no decimal place is given - if !x_str.contains('.') { - x += 0.5; - } - if !z_str.contains('.') { - z += 0.5; - } - - Some(Arg::Pos2D(Vector2::new(x, z))) + Some(Arg::Pos2D(vec2)) } async fn suggest<'a>( @@ -62,6 +54,21 @@ impl ArgumentConsumer for Position2DArgumentConsumer { } } +struct Position2D(Coordinate, Coordinate); + +impl Position2D { + fn try_new(x: &str, z: &str) -> Option { + Some(Self(x.try_into().ok()?, z.try_into().ok()?)) + } + + fn try_get_values(self, origin: Option>) -> Option> { + Some(Vector2::new( + self.0.value(origin.map(|o| o.x))?, + self.1.value(origin.map(|o| o.z))?, + )) + } +} + impl DefaultNameArgConsumer for Position2DArgumentConsumer { fn default_name(&self) -> &'static str { "pos2d" diff --git a/pumpkin/src/command/args/arg_position_3d.rs b/pumpkin/src/command/args/arg_position_3d.rs index 6d538d10f..269144ce9 100644 --- a/pumpkin/src/command/args/arg_position_3d.rs +++ b/pumpkin/src/command/args/arg_position_3d.rs @@ -1,5 +1,3 @@ -use std::num::ParseFloatError; - use async_trait::async_trait; use pumpkin_core::math::vector3::Vector3; use pumpkin_protocol::client::play::{ @@ -12,6 +10,7 @@ use crate::command::CommandSender; use crate::server::Server; use super::super::args::ArgumentConsumer; +use super::coordinate::Coordinate; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; /// x, y and z coordinates @@ -82,40 +81,6 @@ impl DefaultNameArgConsumer for Position3DArgumentConsumer { } } -enum Coordinate { - Absolute(f64), - Relative(f64), -} - -impl TryFrom<&str> for Coordinate { - type Error = ParseFloatError; - - fn try_from(s: &str) -> Result { - if let Some(s) = s.strip_prefix('~') { - let offset = if s.is_empty() { 0.0 } else { s.parse()? }; - Ok(Self::Relative(offset)) - } else { - let mut v = s.parse()?; - - // set position to block center if no decimal place is given - if !IS_Y && !s.contains('.') { - v += 0.5; - } - - Ok(Self::Absolute(v)) - } - } -} - -impl Coordinate { - fn value(self, origin: Option) -> Option { - match self { - Self::Absolute(v) => Some(v), - Self::Relative(offset) => Some(origin? + offset), - } - } -} - impl<'a> FindArg<'a> for Position3DArgumentConsumer { type Data = Vector3; diff --git a/pumpkin/src/command/args/arg_postition_block.rs b/pumpkin/src/command/args/arg_postition_block.rs new file mode 100644 index 000000000..3b2826c88 --- /dev/null +++ b/pumpkin/src/command/args/arg_postition_block.rs @@ -0,0 +1,94 @@ +use async_trait::async_trait; +use pumpkin_core::math::position::WorldPosition; +use pumpkin_core::math::vector3::Vector3; +use pumpkin_protocol::client::play::{ + CommandSuggestion, ProtoCmdArgParser, ProtoCmdArgSuggestionType, +}; + +use crate::command::dispatcher::CommandError; +use crate::command::tree::RawArgs; +use crate::command::CommandSender; +use crate::server::Server; + +use super::super::args::ArgumentConsumer; +use super::coordinate::BlockCoordinate; +use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; + +/// x, y and z coordinates +pub(crate) struct BlockPosArgumentConsumer; + +impl GetClientSideArgParser for BlockPosArgumentConsumer { + fn get_client_side_parser(&self) -> ProtoCmdArgParser { + ProtoCmdArgParser::BlockPos + } + + fn get_client_side_suggestion_type_override(&self) -> Option { + None + } +} + +#[async_trait] +impl ArgumentConsumer for BlockPosArgumentConsumer { + async fn consume<'a>( + &self, + src: &CommandSender<'a>, + _server: &'a Server, + args: &mut RawArgs<'a>, + ) -> Option> { + let pos = BlockPos::try_new(args.pop()?, args.pop()?, args.pop()?)?; + + let vec3 = pos.try_get_values(src.position())?; + + Some(Arg::BlockPos(vec3)) + } + + async fn suggest<'a>( + &self, + _sender: &CommandSender<'a>, + _server: &'a Server, + _input: &'a str, + ) -> Result>>, CommandError> { + Ok(None) + } +} + +struct BlockPos(BlockCoordinate, BlockCoordinate, BlockCoordinate); + +impl BlockPos { + fn try_new(x: &str, y: &str, z: &str) -> Option { + Some(Self( + x.try_into().ok()?, + y.try_into().ok()?, + z.try_into().ok()?, + )) + } + + fn try_get_values(self, origin: Option>) -> Option { + Some(WorldPosition(Vector3::new( + self.0.value(origin.map(|o| o.x))?, + self.1.value(origin.map(|o| o.y))?, + self.2.value(origin.map(|o| o.z))?, + ))) + } +} + +impl DefaultNameArgConsumer for BlockPosArgumentConsumer { + fn default_name(&self) -> &'static str { + "block_pos" + } + + fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { + &BlockPosArgumentConsumer + } +} + +impl<'a> FindArg<'a> for BlockPosArgumentConsumer { + type Data = WorldPosition; + + fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { + match args.get(name) { + Some(Arg::BlockPos(data)) => Ok(*data), + _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), + } + } +} diff --git a/pumpkin/src/command/args/coordinate.rs b/pumpkin/src/command/args/coordinate.rs new file mode 100644 index 000000000..bca02c6fe --- /dev/null +++ b/pumpkin/src/command/args/coordinate.rs @@ -0,0 +1,62 @@ +use std::str::FromStr; + +pub enum Coordinate { + Absolute(f64), + Relative(f64), +} + +impl TryFrom<&str> for Coordinate { + type Error = ::Err; + + fn try_from(s: &str) -> Result { + if let Some(s) = s.strip_prefix('~') { + let offset = if s.is_empty() { 0.0 } else { s.parse()? }; + Ok(Self::Relative(offset)) + } else { + let mut v = s.parse()?; + + // set position to block center if no decimal place is given + if !IS_Y && !s.contains('.') { + v += 0.5; + } + + Ok(Self::Absolute(v)) + } + } +} + +impl Coordinate { + pub fn value(self, origin: Option) -> Option { + match self { + Self::Absolute(v) => Some(v), + Self::Relative(offset) => Some(origin? + offset), + } + } +} + +pub enum BlockCoordinate { + Absolute(i32), + Relative(i32), +} + +impl TryFrom<&str> for BlockCoordinate { + type Error = ::Err; + + fn try_from(s: &str) -> Result { + if let Some(s) = s.strip_prefix('~') { + let offset = if s.is_empty() { 0 } else { s.parse()? }; + Ok(Self::Relative(offset)) + } else { + Ok(Self::Absolute(s.parse()?)) + } + } +} + +impl BlockCoordinate { + pub fn value(self, origin: Option) -> Option { + match self { + Self::Absolute(v) => Some(v), + Self::Relative(offset) => Some(origin?.round() as i32 + offset), + } + } +} diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index d543814af..8b41c20d3 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc}; use arg_bounded_num::{NotInBounds, Number}; use async_trait::async_trait; use pumpkin_core::{ - math::{vector2::Vector2, vector3::Vector3}, + math::{position::WorldPosition, vector2::Vector2, vector3::Vector3}, GameMode, }; use pumpkin_protocol::client::play::{ @@ -18,6 +18,7 @@ use super::{ CommandSender, }; +pub(crate) mod arg_block; pub(crate) mod arg_bounded_num; pub(crate) mod arg_command; pub(crate) mod arg_entities; @@ -28,8 +29,10 @@ pub(crate) mod arg_message; pub(crate) mod arg_players; pub(crate) mod arg_position_2d; pub(crate) mod arg_position_3d; +pub(crate) mod arg_postition_block; pub(crate) mod arg_rotation; pub(crate) mod arg_simple; +mod coordinate; /// see [`crate::commands::tree_builder::argument`] #[async_trait] @@ -71,12 +74,14 @@ pub(crate) enum Arg<'a> { Entities(Vec>), Entity(Arc), Players(Vec>), + BlockPos(WorldPosition), Pos3D(Vector3), Pos2D(Vector2), Rotation(f32, f32), GameMode(GameMode), CommandTree(&'a CommandTree<'a>), Item(String), + Block(String), Msg(String), Num(Result), #[allow(unused)] diff --git a/pumpkin/src/command/commands/cmd_give.rs b/pumpkin/src/command/commands/cmd_give.rs index a245ff58f..5f62233e2 100644 --- a/pumpkin/src/command/commands/cmd_give.rs +++ b/pumpkin/src/command/commands/cmd_give.rs @@ -1,7 +1,6 @@ use async_trait::async_trait; use pumpkin_core::text::color::{Color, NamedColor}; use pumpkin_core::text::TextComponent; -use pumpkin_world::item::item_registry; use crate::command::args::arg_bounded_num::BoundedNumArgumentConsumer; use crate::command::args::arg_item::ItemArgumentConsumer; @@ -35,17 +34,7 @@ impl CommandExecutor for GiveExecutor { ) -> Result<(), CommandError> { let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; - let item_name = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?; - - let Some(item) = item_registry::get_item(item_name) else { - sender - .send_message( - TextComponent::text_string(format!("Item {item_name} does not exist.")) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; + let item = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?; let item_count = match ITEM_COUNT_CONSUMER.find_arg_default_name(args) { Err(_) => 1, @@ -68,10 +57,14 @@ impl CommandExecutor for GiveExecutor { sender .send_message(TextComponent::text_string(match targets { [target] => format!( - "Gave {item_count} {item_name} to {}", - target.gameprofile.name + "Gave {item_count} {} to {}", + item.name, target.gameprofile.name + ), + _ => format!( + "Gave {item_count} {} to {} players", + item.name, + targets.len() ), - _ => format!("Gave {item_count} {item_name} to {} players", targets.len()), })) .await; diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs new file mode 100644 index 000000000..a5760f336 --- /dev/null +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -0,0 +1,95 @@ +use async_trait::async_trait; +use pumpkin_core::text::TextComponent; + +use crate::command::args::arg_block::BlockArgumentConsumer; +use crate::command::args::arg_item::ItemArgumentConsumer; +use crate::command::args::arg_postition_block::BlockPosArgumentConsumer; +use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName}; +use crate::command::tree::CommandTree; +use crate::command::tree_builder::{argument, argument_default_name, literal, require}; +use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::entity::player::PermissionLvl; + +const NAMES: [&str; 1] = ["setblock"]; + +const DESCRIPTION: &str = "Place a block."; + +const ARG_BLOCK: &str = "block"; + +#[derive(Clone, Copy)] +enum Mode { + /// with particles + Destroy, + + /// only replaces air + Keep, + + /// default; without particles + Replace, +} + +struct SetblockExecutor(Mode); + +#[async_trait] +impl CommandExecutor for SetblockExecutor { + async fn execute<'a>( + &self, + sender: &mut CommandSender<'a>, + _server: &crate::server::Server, + args: &ConsumedArgs<'a>, + ) -> Result<(), CommandError> { + let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; + let pos = BlockPosArgumentConsumer.find_arg_default_name(args)?; + let mode = self.0; + let world = sender.world().ok_or(CommandError::InvalidRequirement)?; + + let success = match mode { + Mode::Destroy => { + world.break_block(pos).await; + world.set_block(pos, block.id).await; + true + } + Mode::Replace => { + world.set_block(pos, block.id).await; + true + } + Mode::Keep => { + match world.get_block(pos).await { + // todo: include other air blocks (I think there's cave air etc?) + Some(old_block) if old_block.id == 0 => { + world.set_block(pos, block.id).await; + true + } + _ => false, + } + } + }; + + sender + .send_message(if success { + TextComponent::text_string(format!("Placed block {} at {pos}", block.name,)) + } else { + TextComponent::text_string(format!("Kept block at {pos}")) + }) + .await; + + Ok(()) + } +} + +pub fn init_command_tree<'a>() -> CommandTree<'a> { + CommandTree::new(NAMES, DESCRIPTION).with_child( + require(&|sender| { + sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some() + }) + .with_child( + argument_default_name(&BlockPosArgumentConsumer).with_child( + argument(ARG_BLOCK, &ItemArgumentConsumer) + .with_child(literal("replace").execute(&SetblockExecutor(Mode::Replace))) + .with_child(literal("destroy").execute(&SetblockExecutor(Mode::Destroy))) + .with_child(literal("keep").execute(&SetblockExecutor(Mode::Keep))) + .execute(&SetblockExecutor(Mode::Replace)), + ), + ), + ) +} diff --git a/pumpkin/src/command/commands/mod.rs b/pumpkin/src/command/commands/mod.rs index a69674e46..2fadce1a9 100644 --- a/pumpkin/src/command/commands/mod.rs +++ b/pumpkin/src/command/commands/mod.rs @@ -9,6 +9,7 @@ pub mod cmd_kill; pub mod cmd_list; pub mod cmd_pumpkin; pub mod cmd_say; +pub mod cmd_setblock; pub mod cmd_stop; pub mod cmd_teleport; pub mod cmd_worldborder; diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 247cb923f..f5ffae7dd 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -4,7 +4,7 @@ use args::ConsumedArgs; use async_trait::async_trait; use commands::{ cmd_clear, cmd_craft, cmd_echest, cmd_gamemode, cmd_give, cmd_help, cmd_kick, cmd_kill, - cmd_list, cmd_pumpkin, cmd_say, cmd_stop, cmd_teleport, cmd_worldborder, + cmd_list, cmd_pumpkin, cmd_say, cmd_setblock, cmd_stop, cmd_teleport, cmd_worldborder, }; use dispatcher::CommandError; use pumpkin_core::math::vector3::Vector3; @@ -13,6 +13,7 @@ use pumpkin_core::text::TextComponent; use crate::command::dispatcher::CommandDispatcher; use crate::entity::player::{PermissionLvl, Player}; use crate::server::Server; +use crate::world::World; pub mod args; pub mod client_cmd_suggestions; @@ -78,6 +79,14 @@ impl<'a> CommandSender<'a> { CommandSender::Player(p) => Some(p.living_entity.entity.pos.load()), } } + + #[must_use] + pub fn world(&self) -> Option<&World> { + match self { + CommandSender::Console | CommandSender::Rcon(..) => None, + CommandSender::Player(p) => Some(&p.living_entity.entity.world), + } + } } #[must_use] @@ -98,6 +107,7 @@ pub fn default_dispatcher<'a>() -> Arc> { dispatcher.register(cmd_give::init_command_tree()); dispatcher.register(cmd_list::init_command_tree()); dispatcher.register(cmd_clear::init_command_tree()); + dispatcher.register(cmd_setblock::init_command_tree()); Arc::new(dispatcher) } From 1dc5ca423745b5916ba5d7c41e50dfc4f3f498b8 Mon Sep 17 00:00:00 2001 From: user622628252416 Date: Wed, 13 Nov 2024 00:10:06 +0100 Subject: [PATCH 2/6] fix /setblock command tree --- pumpkin/src/command/commands/cmd_setblock.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs index a5760f336..f40ce24be 100644 --- a/pumpkin/src/command/commands/cmd_setblock.rs +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -2,11 +2,10 @@ use async_trait::async_trait; use pumpkin_core::text::TextComponent; use crate::command::args::arg_block::BlockArgumentConsumer; -use crate::command::args::arg_item::ItemArgumentConsumer; use crate::command::args::arg_postition_block::BlockPosArgumentConsumer; -use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName}; +use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; -use crate::command::tree_builder::{argument, argument_default_name, literal, require}; +use crate::command::tree_builder::{argument, literal, require}; use crate::command::{CommandError, CommandExecutor, CommandSender}; use crate::entity::player::PermissionLvl; @@ -15,6 +14,7 @@ const NAMES: [&str; 1] = ["setblock"]; const DESCRIPTION: &str = "Place a block."; const ARG_BLOCK: &str = "block"; +const ARG_BLOCK_POS: &str = "position"; #[derive(Clone, Copy)] enum Mode { @@ -39,7 +39,7 @@ impl CommandExecutor for SetblockExecutor { args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; - let pos = BlockPosArgumentConsumer.find_arg_default_name(args)?; + let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?; let mode = self.0; let world = sender.world().ok_or(CommandError::InvalidRequirement)?; @@ -83,8 +83,8 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some() }) .with_child( - argument_default_name(&BlockPosArgumentConsumer).with_child( - argument(ARG_BLOCK, &ItemArgumentConsumer) + argument(ARG_BLOCK_POS, &BlockPosArgumentConsumer).with_child( + argument(ARG_BLOCK, &BlockArgumentConsumer) .with_child(literal("replace").execute(&SetblockExecutor(Mode::Replace))) .with_child(literal("destroy").execute(&SetblockExecutor(Mode::Destroy))) .with_child(literal("keep").execute(&SetblockExecutor(Mode::Keep))) From 653848dfa019b2bc9306d7fb366b878706228c23 Mon Sep 17 00:00:00 2001 From: user622628252416 <76960354+user622628252416@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:09:16 +0100 Subject: [PATCH 3/6] fix particles, block state id, block coordinate rounding, WoldPosition formatting --- pumpkin-core/src/math/position.rs | 2 +- pumpkin/src/client/player_packet.rs | 6 ++-- pumpkin/src/command/args/arg_block.rs | 4 +-- pumpkin/src/command/args/coordinate.rs | 3 +- pumpkin/src/command/commands/cmd_clear.rs | 2 +- pumpkin/src/command/commands/cmd_setblock.rs | 13 +++++--- pumpkin/src/world/mod.rs | 35 +++++++++++++++----- 7 files changed, 43 insertions(+), 22 deletions(-) diff --git a/pumpkin-core/src/math/position.rs b/pumpkin-core/src/math/position.rs index 7c51dff45..478f12706 100644 --- a/pumpkin-core/src/math/position.rs +++ b/pumpkin-core/src/math/position.rs @@ -68,6 +68,6 @@ impl<'de> Deserialize<'de> for WorldPosition { impl std::fmt::Display for WorldPosition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}, {},{})", self.0.x, self.0.y, self.0.z) + write!(f, "{}, {}, {}", self.0.x, self.0.y, self.0.z) } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 06b2583e8..076fdfcd7 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -475,10 +475,9 @@ impl Player { if self.gamemode.load() == GameMode::Creative { let location = player_action.location; // Block break & block break sound - // TODO: currently this is always dirt replace it let entity = &self.living_entity.entity; let world = &entity.world; - world.break_block(location).await; + world.break_block(location, Some(self)).await; } } Status::CancelledDigging => { @@ -505,10 +504,9 @@ impl Player { return; } // Block break & block break sound - // TODO: currently this is always dirt replace it let entity = &self.living_entity.entity; let world = &entity.world; - world.break_block(location).await; + world.break_block(location, Some(self)).await; // TODO: Send this every tick self.client .send_packet(&CAcknowledgeBlockChange::new(player_action.sequence)) diff --git a/pumpkin/src/command/args/arg_block.rs b/pumpkin/src/command/args/arg_block.rs index 55dcb67b6..6aa80f3ce 100644 --- a/pumpkin/src/command/args/arg_block.rs +++ b/pumpkin/src/command/args/arg_block.rs @@ -59,7 +59,7 @@ impl ArgumentConsumer for BlockArgumentConsumer { impl DefaultNameArgConsumer for BlockArgumentConsumer { fn default_name(&self) -> &'static str { - "item" + "block" } fn get_argument_consumer(&self) -> &dyn ArgumentConsumer { @@ -73,7 +73,7 @@ impl<'a> FindArg<'a> for BlockArgumentConsumer { fn find_arg(args: &'a super::ConsumedArgs, name: &'a str) -> Result { match args.get(name) { Some(Arg::Block(name)) => match block_registry::get_block(name) { - Some(item) => Ok(item), + Some(block) => Ok(block), None => Err(CommandError::GeneralCommandIssue(format!( "Block {name} does not exist." ))), diff --git a/pumpkin/src/command/args/coordinate.rs b/pumpkin/src/command/args/coordinate.rs index bca02c6fe..4d96d3dc6 100644 --- a/pumpkin/src/command/args/coordinate.rs +++ b/pumpkin/src/command/args/coordinate.rs @@ -34,6 +34,7 @@ impl Coordinate { } } +#[derive(Debug)] pub enum BlockCoordinate { Absolute(i32), Relative(i32), @@ -56,7 +57,7 @@ impl BlockCoordinate { pub fn value(self, origin: Option) -> Option { match self { Self::Absolute(v) => Some(v), - Self::Relative(offset) => Some(origin?.round() as i32 + offset), + Self::Relative(offset) => Some(origin?.floor() as i32 + offset), } } } diff --git a/pumpkin/src/command/commands/cmd_clear.rs b/pumpkin/src/command/commands/cmd_clear.rs index 37e6f1b98..fee70f35e 100644 --- a/pumpkin/src/command/commands/cmd_clear.rs +++ b/pumpkin/src/command/commands/cmd_clear.rs @@ -97,7 +97,7 @@ impl CommandExecutor for ClearSelfExecutor { let item_count = clear_player(&target).await; - let hold_target = vec![target]; + let hold_target = [target]; let msg = clear_command_text_output(item_count, &hold_target); sender.send_message(msg).await; diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs index f40ce24be..2fd4c92bd 100644 --- a/pumpkin/src/command/commands/cmd_setblock.rs +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use pumpkin_core::text::color::NamedColor; use pumpkin_core::text::TextComponent; use crate::command::args::arg_block::BlockArgumentConsumer; @@ -18,7 +19,7 @@ const ARG_BLOCK_POS: &str = "position"; #[derive(Clone, Copy)] enum Mode { - /// with particles + /// with particles + item drops Destroy, /// only replaces air @@ -39,25 +40,26 @@ impl CommandExecutor for SetblockExecutor { args: &ConsumedArgs<'a>, ) -> Result<(), CommandError> { let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; + let block_state_id = block.default_state_id; let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?; let mode = self.0; let world = sender.world().ok_or(CommandError::InvalidRequirement)?; let success = match mode { Mode::Destroy => { - world.break_block(pos).await; - world.set_block(pos, block.id).await; + world.break_block(pos, None).await; + world.set_block(pos, block_state_id).await; true } Mode::Replace => { - world.set_block(pos, block.id).await; + world.set_block(pos, block_state_id).await; true } Mode::Keep => { match world.get_block(pos).await { // todo: include other air blocks (I think there's cave air etc?) Some(old_block) if old_block.id == 0 => { - world.set_block(pos, block.id).await; + world.set_block(pos, block_state_id).await; true } _ => false, @@ -70,6 +72,7 @@ impl CommandExecutor for SetblockExecutor { TextComponent::text_string(format!("Placed block {} at {pos}", block.name,)) } else { TextComponent::text_string(format!("Kept block at {pos}")) + .color_named(NamedColor::Red) }) .await; diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 2ecc5bd03..893c28715 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -606,17 +606,28 @@ impl World { self.broadcast_packet_all(&CRemoveEntities::new(&[entity.entity_id.into()])) .await; } - pub async fn set_block(&self, position: WorldPosition, block_id: u16) { + + /// Sets a block + pub async fn set_block(&self, position: WorldPosition, block_state_id: u16) -> u16 { let (chunk_coordinate, relative_coordinates) = position.chunk_and_chunk_relative_position(); // Since we divide by 16 remnant can never exceed u8 let relative = ChunkRelativeBlockCoordinates::from(relative_coordinates); let chunk = self.receive_chunk(chunk_coordinate).await; - chunk.write().await.blocks.set_block(relative, block_id); + let replaced_block_state_id = chunk + .write() + .await + .blocks + .set_block(relative, block_state_id); - self.broadcast_packet_all(&CBlockUpdate::new(&position, i32::from(block_id).into())) - .await; + self.broadcast_packet_all(&CBlockUpdate::new( + &position, + i32::from(block_state_id).into(), + )) + .await; + + replaced_block_state_id } // Stream the chunks (don't collect them and then do stuff with them) @@ -634,11 +645,19 @@ impl World { .expect("Channel closed for unknown reason") } - pub async fn break_block(&self, position: WorldPosition) { - self.set_block(position, 0).await; + pub async fn break_block(&self, position: WorldPosition, cause: Option<&Player>) { + let broken_block_state_id = self.set_block(position, 0).await; - self.broadcast_packet_all(&CWorldEvent::new(2001, &position, 11, false)) - .await; + let particles_packet = + CWorldEvent::new(2001, &position, broken_block_state_id.into(), false); + + match cause { + Some(player) => { + self.broadcast_packet_except(&[player.gameprofile.id], &particles_packet) + .await; + } + None => self.broadcast_packet_all(&particles_packet).await, + } } pub async fn get_block_id(&self, position: WorldPosition) -> u16 { From 34943057726a76568e2c0871a59aae81f8ad083c Mon Sep 17 00:00:00 2001 From: user622628252416 Date: Wed, 13 Nov 2024 17:39:00 +0100 Subject: [PATCH 4/6] fix block state/block mixup --- pumpkin-world/src/block/block_registry.rs | 20 ++++++++ pumpkin/src/client/player_packet.rs | 4 +- pumpkin/src/command/args/arg_position_2d.rs | 19 ++++--- pumpkin/src/command/args/arg_position_3d.rs | 22 ++++---- .../src/command/args/arg_postition_block.rs | 22 ++++---- pumpkin/src/command/args/coordinate.rs | 30 +++++++---- pumpkin/src/command/commands/cmd_getblock.rs | 50 +++++++++++++++++++ pumpkin/src/command/commands/cmd_setblock.rs | 19 +++---- pumpkin/src/command/commands/mod.rs | 1 + pumpkin/src/command/mod.rs | 6 ++- pumpkin/src/world/mod.rs | 46 +++++++++++++---- 11 files changed, 178 insertions(+), 61 deletions(-) create mode 100644 pumpkin/src/command/commands/cmd_getblock.rs diff --git a/pumpkin-world/src/block/block_registry.rs b/pumpkin-world/src/block/block_registry.rs index f885c11e4..1ceca5a17 100644 --- a/pumpkin-world/src/block/block_registry.rs +++ b/pumpkin-world/src/block/block_registry.rs @@ -18,6 +18,26 @@ pub fn get_block_by_id<'a>(id: u16) -> Option<&'a Block> { BLOCKS.blocks.iter().find(|&block| block.id == id) } +pub fn get_state_by_state_id<'a>(id: u16) -> Option<&'a State> { + get_block_and_state_by_state_id(id).map(|(_, state)| state) +} + +pub fn get_block_by_state_id<'a>(id: u16) -> Option<&'a Block> { + get_block_and_state_by_state_id(id).map(|(block, _)| block) +} + +pub fn get_block_and_state_by_state_id<'a>(id: u16) -> Option<(&'a Block, &'a State)> { + for block in &BLOCKS.blocks { + for state in &block.states { + if state.id == id { + return Some((block, state)); + } + } + } + + None +} + pub fn get_block_by_item<'a>(item_id: u16) -> Option<&'a Block> { BLOCKS.blocks.iter().find(|&block| block.item_id == item_id) } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 076fdfcd7..ccb788600 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -588,7 +588,9 @@ impl Player { let bounding_box = entity.bounding_box.load(); //TODO: Make this check for every entity in that posistion if !bounding_box.intersects(&block_bounding_box) { - world.set_block(world_pos, block.default_state_id).await; + world + .set_block_state(world_pos, block.default_state_id) + .await; } } self.client diff --git a/pumpkin/src/command/args/arg_position_2d.rs b/pumpkin/src/command/args/arg_position_2d.rs index 7dd8ada4f..b855348ec 100644 --- a/pumpkin/src/command/args/arg_position_2d.rs +++ b/pumpkin/src/command/args/arg_position_2d.rs @@ -11,7 +11,7 @@ use crate::command::CommandSender; use crate::server::Server; use super::super::args::ArgumentConsumer; -use super::coordinate::Coordinate; +use super::coordinate::MaybeRelativeCoordinate; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; /// x and z coordinates only @@ -37,9 +37,9 @@ impl ArgumentConsumer for Position2DArgumentConsumer { _server: &'a Server, args: &mut RawArgs<'a>, ) -> Option> { - let pos = Position2D::try_new(args.pop()?, args.pop()?)?; + let pos = MaybeRelativePosition2D::try_new(args.pop()?, args.pop()?)?; - let vec2 = pos.try_get_values(src.position())?; + let vec2 = pos.try_to_abolute(src.position())?; Some(Arg::Pos2D(vec2)) } @@ -54,17 +54,20 @@ impl ArgumentConsumer for Position2DArgumentConsumer { } } -struct Position2D(Coordinate, Coordinate); +struct MaybeRelativePosition2D( + MaybeRelativeCoordinate, + MaybeRelativeCoordinate, +); -impl Position2D { +impl MaybeRelativePosition2D { fn try_new(x: &str, z: &str) -> Option { Some(Self(x.try_into().ok()?, z.try_into().ok()?)) } - fn try_get_values(self, origin: Option>) -> Option> { + fn try_to_abolute(self, origin: Option>) -> Option> { Some(Vector2::new( - self.0.value(origin.map(|o| o.x))?, - self.1.value(origin.map(|o| o.z))?, + self.0.into_absolute(origin.map(|o| o.x))?, + self.1.into_absolute(origin.map(|o| o.z))?, )) } } diff --git a/pumpkin/src/command/args/arg_position_3d.rs b/pumpkin/src/command/args/arg_position_3d.rs index 269144ce9..4940b4dbc 100644 --- a/pumpkin/src/command/args/arg_position_3d.rs +++ b/pumpkin/src/command/args/arg_position_3d.rs @@ -10,7 +10,7 @@ use crate::command::CommandSender; use crate::server::Server; use super::super::args::ArgumentConsumer; -use super::coordinate::Coordinate; +use super::coordinate::MaybeRelativeCoordinate; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; /// x, y and z coordinates @@ -34,9 +34,9 @@ impl ArgumentConsumer for Position3DArgumentConsumer { _server: &'a Server, args: &mut RawArgs<'a>, ) -> Option> { - let pos = Position3D::try_new(args.pop()?, args.pop()?, args.pop()?)?; + let pos = MaybeRelativePosition3D::try_new(args.pop()?, args.pop()?, args.pop()?)?; - let vec3 = pos.try_get_values(src.position())?; + let vec3 = pos.try_to_absolute(src.position())?; Some(Arg::Pos3D(vec3)) } @@ -51,9 +51,13 @@ impl ArgumentConsumer for Position3DArgumentConsumer { } } -struct Position3D(Coordinate, Coordinate, Coordinate); +struct MaybeRelativePosition3D( + MaybeRelativeCoordinate, + MaybeRelativeCoordinate, + MaybeRelativeCoordinate, +); -impl Position3D { +impl MaybeRelativePosition3D { fn try_new(x: &str, y: &str, z: &str) -> Option { Some(Self( x.try_into().ok()?, @@ -62,11 +66,11 @@ impl Position3D { )) } - fn try_get_values(self, origin: Option>) -> Option> { + fn try_to_absolute(self, origin: Option>) -> Option> { Some(Vector3::new( - self.0.value(origin.map(|o| o.x))?, - self.1.value(origin.map(|o| o.y))?, - self.2.value(origin.map(|o| o.z))?, + self.0.into_absolute(origin.map(|o| o.x))?, + self.1.into_absolute(origin.map(|o| o.y))?, + self.2.into_absolute(origin.map(|o| o.z))?, )) } } diff --git a/pumpkin/src/command/args/arg_postition_block.rs b/pumpkin/src/command/args/arg_postition_block.rs index 3b2826c88..b8ae25dba 100644 --- a/pumpkin/src/command/args/arg_postition_block.rs +++ b/pumpkin/src/command/args/arg_postition_block.rs @@ -11,7 +11,7 @@ use crate::command::CommandSender; use crate::server::Server; use super::super::args::ArgumentConsumer; -use super::coordinate::BlockCoordinate; +use super::coordinate::MaybeRelativeBlockCoordinate; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; /// x, y and z coordinates @@ -35,9 +35,9 @@ impl ArgumentConsumer for BlockPosArgumentConsumer { _server: &'a Server, args: &mut RawArgs<'a>, ) -> Option> { - let pos = BlockPos::try_new(args.pop()?, args.pop()?, args.pop()?)?; + let pos = MaybeRelativeBlockPos::try_new(args.pop()?, args.pop()?, args.pop()?)?; - let vec3 = pos.try_get_values(src.position())?; + let vec3 = pos.try_to_absolute(src.position())?; Some(Arg::BlockPos(vec3)) } @@ -52,9 +52,13 @@ impl ArgumentConsumer for BlockPosArgumentConsumer { } } -struct BlockPos(BlockCoordinate, BlockCoordinate, BlockCoordinate); +struct MaybeRelativeBlockPos( + MaybeRelativeBlockCoordinate, + MaybeRelativeBlockCoordinate, + MaybeRelativeBlockCoordinate, +); -impl BlockPos { +impl MaybeRelativeBlockPos { fn try_new(x: &str, y: &str, z: &str) -> Option { Some(Self( x.try_into().ok()?, @@ -63,11 +67,11 @@ impl BlockPos { )) } - fn try_get_values(self, origin: Option>) -> Option { + fn try_to_absolute(self, origin: Option>) -> Option { Some(WorldPosition(Vector3::new( - self.0.value(origin.map(|o| o.x))?, - self.1.value(origin.map(|o| o.y))?, - self.2.value(origin.map(|o| o.z))?, + self.0.into_absolute(origin.map(|o| o.x))?, + self.1.into_absolute(origin.map(|o| o.y))?, + self.2.into_absolute(origin.map(|o| o.z))?, ))) } } diff --git a/pumpkin/src/command/args/coordinate.rs b/pumpkin/src/command/args/coordinate.rs index 4d96d3dc6..036ad5b0a 100644 --- a/pumpkin/src/command/args/coordinate.rs +++ b/pumpkin/src/command/args/coordinate.rs @@ -1,11 +1,13 @@ use std::str::FromStr; -pub enum Coordinate { +use pumpkin_world::{WORLD_LOWEST_Y, WORLD_MAX_Y}; + +pub enum MaybeRelativeCoordinate { Absolute(f64), Relative(f64), } -impl TryFrom<&str> for Coordinate { +impl TryFrom<&str> for MaybeRelativeCoordinate { type Error = ::Err; fn try_from(s: &str) -> Result { @@ -25,8 +27,8 @@ impl TryFrom<&str> for Coordinate { } } -impl Coordinate { - pub fn value(self, origin: Option) -> Option { +impl MaybeRelativeCoordinate { + pub fn into_absolute(self, origin: Option) -> Option { match self { Self::Absolute(v) => Some(v), Self::Relative(offset) => Some(origin? + offset), @@ -35,12 +37,12 @@ impl Coordinate { } #[derive(Debug)] -pub enum BlockCoordinate { +pub enum MaybeRelativeBlockCoordinate { Absolute(i32), Relative(i32), } -impl TryFrom<&str> for BlockCoordinate { +impl TryFrom<&str> for MaybeRelativeBlockCoordinate { type Error = ::Err; fn try_from(s: &str) -> Result { @@ -53,11 +55,17 @@ impl TryFrom<&str> for BlockCoordinate { } } -impl BlockCoordinate { - pub fn value(self, origin: Option) -> Option { - match self { - Self::Absolute(v) => Some(v), - Self::Relative(offset) => Some(origin?.floor() as i32 + offset), +impl MaybeRelativeBlockCoordinate { + pub fn into_absolute(self, origin: Option) -> Option { + let abs = match self { + Self::Absolute(v) => v, + Self::Relative(offset) => origin?.floor() as i32 + offset, + }; + + if IS_Y && (abs < WORLD_LOWEST_Y.into() || abs >= WORLD_MAX_Y.into()) { + return None; } + + Some(abs) } } diff --git a/pumpkin/src/command/commands/cmd_getblock.rs b/pumpkin/src/command/commands/cmd_getblock.rs new file mode 100644 index 000000000..29549f6d8 --- /dev/null +++ b/pumpkin/src/command/commands/cmd_getblock.rs @@ -0,0 +1,50 @@ +use async_trait::async_trait; +use pumpkin_core::text::TextComponent; + +use crate::command::args::arg_postition_block::BlockPosArgumentConsumer; +use crate::command::args::{ConsumedArgs, FindArg}; +use crate::command::tree::CommandTree; +use crate::command::tree_builder::{argument, require}; +use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::entity::player::PermissionLvl; + +const NAMES: [&str; 2] = ["getblock", "gb"]; + +const DESCRIPTION: &str = "Print a block to. For debug purposes"; + +const ARG_BLOCK_POS: &str = "position"; + +struct GetblockExecutor; + +#[async_trait] +impl CommandExecutor for GetblockExecutor { + async fn execute<'a>( + &self, + sender: &mut CommandSender<'a>, + _server: &crate::server::Server, + args: &ConsumedArgs<'a>, + ) -> Result<(), CommandError> { + let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?; + let world = sender.world().ok_or(CommandError::InvalidRequirement)?; + + if let Some(block) = world.get_block(pos).await { + sender + .send_message(TextComponent::text_string(format!( + "Block {} at {pos}", + block.name, + ))) + .await; + } + + Ok(()) + } +} + +pub fn init_command_tree<'a>() -> CommandTree<'a> { + CommandTree::new(NAMES, DESCRIPTION).with_child( + require(&|sender| { + sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some() + }) + .with_child(argument(ARG_BLOCK_POS, &BlockPosArgumentConsumer).execute(&GetblockExecutor)), + ) +} diff --git a/pumpkin/src/command/commands/cmd_setblock.rs b/pumpkin/src/command/commands/cmd_setblock.rs index 2fd4c92bd..e2a92400d 100644 --- a/pumpkin/src/command/commands/cmd_setblock.rs +++ b/pumpkin/src/command/commands/cmd_setblock.rs @@ -48,23 +48,20 @@ impl CommandExecutor for SetblockExecutor { let success = match mode { Mode::Destroy => { world.break_block(pos, None).await; - world.set_block(pos, block_state_id).await; + world.set_block_state(pos, block_state_id).await; true } Mode::Replace => { - world.set_block(pos, block_state_id).await; + world.set_block_state(pos, block_state_id).await; true } - Mode::Keep => { - match world.get_block(pos).await { - // todo: include other air blocks (I think there's cave air etc?) - Some(old_block) if old_block.id == 0 => { - world.set_block(pos, block_state_id).await; - true - } - _ => false, + Mode::Keep => match world.get_block_state(pos).await { + Some(old_state) if old_state.air => { + world.set_block_state(pos, block_state_id).await; + true } - } + _ => false, + }, }; sender diff --git a/pumpkin/src/command/commands/mod.rs b/pumpkin/src/command/commands/mod.rs index 2fadce1a9..a5edb4bdd 100644 --- a/pumpkin/src/command/commands/mod.rs +++ b/pumpkin/src/command/commands/mod.rs @@ -2,6 +2,7 @@ pub mod cmd_clear; pub mod cmd_craft; pub mod cmd_echest; pub mod cmd_gamemode; +pub mod cmd_getblock; pub mod cmd_give; pub mod cmd_help; pub mod cmd_kick; diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index f5ffae7dd..a048bebc8 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use args::ConsumedArgs; use async_trait::async_trait; use commands::{ - cmd_clear, cmd_craft, cmd_echest, cmd_gamemode, cmd_give, cmd_help, cmd_kick, cmd_kill, - cmd_list, cmd_pumpkin, cmd_say, cmd_setblock, cmd_stop, cmd_teleport, cmd_worldborder, + cmd_clear, cmd_craft, cmd_echest, cmd_gamemode, cmd_getblock, cmd_give, cmd_help, cmd_kick, + cmd_kill, cmd_list, cmd_pumpkin, cmd_say, cmd_setblock, cmd_stop, cmd_teleport, + cmd_worldborder, }; use dispatcher::CommandError; use pumpkin_core::math::vector3::Vector3; @@ -108,6 +109,7 @@ pub fn default_dispatcher<'a>() -> Arc> { dispatcher.register(cmd_list::init_command_tree()); dispatcher.register(cmd_clear::init_command_tree()); dispatcher.register(cmd_setblock::init_command_tree()); + dispatcher.register(cmd_getblock::init_command_tree()); Arc::new(dispatcher) } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 893c28715..cfe7e4be8 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -28,9 +28,14 @@ use pumpkin_protocol::{ }, ClientPacket, VarInt, }; -use pumpkin_world::coordinates::ChunkRelativeBlockCoordinates; +use pumpkin_world::chunk::ChunkData; use pumpkin_world::level::Level; -use pumpkin_world::{block::block_registry::get_block_by_id, chunk::ChunkData}; +use pumpkin_world::{ + block::block_registry::{ + get_block_and_state_by_state_id, get_block_by_state_id, get_state_by_state_id, + }, + coordinates::ChunkRelativeBlockCoordinates, +}; use rand::{thread_rng, Rng}; use scoreboard::Scoreboard; use tokio::sync::{mpsc::Receiver, Mutex}; @@ -141,9 +146,9 @@ impl World { pub async fn get_top_block(&self, position: Vector2) -> i32 { for y in (-64..=319).rev() { let pos = WorldPosition(Vector3::new(position.x, y, position.z)); - let block = self.get_block(pos).await; + let block = self.get_block_state(pos).await; if let Some(block) = block { - if block.states[0].air { + if block.air { continue; } } @@ -608,7 +613,7 @@ impl World { } /// Sets a block - pub async fn set_block(&self, position: WorldPosition, block_state_id: u16) -> u16 { + pub async fn set_block_state(&self, position: WorldPosition, block_state_id: u16) -> u16 { let (chunk_coordinate, relative_coordinates) = position.chunk_and_chunk_relative_position(); // Since we divide by 16 remnant can never exceed u8 @@ -646,7 +651,7 @@ impl World { } pub async fn break_block(&self, position: WorldPosition, cause: Option<&Player>) { - let broken_block_state_id = self.set_block(position, 0).await; + let broken_block_state_id = self.set_block_state(position, 0).await; let particles_packet = CWorldEvent::new(2001, &position, broken_block_state_id.into(), false); @@ -660,11 +665,11 @@ impl World { } } - pub async fn get_block_id(&self, position: WorldPosition) -> u16 { + pub async fn get_block_state_id(&self, position: WorldPosition) -> u16 { let (chunk, relative) = position.chunk_and_chunk_relative_position(); let relative = ChunkRelativeBlockCoordinates::from(relative); let chunk = self.receive_chunk(chunk).await; - let chunk = chunk.read().await; + let chunk: tokio::sync::RwLockReadGuard = chunk.read().await; chunk.blocks.get_block(relative) } @@ -673,7 +678,28 @@ impl World { &self, position: WorldPosition, ) -> Option<&pumpkin_world::block::block_registry::Block> { - let block_id = self.get_block_id(position).await; - get_block_by_id(block_id) + let id = self.get_block_state_id(position).await; + get_block_by_state_id(id) + } + + /// Gets the Block state from the Block Registry, Returns None if the Block state has not been found + pub async fn get_block_state( + &self, + position: WorldPosition, + ) -> Option<&pumpkin_world::block::block_registry::State> { + let id = self.get_block_state_id(position).await; + get_state_by_state_id(id) + } + + /// Gets the Block + Block state from the Block Registry, Returns None if the Block state has not been found + pub async fn get_block_and_block_state( + &self, + position: WorldPosition, + ) -> Option<( + &pumpkin_world::block::block_registry::Block, + &pumpkin_world::block::block_registry::State, + )> { + let id = self.get_block_state_id(position).await; + get_block_and_state_by_state_id(id) } } From bb74fc9efc945345230395c238c27de4e39ee7a5 Mon Sep 17 00:00:00 2001 From: user622628252416 Date: Wed, 13 Nov 2024 17:49:31 +0100 Subject: [PATCH 5/6] fix typo --- pumpkin/src/command/commands/cmd_getblock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pumpkin/src/command/commands/cmd_getblock.rs b/pumpkin/src/command/commands/cmd_getblock.rs index 29549f6d8..896b78b33 100644 --- a/pumpkin/src/command/commands/cmd_getblock.rs +++ b/pumpkin/src/command/commands/cmd_getblock.rs @@ -10,7 +10,7 @@ use crate::entity::player::PermissionLvl; const NAMES: [&str; 2] = ["getblock", "gb"]; -const DESCRIPTION: &str = "Print a block to. For debug purposes"; +const DESCRIPTION: &str = "Print a block to chat. For debug purposes."; const ARG_BLOCK_POS: &str = "position"; From 3633aa34b10103880626de35b5c51da9aab818c1 Mon Sep 17 00:00:00 2001 From: user622628252416 Date: Thu, 14 Nov 2024 20:11:11 +0100 Subject: [PATCH 6/6] remove /getblock debug command --- pumpkin/src/command/commands/cmd_getblock.rs | 50 -------------------- pumpkin/src/command/commands/mod.rs | 1 - pumpkin/src/command/mod.rs | 6 +-- 3 files changed, 2 insertions(+), 55 deletions(-) delete mode 100644 pumpkin/src/command/commands/cmd_getblock.rs diff --git a/pumpkin/src/command/commands/cmd_getblock.rs b/pumpkin/src/command/commands/cmd_getblock.rs deleted file mode 100644 index 896b78b33..000000000 --- a/pumpkin/src/command/commands/cmd_getblock.rs +++ /dev/null @@ -1,50 +0,0 @@ -use async_trait::async_trait; -use pumpkin_core::text::TextComponent; - -use crate::command::args::arg_postition_block::BlockPosArgumentConsumer; -use crate::command::args::{ConsumedArgs, FindArg}; -use crate::command::tree::CommandTree; -use crate::command::tree_builder::{argument, require}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::entity::player::PermissionLvl; - -const NAMES: [&str; 2] = ["getblock", "gb"]; - -const DESCRIPTION: &str = "Print a block to chat. For debug purposes."; - -const ARG_BLOCK_POS: &str = "position"; - -struct GetblockExecutor; - -#[async_trait] -impl CommandExecutor for GetblockExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender<'a>, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?; - let world = sender.world().ok_or(CommandError::InvalidRequirement)?; - - if let Some(block) = world.get_block(pos).await { - sender - .send_message(TextComponent::text_string(format!( - "Block {} at {pos}", - block.name, - ))) - .await; - } - - Ok(()) - } -} - -pub fn init_command_tree<'a>() -> CommandTree<'a> { - CommandTree::new(NAMES, DESCRIPTION).with_child( - require(&|sender| { - sender.has_permission_lvl(PermissionLvl::Two) && sender.world().is_some() - }) - .with_child(argument(ARG_BLOCK_POS, &BlockPosArgumentConsumer).execute(&GetblockExecutor)), - ) -} diff --git a/pumpkin/src/command/commands/mod.rs b/pumpkin/src/command/commands/mod.rs index a5edb4bdd..2fadce1a9 100644 --- a/pumpkin/src/command/commands/mod.rs +++ b/pumpkin/src/command/commands/mod.rs @@ -2,7 +2,6 @@ pub mod cmd_clear; pub mod cmd_craft; pub mod cmd_echest; pub mod cmd_gamemode; -pub mod cmd_getblock; pub mod cmd_give; pub mod cmd_help; pub mod cmd_kick; diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index a048bebc8..f5ffae7dd 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -3,9 +3,8 @@ use std::sync::Arc; use args::ConsumedArgs; use async_trait::async_trait; use commands::{ - cmd_clear, cmd_craft, cmd_echest, cmd_gamemode, cmd_getblock, cmd_give, cmd_help, cmd_kick, - cmd_kill, cmd_list, cmd_pumpkin, cmd_say, cmd_setblock, cmd_stop, cmd_teleport, - cmd_worldborder, + cmd_clear, cmd_craft, cmd_echest, cmd_gamemode, cmd_give, cmd_help, cmd_kick, cmd_kill, + cmd_list, cmd_pumpkin, cmd_say, cmd_setblock, cmd_stop, cmd_teleport, cmd_worldborder, }; use dispatcher::CommandError; use pumpkin_core::math::vector3::Vector3; @@ -109,7 +108,6 @@ pub fn default_dispatcher<'a>() -> Arc> { dispatcher.register(cmd_list::init_command_tree()); dispatcher.register(cmd_clear::init_command_tree()); dispatcher.register(cmd_setblock::init_command_tree()); - dispatcher.register(cmd_getblock::init_command_tree()); Arc::new(dispatcher) }