Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /setblock command #263

Merged
merged 6 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pumpkin-core/src/math/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
20 changes: 20 additions & 0 deletions pumpkin-world/src/block/block_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
10 changes: 5 additions & 5 deletions pumpkin/src/client/player_packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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))
Expand Down Expand Up @@ -590,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
Expand Down
84 changes: 84 additions & 0 deletions pumpkin/src/command/args/arg_block.rs
Original file line number Diff line number Diff line change
@@ -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<ProtoCmdArgSuggestionType> {
None
}
}

#[async_trait]
impl ArgumentConsumer for BlockArgumentConsumer {
async fn consume<'a>(
&self,
_sender: &CommandSender<'a>,
_server: &'a Server,
args: &mut RawArgs<'a>,
) -> Option<Arg<'a>> {
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<Option<Vec<CommandSuggestion<'a>>>, CommandError> {
Ok(None)
}
}

impl DefaultNameArgConsumer for BlockArgumentConsumer {
fn default_name(&self) -> &'static str {
"block"
}

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<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Block(name)) => match block_registry::get_block(name) {
Some(block) => Ok(block),
None => Err(CommandError::GeneralCommandIssue(format!(
"Block {name} does not exist."
))),
},
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
}
}
}
10 changes: 8 additions & 2 deletions pumpkin/src/command/args/arg_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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<Self::Data, CommandError> {
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()))),
}
}
Expand Down
38 changes: 24 additions & 14 deletions pumpkin/src/command/args/arg_position_2d.rs
Original file line number Diff line number Diff line change
@@ -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,
};
Expand All @@ -10,6 +11,7 @@ use crate::command::CommandSender;
use crate::server::Server;

use super::super::args::ArgumentConsumer;
use super::coordinate::MaybeRelativeCoordinate;
use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser};

/// x and z coordinates only
Expand All @@ -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<Arg<'a>> {
let x_str = args.pop()?;
let z_str = args.pop()?;
let pos = MaybeRelativePosition2D::try_new(args.pop()?, args.pop()?)?;

let mut x = x_str.parse::<f64>().ok()?;
let mut z = z_str.parse::<f64>().ok()?;
let vec2 = pos.try_to_abolute(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>(
Expand All @@ -62,6 +54,24 @@ impl ArgumentConsumer for Position2DArgumentConsumer {
}
}

struct MaybeRelativePosition2D(
MaybeRelativeCoordinate<false>,
MaybeRelativeCoordinate<false>,
);

impl MaybeRelativePosition2D {
fn try_new(x: &str, z: &str) -> Option<Self> {
Some(Self(x.try_into().ok()?, z.try_into().ok()?))
}

fn try_to_abolute(self, origin: Option<Vector3<f64>>) -> Option<Vector2<f64>> {
Some(Vector2::new(
self.0.into_absolute(origin.map(|o| o.x))?,
self.1.into_absolute(origin.map(|o| o.z))?,
))
}
}

impl DefaultNameArgConsumer for Position2DArgumentConsumer {
fn default_name(&self) -> &'static str {
"pos2d"
Expand Down
57 changes: 13 additions & 44 deletions pumpkin/src/command/args/arg_position_3d.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::num::ParseFloatError;

use async_trait::async_trait;
use pumpkin_core::math::vector3::Vector3;
use pumpkin_protocol::client::play::{
Expand All @@ -12,6 +10,7 @@ use crate::command::CommandSender;
use crate::server::Server;

use super::super::args::ArgumentConsumer;
use super::coordinate::MaybeRelativeCoordinate;
use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser};

/// x, y and z coordinates
Expand All @@ -35,9 +34,9 @@ impl ArgumentConsumer for Position3DArgumentConsumer {
_server: &'a Server,
args: &mut RawArgs<'a>,
) -> Option<Arg<'a>> {
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))
}
Expand All @@ -52,9 +51,13 @@ impl ArgumentConsumer for Position3DArgumentConsumer {
}
}

struct Position3D(Coordinate<false>, Coordinate<true>, Coordinate<false>);
struct MaybeRelativePosition3D(
MaybeRelativeCoordinate<false>,
MaybeRelativeCoordinate<true>,
MaybeRelativeCoordinate<false>,
);

impl Position3D {
impl MaybeRelativePosition3D {
fn try_new(x: &str, y: &str, z: &str) -> Option<Self> {
Some(Self(
x.try_into().ok()?,
Expand All @@ -63,11 +66,11 @@ impl Position3D {
))
}

fn try_get_values(self, origin: Option<Vector3<f64>>) -> Option<Vector3<f64>> {
fn try_to_absolute(self, origin: Option<Vector3<f64>>) -> Option<Vector3<f64>> {
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))?,
))
}
}
Expand All @@ -82,40 +85,6 @@ impl DefaultNameArgConsumer for Position3DArgumentConsumer {
}
}

enum Coordinate<const IS_Y: bool> {
Absolute(f64),
Relative(f64),
}

impl<const IS_Y: bool> TryFrom<&str> for Coordinate<IS_Y> {
type Error = ParseFloatError;

fn try_from(s: &str) -> Result<Self, Self::Error> {
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<const IS_Y: bool> Coordinate<IS_Y> {
fn value(self, origin: Option<f64>) -> Option<f64> {
match self {
Self::Absolute(v) => Some(v),
Self::Relative(offset) => Some(origin? + offset),
}
}
}

impl<'a> FindArg<'a> for Position3DArgumentConsumer {
type Data = Vector3<f64>;

Expand Down
Loading