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
Changes from 1 commit
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
Prev Previous commit
Next Next commit
fix particles, block state id, block coordinate rounding, WoldPositio…
…n formatting
  • Loading branch information
user622628252416 committed Nov 13, 2024
commit 653848dfa019b2bc9306d7fb366b878706228c23
2 changes: 1 addition & 1 deletion pumpkin-core/src/math/position.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
6 changes: 2 additions & 4 deletions pumpkin/src/client/player_packet.rs
Original file line number Diff line number Diff line change
@@ -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))
4 changes: 2 additions & 2 deletions pumpkin/src/command/args/arg_block.rs
Original file line number Diff line number Diff line change
@@ -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<Self::Data, CommandError> {
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."
))),
3 changes: 2 additions & 1 deletion pumpkin/src/command/args/coordinate.rs
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ impl<const IS_Y: bool> Coordinate<IS_Y> {
}
}

#[derive(Debug)]
pub enum BlockCoordinate {
Absolute(i32),
Relative(i32),
@@ -56,7 +57,7 @@ impl BlockCoordinate {
pub fn value(self, origin: Option<f64>) -> Option<i32> {
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),
}
}
}
2 changes: 1 addition & 1 deletion pumpkin/src/command/commands/cmd_clear.rs
Original file line number Diff line number Diff line change
@@ -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;
13 changes: 8 additions & 5 deletions pumpkin/src/command/commands/cmd_setblock.rs
Original file line number Diff line number Diff line change
@@ -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;

35 changes: 27 additions & 8 deletions pumpkin/src/world/mod.rs
Original file line number Diff line number Diff line change
@@ -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 {