From 1edbfa7289866efd27cfee539e1ae1cdd9a3bca7 Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Mon, 12 May 2025 19:42:55 +0200 Subject: [PATCH] Implement Piston 1/2 Not fully done --- Cargo.lock | 24 +- Cargo.toml | 2 +- pumpkin-data/build/block.rs | 26 + pumpkin-data/src/block_state.rs | 10 + pumpkin-data/src/lib.rs | 2 +- .../src/client/play/chunk_data.rs | 1 + pumpkin-world/Cargo.toml | 6 +- pumpkin-world/src/block/entities/mod.rs | 8 +- pumpkin-world/src/block/entities/piston.rs | 153 ++++++ pumpkin-world/src/block/mod.rs | 23 + pumpkin-world/src/chunk/format/anvil.rs | 2 +- pumpkin-world/src/chunk/format/mod.rs | 6 +- .../src/chunk/io/chunk_file_manager.rs | 2 +- pumpkin-world/src/chunk/mod.rs | 7 +- .../src/generation/implementation/mod.rs | 4 +- pumpkin-world/src/lib.rs | 1 + pumpkin-world/src/world.rs | 62 +++ pumpkin/src/block/blocks/barrel.rs | 4 +- pumpkin/src/block/blocks/bed.rs | 2 +- pumpkin/src/block/blocks/cactus.rs | 2 +- pumpkin/src/block/blocks/chest.rs | 3 +- pumpkin/src/block/blocks/dirt_path.rs | 2 +- pumpkin/src/block/blocks/doors.rs | 2 +- pumpkin/src/block/blocks/farmland.rs | 2 +- pumpkin/src/block/blocks/fence_gates.rs | 2 +- pumpkin/src/block/blocks/jukebox.rs | 3 +- pumpkin/src/block/blocks/mod.rs | 1 + pumpkin/src/block/blocks/note.rs | 6 +- pumpkin/src/block/blocks/piston/mod.rs | 238 +++++++++ pumpkin/src/block/blocks/piston/piston.rs | 462 ++++++++++++++++++ .../block/blocks/piston/piston_extension.rs | 46 ++ .../src/block/blocks/piston/piston_head.rs | 10 + pumpkin/src/block/blocks/pumpkin.rs | 3 +- pumpkin/src/block/blocks/redstone/buttons.rs | 2 +- pumpkin/src/block/blocks/redstone/lever.rs | 3 +- pumpkin/src/block/blocks/redstone/mod.rs | 22 +- pumpkin/src/block/blocks/redstone/observer.rs | 7 +- pumpkin/src/block/blocks/redstone/piston.rs | 4 +- .../blocks/redstone/rails/activator_rail.rs | 2 +- .../src/block/blocks/redstone/rails/common.rs | 4 +- .../blocks/redstone/rails/detector_rail.rs | 2 +- .../blocks/redstone/rails/powered_rail.rs | 2 +- .../src/block/blocks/redstone/rails/rail.rs | 2 +- .../block/blocks/redstone/redstone_lamp.rs | 8 +- .../block/blocks/redstone/redstone_torch.rs | 4 +- .../block/blocks/redstone/redstone_wire.rs | 2 +- pumpkin/src/block/blocks/redstone/repeater.rs | 7 +- pumpkin/src/block/blocks/redstone/turbo.rs | 4 +- pumpkin/src/block/blocks/stairs.rs | 2 +- pumpkin/src/block/blocks/sugar_cane.rs | 2 +- pumpkin/src/block/blocks/tnt.rs | 3 +- pumpkin/src/block/fluids/flowing_fluid.rs | 4 +- pumpkin/src/block/mod.rs | 5 + pumpkin/src/block/pumpkin_block.rs | 3 +- pumpkin/src/block/registry.rs | 3 +- pumpkin/src/command/commands/fill.rs | 2 +- pumpkin/src/command/commands/setblock.rs | 2 +- pumpkin/src/item/items/axe.rs | 2 +- pumpkin/src/item/items/bucket.rs | 3 +- pumpkin/src/item/items/flint_and_steel.rs | 2 +- pumpkin/src/item/items/hoe.rs | 2 +- pumpkin/src/item/items/honeycomb.rs | 2 +- pumpkin/src/item/items/shovel.rs | 2 +- pumpkin/src/net/packet/play.rs | 3 +- pumpkin/src/world/mod.rs | 92 ++-- 65 files changed, 1202 insertions(+), 134 deletions(-) create mode 100644 pumpkin-world/src/block/entities/piston.rs create mode 100644 pumpkin-world/src/world.rs create mode 100644 pumpkin/src/block/blocks/piston/mod.rs create mode 100644 pumpkin/src/block/blocks/piston/piston.rs create mode 100644 pumpkin/src/block/blocks/piston/piston_extension.rs create mode 100644 pumpkin/src/block/blocks/piston/piston_head.rs diff --git a/Cargo.lock b/Cargo.lock index 469ae381b..42a6aa4dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -323,18 +323,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstyle", "clap_lex", @@ -1365,12 +1365,12 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] @@ -1415,9 +1415,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lru" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +checksum = "9f8cc7106155f10bdf99a6f379688f543ad6596a415375b36a59a054ceda1198" dependencies = [ "hashbrown 0.15.3", ] @@ -2022,8 +2022,10 @@ name = "pumpkin-world" version = "0.1.0-dev+1.21.5" dependencies = [ "async-trait", + "bitflags 2.9.0", "bytes", "criterion", + "crossbeam", "dashmap", "derive-getters", "enum_dispatch", @@ -2757,9 +2759,9 @@ checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.3", diff --git a/Cargo.toml b/Cargo.toml index cb356c6a9..26a1b9904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,4 +60,4 @@ serde_json = "1.0" async-trait = "0.1" -tempfile = "3.19.1" +tempfile = "3.20.0" diff --git a/pumpkin-data/build/block.rs b/pumpkin-data/build/block.rs index 874bf032e..5e770063d 100644 --- a/pumpkin-data/build/block.rs +++ b/pumpkin-data/build/block.rs @@ -357,12 +357,35 @@ pub struct BlockState { pub state_flags: u8, pub instrument: String, // TODO: make this an enum pub luminance: u8, + pub piston_behavior: PistonBehavior, pub hardness: f32, pub collision_shapes: Vec, pub opacity: Option, pub block_entity_type: Option, } +#[derive(Deserialize, Clone, Debug)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum PistonBehavior { + Normal, + Destroy, + Block, + Ignore, + PushOnly, +} + +impl PistonBehavior { + fn to_tokens(&self) -> TokenStream { + match self { + PistonBehavior::Normal => quote! { PistonBehavior::Normal }, + PistonBehavior::Destroy => quote! { PistonBehavior::Destroy }, + PistonBehavior::Block => quote! { PistonBehavior::Block }, + PistonBehavior::Ignore => quote! { PistonBehavior::Ignore }, + PistonBehavior::PushOnly => quote! { PistonBehavior::PushOnly }, + } + } +} + #[derive(Deserialize, Clone, Debug)] pub struct BlockStateRef { pub id: u16, @@ -397,6 +420,7 @@ impl BlockState { .collision_shapes .iter() .map(|shape_id| LitInt::new(&shape_id.to_string(), Span::call_site())); + let piston_behavior = &self.piston_behavior.to_tokens(); tokens.extend(quote! { BlockState { @@ -404,6 +428,7 @@ impl BlockState { state_flags: #state_flags, instrument: #instrument, luminance: #luminance, + piston_behavior: #piston_behavior, hardness: #hardness, collision_shapes: &[#(#collision_shapes),*], opacity: #opacity, @@ -1270,6 +1295,7 @@ pub(crate) fn build() -> TokenStream { quote! { use crate::{BlockState, BlockStateRef, Block, CollisionShape}; + use crate::block_state::PistonBehavior; use pumpkin_util::math::int_provider::{UniformIntProvider, IntProvider, NormalIntProvider}; use pumpkin_util::loot_table::*; use pumpkin_util::math::experience::Experience; diff --git a/pumpkin-data/src/block_state.rs b/pumpkin-data/src/block_state.rs index 02982a8c0..e3faa5368 100644 --- a/pumpkin-data/src/block_state.rs +++ b/pumpkin-data/src/block_state.rs @@ -4,6 +4,7 @@ pub struct BlockState { pub state_flags: u8, pub instrument: &'static str, pub luminance: u8, + pub piston_behavior: PistonBehavior, pub hardness: f32, pub collision_shapes: &'static [u16], /// u8::MAX is used as None @@ -12,6 +13,15 @@ pub struct BlockState { pub block_entity_type: u16, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PistonBehavior { + Normal, + Destroy, + Block, + Ignore, + PushOnly, +} + // Add your methods here impl BlockState { pub const fn is_air(&self) -> bool { diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index b3c6baab4..c0aa6e105 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -122,7 +122,7 @@ pub mod noise_router { include!(concat!(env!("OUT_DIR"), "/noise_router.rs")); } -mod block_state; +pub mod block_state; mod blocks; mod collision_shape; diff --git a/pumpkin-protocol/src/client/play/chunk_data.rs b/pumpkin-protocol/src/client/play/chunk_data.rs index d63c01171..a28a21f58 100644 --- a/pumpkin-protocol/src/client/play/chunk_data.rs +++ b/pumpkin-protocol/src/client/play/chunk_data.rs @@ -116,6 +116,7 @@ impl ClientPacket for CChunkData<'_> { // TODO: block entities write.write_var_int(&VarInt(self.0.block_entities.len() as i32))?; for block_entity in self.0.block_entities.values() { + let block_entity = &block_entity.1; let chunk_data_nbt = block_entity.chunk_data_nbt(); let pos = block_entity.get_position(); let block_entity_id = block_entity.get_id(); diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index da3ebd7bf..c62885e37 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -22,9 +22,11 @@ thiserror.workspace = true serde.workspace = true serde_json.workspace = true log.workspace = true +crossbeam.workspace = true num-derive = "0.4" sha2 = "0.10" +bitflags = "2.9.0" dashmap = "6.1" @@ -42,13 +44,13 @@ enum_dispatch = "0.3" derive-getters = "0.5.0" thread_local = "1.1.8" -lru = "0.13.0" +lru = "0.14.0" tokio-util = { version = "0.7.15", features = ["rt"] } [dev-dependencies] criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } -temp-dir = "0.1.14" +temp-dir = "0.1.16" # Print log info inside tests when needed env_logger = "0.11.8" # Allows us to modify the config diff --git a/pumpkin-world/src/block/entities/mod.rs b/pumpkin-world/src/block/entities/mod.rs index 71fba448e..42d2d1fda 100644 --- a/pumpkin-world/src/block/entities/mod.rs +++ b/pumpkin-world/src/block/entities/mod.rs @@ -5,16 +5,18 @@ use barrel::BarrelBlockEntity; use bed::BedBlockEntity; use chest::ChestBlockEntity; use comparator::ComparatorBlockEntity; +use piston::PistonBlockEntity; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::math::position::BlockPos; use sign::SignBlockEntity; -use crate::inventory::Inventory; +use crate::{inventory::Inventory, world::SimpleWorld}; pub mod barrel; pub mod bed; pub mod chest; pub mod comparator; +pub mod piston; pub mod sign; //TODO: We need a mark_dirty for chests @@ -24,6 +26,7 @@ pub trait BlockEntity: Send + Sync { fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self where Self: Sized; + async fn tick(&self, _world: &Arc) {} fn identifier(&self) -> &'static str; fn get_position(&self) -> BlockPos; async fn write_internal(&self, nbt: &mut NbtCompound) { @@ -73,6 +76,9 @@ pub fn block_entity_from_nbt(nbt: &NbtCompound) -> Option> BarrelBlockEntity::ID => Some(Arc::new(block_entity_from_generic::( nbt, ))), + PistonBlockEntity::ID => Some(Arc::new(block_entity_from_generic::( + nbt, + ))), _ => None, } } diff --git a/pumpkin-world/src/block/entities/piston.rs b/pumpkin-world/src/block/entities/piston.rs new file mode 100644 index 000000000..7554b3b2c --- /dev/null +++ b/pumpkin-world/src/block/entities/piston.rs @@ -0,0 +1,153 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use crossbeam::atomic::AtomicCell; +use pumpkin_data::{ + Block, BlockState, + block_properties::{get_block_by_state_id, get_state_by_state_id}, +}; +use pumpkin_nbt::compound::NbtCompound; +use pumpkin_util::math::position::BlockPos; + +use crate::{ + block::BlockDirection, + world::{BlockFlags, SimpleWorld}, +}; + +use super::BlockEntity; + +pub struct PistonBlockEntity { + pub position: BlockPos, + pub pushed_block_state: BlockState, + pub facing: BlockDirection, + pub current_progress: AtomicCell, + pub last_progress: AtomicCell, + pub extending: bool, + pub source: bool, +} + +impl PistonBlockEntity { + pub const ID: &'static str = "minecraft:piston"; + + pub async fn finish(&self, world: Arc) { + if self.last_progress.load() < 1.0 { + let pos = self.position; + world.remove_block_entity(&pos).await; + if world.get_block(&pos).await.unwrap() == Block::MOVING_PISTON { + let state = if self.source { + Block::AIR.default_state_id + } else { + self.pushed_block_state.id + }; + world + .clone() + .set_block_state(&pos, state, BlockFlags::NOTIFY_ALL) + .await; + world + .update_neighbor(&pos, &get_block_by_state_id(state).unwrap()) + .await; + } + } + } +} + +const FACING: &str = "facing"; +const LAST_PROGRESS: &str = "progress"; +const EXTENDING: &str = "extending"; +const SOURCE: &str = "source"; + +#[async_trait] +impl BlockEntity for PistonBlockEntity { + fn identifier(&self) -> &'static str { + Self::ID + } + + fn get_position(&self) -> BlockPos { + self.position + } + + async fn tick(&self, world: &Arc) { + let current_progress = self.current_progress.load(); + self.last_progress.store(current_progress); + if current_progress >= 1.0 { + let pos = self.position; + world.remove_block_entity(&pos).await; + if world.get_block(&pos).await.unwrap() == Block::MOVING_PISTON { + if self.pushed_block_state.is_air() { + world + .clone() + .set_block_state( + &pos, + self.pushed_block_state.id, + BlockFlags::FORCE_STATE | BlockFlags::MOVED, + ) + .await; + } else { + world + .clone() + .set_block_state( + &pos, + self.pushed_block_state.id, + BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, + ) + .await; + world + .clone() + .update_neighbor( + &pos, + &get_block_by_state_id(self.pushed_block_state.id).unwrap(), + ) + .await; + } + } + } + self.current_progress.store(current_progress + 0.5); + if current_progress + 0.5 >= 1.0 { + self.current_progress.store(1.0); + } + } + + fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self + where + Self: Sized, + { + // TODO + let pushed_block_state = get_state_by_state_id(Block::AIR.default_state_id).unwrap(); + let facing = nbt.get_byte(FACING).unwrap_or(0); + let last_progress = nbt.get_float(LAST_PROGRESS).unwrap_or(0.0); + let extending = nbt.get_bool(EXTENDING).unwrap_or(false); + let source = nbt.get_bool(SOURCE).unwrap_or(false); + Self { + pushed_block_state, + position, + facing: BlockDirection::from_index(facing as u8).unwrap_or(BlockDirection::Down), + current_progress: last_progress.into(), + last_progress: last_progress.into(), + extending, + source, + } + } + + async fn write_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) { + // TODO: pushed_block_state + nbt.put_byte(FACING, self.facing.to_index() as i8); + nbt.put_float(LAST_PROGRESS, self.last_progress.load()); + nbt.put_bool(EXTENDING, self.extending); + nbt.put_bool(SOURCE, self.source); + } + + fn chunk_data_nbt(&self) -> Option { + let mut nbt = NbtCompound::new(); + // TODO: pushed_block_state + nbt.put_byte(FACING, self.facing.to_index() as i8); + nbt.put_float(LAST_PROGRESS, self.last_progress.load()); + nbt.put_bool(EXTENDING, self.extending); + nbt.put_bool(SOURCE, self.source); + // TODO: duplicated code :c + Some(nbt) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/pumpkin-world/src/block/mod.rs b/pumpkin-world/src/block/mod.rs index a882b3e9e..329613349 100644 --- a/pumpkin-world/src/block/mod.rs +++ b/pumpkin-world/src/block/mod.rs @@ -44,6 +44,29 @@ pub struct BlockStateCodec { } impl BlockDirection { + pub fn to_index(&self) -> u8 { + match self { + BlockDirection::Down => 0, + BlockDirection::Up => 1, + BlockDirection::North => 2, + BlockDirection::South => 3, + BlockDirection::West => 4, + BlockDirection::East => 5, + } + } + + pub fn from_index(index: u8) -> Option { + match index { + 0 => Some(Self::Down), + 1 => Some(Self::Up), + 2 => Some(Self::North), + 3 => Some(Self::South), + 4 => Some(Self::West), + 5 => Some(Self::East), + _ => None, + } + } + pub fn to_offset(&self) -> Vector3 { match self { BlockDirection::Down => (0, -1, 0), diff --git a/pumpkin-world/src/chunk/format/anvil.rs b/pumpkin-world/src/chunk/format/anvil.rs index a7289c5ad..286edebb1 100644 --- a/pumpkin-world/src/chunk/format/anvil.rs +++ b/pumpkin-world/src/chunk/format/anvil.rs @@ -885,7 +885,7 @@ pub async fn chunk_to_bytes(chunk_data: &ChunkData) -> Result, ChunkSeri block_entities: join_all(chunk_data.block_entities.values().map( |block_entity| async move { let mut nbt = NbtCompound::new(); - block_entity.write_internal(&mut nbt).await; + block_entity.1.write_internal(&mut nbt).await; nbt }, )) diff --git a/pumpkin-world/src/chunk/format/mod.rs b/pumpkin-world/src/chunk/format/mod.rs index b4d77e558..86e17301b 100644 --- a/pumpkin-world/src/chunk/format/mod.rs +++ b/pumpkin-world/src/chunk/format/mod.rs @@ -8,7 +8,7 @@ use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use serde::{Deserialize, Serialize}; use super::{ - ChunkData, ChunkHeightmaps, ChunkLightEngine, ChunkParsingError, ChunkSections, ScheduledTick, + ChunkData, ChunkHeightmaps, ChunkLight, ChunkParsingError, ChunkSections, ScheduledTick, SubChunk, TickPriority, palette::{BiomePalette, BlockPalette}, }; @@ -82,7 +82,7 @@ impl ChunkData { ))); } - let light_engine = ChunkLightEngine { + let light_engine = ChunkLight { block_light: (0..chunk_data.sections.len() + 2) .map(|index| { chunk_data @@ -167,7 +167,7 @@ impl ChunkData { for nbt in chunk_data.block_entities { let block_entity = block_entity_from_nbt(&nbt); if let Some(block_entity) = block_entity { - block_entities.insert(block_entity.get_position(), block_entity); + block_entities.insert(block_entity.get_position(), (nbt, block_entity)); } } block_entities diff --git a/pumpkin-world/src/chunk/io/chunk_file_manager.rs b/pumpkin-world/src/chunk/io/chunk_file_manager.rs index 124cc4768..5ec7f0449 100644 --- a/pumpkin-world/src/chunk/io/chunk_file_manager.rs +++ b/pumpkin-world/src/chunk/io/chunk_file_manager.rs @@ -297,7 +297,7 @@ where let mut serializer = chunk_serializer.write().await; for chunk_lock in chunk_locks { let mut chunk = chunk_lock.write().await; - let chunk_is_dirty = chunk.dirty || chunk.block_entities.values().any(|block_entity| block_entity.is_dirty()); + let chunk_is_dirty = chunk.dirty || chunk.block_entities.values().any(|block_entity| block_entity.1.is_dirty()); // Edge case: this chunk is loaded while we were saving, mark it as cleaned since we are // updating what we will write here chunk.dirty = false; diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index 94a6ce97f..2c4f19ea9 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -1,5 +1,6 @@ use crate::block::entities::BlockEntity; use palette::{BiomePalette, BlockPalette}; +use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::nbt_long_array; use pumpkin_util::math::{position::BlockPos, vector2::Vector2}; use serde::{Deserialize, Serialize}; @@ -115,8 +116,8 @@ pub struct ChunkData { pub position: Vector2, pub block_ticks: Vec, pub fluid_ticks: Vec, - pub block_entities: HashMap>, - pub light_engine: ChunkLightEngine, + pub block_entities: HashMap)>, + pub light_engine: ChunkLight, pub dirty: bool, } @@ -166,7 +167,7 @@ pub struct SubChunk { } #[derive(Debug, Default)] -pub struct ChunkLightEngine { +pub struct ChunkLight { pub sky_light: Box<[LightContainer]>, pub block_light: Box<[LightContainer]>, } diff --git a/pumpkin-world/src/generation/implementation/mod.rs b/pumpkin-world/src/generation/implementation/mod.rs index 0fca959da..ebc9da4b9 100644 --- a/pumpkin-world/src/generation/implementation/mod.rs +++ b/pumpkin-world/src/generation/implementation/mod.rs @@ -6,7 +6,7 @@ use super::{ noise_router::proto_noise_router::ProtoNoiseRouters, settings::{GENERATION_SETTINGS, GeneratorSetting}, }; -use crate::chunk::ChunkLightEngine; +use crate::chunk::ChunkLight; use crate::chunk::format::LightContainer; use crate::{ chunk::{ @@ -81,7 +81,7 @@ impl WorldGenerator for VanillaGenerator { } ChunkData { - light_engine: ChunkLightEngine { + light_engine: ChunkLight { sky_light: (0..sections.sections.len() + 2) .map(|_| LightContainer::new_filled(15)) .collect(), diff --git a/pumpkin-world/src/lib.rs b/pumpkin-world/src/lib.rs index 46cb932b5..c3f259693 100644 --- a/pumpkin-world/src/lib.rs +++ b/pumpkin-world/src/lib.rs @@ -13,6 +13,7 @@ pub mod inventory; pub mod item; pub mod level; mod lock; +pub mod world; pub mod world_info; pub type BlockId = u16; diff --git a/pumpkin-world/src/world.rs b/pumpkin-world/src/world.rs new file mode 100644 index 000000000..61de2b9f0 --- /dev/null +++ b/pumpkin-world/src/world.rs @@ -0,0 +1,62 @@ +use std::sync::Arc; + +use crate::block::BlockDirection; +use async_trait::async_trait; +use bitflags::bitflags; +use pumpkin_util::math::position::BlockPos; +use thiserror::Error; + +use crate::BlockStateId; + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct BlockFlags: u32 { + const NOTIFY_NEIGHBORS = 0b000_0000_0001; + const NOTIFY_LISTENERS = 0b000_0000_0010; + const NOTIFY_ALL = 0b000_0000_0011; + const FORCE_STATE = 0b000_0000_0100; + const SKIP_DROPS = 0b000_0000_1000; + const MOVED = 0b000_0001_0000; + const SKIP_REDSTONE_WIRE_STATE_REPLACEMENT = 0b000_0010_0000; + const SKIP_BLOCK_ENTITY_REPLACED_CALLBACK = 0b000_0100_0000; + const SKIP_BLOCK_ADDED_CALLBACK = 0b000_1000_0000; + } +} + +#[derive(Debug, Error)] +pub enum GetBlockError { + InvalidBlockId, + BlockOutOfWorldBounds, +} + +impl std::fmt::Display for GetBlockError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +#[async_trait] +pub trait SimpleWorld: Send + Sync { + async fn set_block_state( + self: Arc, + position: &BlockPos, + block_state_id: BlockStateId, + flags: BlockFlags, + ) -> BlockStateId; + + async fn get_block(&self, position: &BlockPos) -> Result; + + async fn update_neighbor( + self: Arc, + neighbor_block_pos: &BlockPos, + source_block: &pumpkin_data::Block, + ); + + async fn update_neighbors( + self: Arc, + block_pos: &BlockPos, + except: Option, + ); + + async fn remove_block_entity(&self, block_pos: &BlockPos); +} diff --git a/pumpkin/src/block/blocks/barrel.rs b/pumpkin/src/block/blocks/barrel.rs index 38958ee51..a75d0d6a3 100644 --- a/pumpkin/src/block/blocks/barrel.rs +++ b/pumpkin/src/block/blocks/barrel.rs @@ -57,7 +57,7 @@ impl PumpkinBlock for BarrelBlock { world: &Arc, ) { if let Some(block_entity) = world.get_block_entity(&location).await { - if let Some(inventory) = block_entity.get_inventory() { + if let Some(inventory) = block_entity.1.get_inventory() { player .open_handled_screen(&BarrelScreenFactory(inventory)) .await; @@ -75,7 +75,7 @@ impl PumpkinBlock for BarrelBlock { world: &Arc, ) -> BlockActionResult { if let Some(block_entity) = world.get_block_entity(&location).await { - if let Some(inventory) = block_entity.get_inventory() { + if let Some(inventory) = block_entity.1.get_inventory() { player .open_handled_screen(&BarrelScreenFactory(inventory)) .await; diff --git a/pumpkin/src/block/blocks/bed.rs b/pumpkin/src/block/blocks/bed.rs index d9cdfd031..ec1a99a80 100644 --- a/pumpkin/src/block/blocks/bed.rs +++ b/pumpkin/src/block/blocks/bed.rs @@ -9,12 +9,12 @@ use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::entity::player::Player; -use crate::world::BlockFlags; use pumpkin_protocol::server::play::SUseItemOn; use crate::server::Server; diff --git a/pumpkin/src/block/blocks/cactus.rs b/pumpkin/src/block/blocks/cactus.rs index 7598955a6..a7500b616 100644 --- a/pumpkin/src/block/blocks/cactus.rs +++ b/pumpkin/src/block/blocks/cactus.rs @@ -12,11 +12,11 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::chunk::TickPriority; +use pumpkin_world::world::BlockFlags; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; #[pumpkin_block("minecraft:cactus")] diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index d32fc289c..ad3b8428c 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -13,10 +13,11 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::block::entities::chest::ChestBlockEntity; +use pumpkin_world::world::BlockFlags; use crate::block::BlockIsReplacing; use crate::entity::EntityBase; -use crate::world::{BlockFlags, World}; +use crate::world::World; use crate::{ block::{pumpkin_block::PumpkinBlock, registry::BlockActionResult}, entity::player::Player, diff --git a/pumpkin/src/block/blocks/dirt_path.rs b/pumpkin/src/block/blocks/dirt_path.rs index 8c8f9f3b3..d7d2ae1cb 100644 --- a/pumpkin/src/block/blocks/dirt_path.rs +++ b/pumpkin/src/block/blocks/dirt_path.rs @@ -4,7 +4,6 @@ use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use async_trait::async_trait; use pumpkin_data::Block; @@ -14,6 +13,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::chunk::TickPriority; +use pumpkin_world::world::BlockFlags; #[pumpkin_block("minecraft:dirt_path")] pub struct DirtPathBlock; diff --git a/pumpkin/src/block/blocks/doors.rs b/pumpkin/src/block/blocks/doors.rs index 364437780..284c4357d 100644 --- a/pumpkin/src/block/blocks/doors.rs +++ b/pumpkin/src/block/blocks/doors.rs @@ -14,6 +14,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::block::HorizontalFacingExt; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; @@ -21,7 +22,6 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::block::registry::BlockActionResult; use crate::entity::player::Player; -use crate::world::BlockFlags; use pumpkin_data::item::Item; use pumpkin_protocol::server::play::SUseItemOn; diff --git a/pumpkin/src/block/blocks/farmland.rs b/pumpkin/src/block/blocks/farmland.rs index f589a1d66..dfccafb71 100644 --- a/pumpkin/src/block/blocks/farmland.rs +++ b/pumpkin/src/block/blocks/farmland.rs @@ -4,7 +4,6 @@ use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use async_trait::async_trait; use pumpkin_data::Block; @@ -14,6 +13,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::chunk::TickPriority; +use pumpkin_world::world::BlockFlags; #[pumpkin_block("minecraft:farmland")] pub struct FarmLandBlock; diff --git a/pumpkin/src/block/blocks/fence_gates.rs b/pumpkin/src/block/blocks/fence_gates.rs index 857225a64..535a69b00 100644 --- a/pumpkin/src/block/blocks/fence_gates.rs +++ b/pumpkin/src/block/blocks/fence_gates.rs @@ -11,11 +11,11 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::block::registry::BlockActionResult; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use pumpkin_data::item::Item; diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index edad608b9..5f78ea8f2 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -4,7 +4,7 @@ use crate::block::pumpkin_block::PumpkinBlock; use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::server::Server; -use crate::world::{BlockFlags, World}; +use crate::world::World; use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_data::world::WorldEvent; @@ -15,6 +15,7 @@ use pumpkin_data::{ use pumpkin_macros::pumpkin_block; use pumpkin_registry::SYNCED_REGISTRIES; use pumpkin_util::math::position::BlockPos; +use pumpkin_world::world::BlockFlags; #[pumpkin_block("minecraft:jukebox")] pub struct JukeboxBlock; diff --git a/pumpkin/src/block/blocks/mod.rs b/pumpkin/src/block/blocks/mod.rs index 7ad1697f0..7ce8f8da0 100644 --- a/pumpkin/src/block/blocks/mod.rs +++ b/pumpkin/src/block/blocks/mod.rs @@ -16,6 +16,7 @@ pub mod jukebox; pub mod logs; pub mod nether_portal; pub mod note; +pub mod piston; pub mod pumpkin; pub mod redstone; pub mod signs; diff --git a/pumpkin/src/block/blocks/note.rs b/pumpkin/src/block/blocks/note.rs index 6698ddeb6..d43bdedc6 100644 --- a/pumpkin/src/block/blocks/note.rs +++ b/pumpkin/src/block/blocks/note.rs @@ -18,11 +18,9 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; -use crate::{ - block::pumpkin_block::PumpkinBlock, - world::{BlockFlags, World}, -}; +use crate::{block::pumpkin_block::PumpkinBlock, world::World}; use super::redstone::block_receives_redstone_power; diff --git a/pumpkin/src/block/blocks/piston/mod.rs b/pumpkin/src/block/blocks/piston/mod.rs new file mode 100644 index 000000000..4db46605a --- /dev/null +++ b/pumpkin/src/block/blocks/piston/mod.rs @@ -0,0 +1,238 @@ +use piston::PistonBlock; +use pumpkin_data::Block; +use pumpkin_data::block_state::PistonBehavior; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::block::BlockDirection; + +use crate::world::World; + +#[expect(clippy::module_inception)] +pub(crate) mod piston; +pub(crate) mod piston_extension; +pub(crate) mod piston_head; + +const MAX_MOVABLE_BLOCKS: usize = 12; + +pub struct PistonHandler<'a> { + world: &'a World, + pos_from: BlockPos, + retracted: bool, + pos_to: BlockPos, + motion_direction: BlockDirection, + pub moved_blocks: Vec, + pub broken_blocks: Vec, + _piston_direction: BlockDirection, +} + +impl<'a> PistonHandler<'a> { + pub fn new(world: &'a World, pos: BlockPos, dir: BlockDirection, retracted: bool) -> Self { + let motion_direction; + let pos_to = if retracted { + motion_direction = dir; + pos.offset_dir(dir.to_offset(), 1) + } else { + motion_direction = dir.opposite(); + pos.offset_dir(dir.to_offset(), 2) + }; + PistonHandler { + world, + pos_from: pos, + _piston_direction: dir, + retracted, + motion_direction, + pos_to, + moved_blocks: Vec::new(), + broken_blocks: Vec::new(), + } + } + + pub async fn calculate_push(&mut self) -> bool { + self.moved_blocks.clear(); + self.broken_blocks.clear(); + let (block, block_state) = self + .world + .get_block_and_block_state(&self.pos_to) + .await + .unwrap(); + if !PistonBlock::is_movable( + &block, + &block_state, + // self.world, + // self.pos_to, + // self.motion_direction, + // false, + // self.piston_direction, + ) { + if self.retracted && block_state.piston_behavior == PistonBehavior::Destroy { + self.broken_blocks.push(self.pos_to); + return true; + } + return false; + } + if !self.try_move(self.pos_to, self.motion_direction).await { + return false; + } + for block_pos in self.moved_blocks.clone() { + let block = self.world.get_block(&block_pos).await.unwrap(); + if Self::is_block_sticky(&block) + && !self.try_move_adjacent_block(&block, block_pos).await + { + return false; + } + } + true + } + + fn is_block_sticky(block: &Block) -> bool { + block == &Block::SLIME_BLOCK || block == &Block::HONEY_BLOCK + } + + fn is_adjacent_block_stuck(state: &Block, adjacent_state: &Block) -> bool { + if state == &Block::HONEY_BLOCK && adjacent_state == &Block::SLIME_BLOCK { + return false; + } + if state == &Block::SLIME_BLOCK && adjacent_state == &Block::HONEY_BLOCK { + return false; + } + Self::is_block_sticky(state) || Self::is_block_sticky(adjacent_state) + } + + async fn try_move(&mut self, pos: BlockPos, _dir: BlockDirection) -> bool { + let (mut block, mut block_state) = + self.world.get_block_and_block_state(&pos).await.unwrap(); + if block_state.is_air() { + return true; + } + if !PistonBlock::is_movable( + &block, + &block_state, + // self.world, + // pos, + // self.motion_direction, + // false, + // dir, + ) { + return true; + } + if pos == self.pos_from { + return true; + } + if self.moved_blocks.contains(&pos) { + return true; + } + let mut i = 1; + if i + self.moved_blocks.len() > MAX_MOVABLE_BLOCKS { + return false; + } + while Self::is_block_sticky(&block) { + let block_pos = pos.offset_dir(self.motion_direction.opposite().to_offset(), i as i32); + let block2 = block; + (block, block_state) = self + .world + .get_block_and_block_state(&block_pos) + .await + .unwrap(); + if block_state.is_air() + || !Self::is_adjacent_block_stuck(&block2, &block) + || !PistonBlock::is_movable( + &block, + &block_state, + // self.world, + // block_pos, + // self.motion_direction, + // false, + // self.motion_direction.opposite(), + ) + || block_pos == self.pos_from + { + break; + } + i += 1; + if i + self.moved_blocks.len() > MAX_MOVABLE_BLOCKS { + return false; + } + } + let mut j = 0; + for k in (0..i).rev() { + self.moved_blocks + .push(pos.offset_dir(self.motion_direction.opposite().to_offset(), k as i32)); + j += 1; + } + let mut k = 1; + loop { + let block_pos2 = pos.offset_dir(self.motion_direction.to_offset(), k); + if let Some(l) = self.moved_blocks.iter().position(|&p| p == block_pos2) { + self.set_moved_blocks(j, l); + for m in 0..=(l + j) { + let block_pos3 = self.moved_blocks[m]; + let block = self.world.get_block(&block_pos3).await.unwrap(); + if Self::is_block_sticky(&block) + && !Box::pin(self.try_move_adjacent_block(&block, block_pos3)).await + { + return false; + } + } + return true; + } + (block, block_state) = self + .world + .get_block_and_block_state(&block_pos2) + .await + .unwrap(); + if block_state.is_air() { + return true; + } + if !PistonBlock::is_movable( + &block, + &block_state, + // self.world, + // block_pos2, + // self.motion_direction, + // true, + // self.motion_direction, + ) || block_pos2 == self.pos_from + { + return false; + } + if block_state.piston_behavior == PistonBehavior::Destroy { + self.broken_blocks.push(block_pos2); + return true; + } + if self.moved_blocks.len() >= MAX_MOVABLE_BLOCKS { + return false; + } + self.moved_blocks.push(block_pos2); + j += 1; + k += 1; + } + } + + fn set_moved_blocks(&mut self, from: usize, to: usize) { + let mut list = Vec::new(); + let mut list2 = Vec::new(); + let mut list3 = Vec::new(); + list.extend_from_slice(&self.moved_blocks[0..to]); + list2.extend_from_slice(&self.moved_blocks[self.moved_blocks.len() - from..]); + list3.extend_from_slice(&self.moved_blocks[to..self.moved_blocks.len() - from]); + self.moved_blocks.clear(); + self.moved_blocks.extend(list); + self.moved_blocks.extend(list2); + self.moved_blocks.extend(list3); + } + + async fn try_move_adjacent_block(&mut self, block: &Block, pos: BlockPos) -> bool { + for direction in BlockDirection::all() { + if direction.to_axis() == self.motion_direction.to_axis() { + continue; + } + let block_pos = pos.offset(direction.to_offset()); + let block_state2 = self.world.get_block(&block_pos).await.unwrap(); + if Self::is_adjacent_block_stuck(&block_state2, block) + && !self.try_move(block_pos, direction).await + { + return false; + } + } + true + } +} diff --git a/pumpkin/src/block/blocks/piston/piston.rs b/pumpkin/src/block/blocks/piston/piston.rs new file mode 100644 index 000000000..d5e2a30d6 --- /dev/null +++ b/pumpkin/src/block/blocks/piston/piston.rs @@ -0,0 +1,462 @@ +use std::{collections::HashMap, sync::Arc}; + +use async_trait::async_trait; +use pumpkin_data::{ + Block, BlockState, + block_properties::{ + BlockProperties, MovingPistonLikeProperties, PistonHeadLikeProperties, PistonType, + get_block_by_state_id, get_state_by_state_id, + }, +}; +use pumpkin_protocol::server::play::SUseItemOn; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::{ + BlockStateId, + block::{ + BlockDirection, FacingExt, + entities::{BlockEntity, piston::PistonBlockEntity}, + }, + world::BlockFlags, +}; + +use crate::{ + block::{ + BlockIsReplacing, + blocks::redstone::is_emitting_redstone_power, + pumpkin_block::{BlockMetadata, PumpkinBlock}, + }, + entity::player::Player, + server::Server, + world::World, +}; + +use super::{PistonHandler, piston_extension::MovingPistonProps}; + +pub(crate) type PistonProps = pumpkin_data::block_properties::StickyPistonLikeProperties; + +pub struct PistonBlock; + +impl BlockMetadata for PistonBlock { + fn namespace(&self) -> &'static str { + "minecraft" + } + + fn ids(&self) -> &'static [&'static str] { + &[Block::PISTON.name, Block::STICKY_PISTON.name] + } +} + +impl PistonBlock { + pub fn is_movable(block: &Block, state: &BlockState) -> bool { + // TODO: more checks + if state.is_air() { + return true; + } + // Vanilla hardcoded them aswell + if block == &Block::OBSIDIAN + || block == &Block::CRYING_OBSIDIAN + || block == &Block::RESPAWN_ANCHOR + || block == &Block::REINFORCED_DEEPSLATE + { + return false; + } + true + } +} + +#[async_trait] +impl PumpkinBlock for PistonBlock { + async fn on_place( + &self, + _server: &Server, + _world: &World, + player: &Player, + block: &Block, + _block_pos: &BlockPos, + _face: BlockDirection, + _replacing: BlockIsReplacing, + _use_item_on: &SUseItemOn, + ) -> BlockStateId { + let mut props = PistonProps::default(block); + props.extended = false; + props.facing = player.living_entity.entity.get_facing().opposite(); + props.to_state_id(block) + } + + async fn placed( + &self, + world: &Arc, + block: &Block, + state_id: BlockStateId, + pos: &BlockPos, + old_state_id: BlockStateId, + _notify: bool, + ) { + if old_state_id == state_id { + return; + } + try_move(world, block, pos).await; + } + + async fn on_neighbor_update( + &self, + world: &Arc, + block: &Block, + block_pos: &BlockPos, + _source_block: &Block, + _notify: bool, + ) { + try_move(world, block, block_pos).await; + } + + async fn on_synced_block_event( + &self, + block: &Block, + world: &Arc, + pos: &BlockPos, + r#type: u8, + _data: u8, + ) -> bool { + let state = world.get_block_state(pos).await.unwrap(); + let mut props = PistonProps::from_state_id(state.id, block); + let dir = props.facing.to_block_direction(); + + // I don't think this is optimal ? + let sticky = block == &Block::STICKY_PISTON; + + let should_extend = should_extend(world, block, &state, pos, dir).await; + if should_extend && (r#type == 1 || r#type == 2) { + props.extended = true; + world + .set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS) + .await; + return false; + } + + // This may prevents when something happens in the one tick before this function got called + if !should_extend && r#type == 0 { + return false; + } + + if r#type == 0 { + if !move_piston(world, dir, pos, true, sticky).await { + return false; + } + props.extended = true; + world + .set_block_state( + pos, + props.to_state_id(block), + BlockFlags::NOTIFY_ALL | BlockFlags::MOVED, + ) + .await; + return true; + } + let extended_pos = pos.offset(dir.to_offset()); + + if let Some((block_entity_nbt, _block_entity)) = world.get_block_entity(&extended_pos).await + { + PistonBlockEntity::from_nbt(&block_entity_nbt, extended_pos) + .finish(world.clone()) + .await; + } + + let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON); + props.facing = dir.to_facing(); + props.r#type = if sticky { + PistonType::Sticky + } else { + PistonType::Normal + }; + + let state = props.to_state_id(&Block::MOVING_PISTON); + + world + .set_block_state(pos, state, BlockFlags::FORCE_STATE) + .await; + + world + .add_block_entity(Arc::new(PistonBlockEntity { + position: *pos, + facing: dir.to_facing().to_block_direction(), + pushed_block_state: get_state_by_state_id(state).unwrap(), + current_progress: 0.0.into(), + last_progress: 0.0.into(), + extending: false, + source: true, + })) + .await; + world.update_neighbors(pos, None).await; + if sticky { + } else { + world + .set_block_state( + &extended_pos, + Block::AIR.default_state_id, + BlockFlags::NOTIFY_ALL, + ) + .await; + } + return true; + } +} + +async fn should_extend( + world: &Arc, + block: &Block, + state: &BlockState, + block_pos: &BlockPos, + piston_dir: BlockDirection, +) -> bool { + // Pistons can't be powered from the same direction as they are facing + for dir in BlockDirection::all() { + if dir == piston_dir + || !is_emitting_redstone_power( + block, + state, + world, + &block_pos.offset(dir.to_offset()), + dir, + ) + .await + { + continue; + } + return true; + } + if is_emitting_redstone_power(block, state, world, block_pos, BlockDirection::Down).await { + return true; + } + for dir in BlockDirection::all() { + if dir == BlockDirection::Down + || !is_emitting_redstone_power( + block, + state, + world, + &block_pos.up().offset(dir.to_offset()), + dir, + ) + .await + { + continue; + } + return true; + } + false +} + +async fn try_move(world: &Arc, block: &Block, block_pos: &BlockPos) { + let state = world.get_block_state(block_pos).await.unwrap(); + let props = PistonProps::from_state_id(state.id, block); + let dir = props.facing.to_block_direction(); + let should_extent = should_extend(world, block, &state, block_pos, dir).await; + + if should_extent && !props.extended { + if PistonHandler::new(world, *block_pos, dir, true) + .calculate_push() + .await + { + world + .add_synced_block_event(*block_pos, 0, dir.to_index() as u8) + .await; + } + } else if !should_extent && props.extended { + let new_pos = block_pos.offset_dir(dir.to_offset(), 2); + let (new_block, new_state) = world.get_block_and_block_state(&new_pos).await.unwrap(); + let mut r#type = 1; + if new_block == Block::MOVING_PISTON { + let new_props = MovingPistonProps::from_state_id(new_state.id, &new_block); + // TODO: check more things + if new_props.facing == props.facing { + r#type = 2; + } + } + world + .add_synced_block_event(*block_pos, r#type, dir.to_index() as u8) + .await; + } +} + +#[expect(clippy::too_many_lines)] +async fn move_piston( + world: &Arc, + dir: BlockDirection, + block_pos: &BlockPos, + extend: bool, + sticky: bool, +) -> bool { + let extended_pos = block_pos.offset(dir.to_offset()); + if !extend && world.get_block(&extended_pos).await.unwrap() == Block::PISTON_HEAD { + world + .set_block_state( + &extended_pos, + Block::AIR.default_state_id, + BlockFlags::FORCE_STATE, + ) + .await; + } + let mut handler = PistonHandler::new(world, *block_pos, dir, extend); + if !handler.calculate_push().await { + return false; + } + + let mut moved_blocks_map: HashMap = HashMap::new(); + let moved_blocks: Vec = handler.moved_blocks; + dbg!(&moved_blocks); + + let mut moved_block_states: Vec = Vec::new(); + + for &block_pos in &moved_blocks { + let block_state = world.get_block_state(&block_pos).await.unwrap(); + dbg!(block_state.id); + moved_block_states.push(block_state.clone()); + moved_blocks_map.insert(block_pos, block_state); + } + + let broken_blocks: Vec = handler.broken_blocks; + dbg!(&broken_blocks); + let mut affected_block_states: Vec = + Vec::with_capacity(moved_blocks.len() + broken_blocks.len()); + let move_direction = if extend { dir } else { dir.opposite() }; + + for &broken_block_pos in broken_blocks.iter().rev() { + let block_state = world.get_block_state(&broken_block_pos).await.unwrap(); + world + .break_block(&broken_block_pos, None, BlockFlags::empty()) + .await; + affected_block_states.push(block_state); + } + + for (index, &moved_block_pos) in moved_blocks.iter().rev().enumerate() { + let block_state = world.get_block_state(&moved_block_pos).await.unwrap(); + let target_pos = moved_block_pos.offset(move_direction.to_offset()); + moved_blocks_map.remove(&target_pos); + + let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON); + props.facing = dir.to_facing(); + let state = props.to_state_id(&Block::MOVING_PISTON); + + world + .set_block_state(&target_pos, state, BlockFlags::MOVED) + .await; + + if let Some(moved_state) = moved_block_states.get(index) { + world + .add_block_entity(Arc::new(PistonBlockEntity { + position: extended_pos, + facing: dir.to_facing().to_block_direction(), + pushed_block_state: moved_state.clone(), + current_progress: 0.0.into(), + last_progress: 0.0.into(), + extending: extend, + source: false, + })) + .await; + } + affected_block_states.push(block_state); + } + + if extend { + let pistion_type = if sticky { + PistonType::Sticky + } else { + PistonType::Normal + }; + let mut props = MovingPistonLikeProperties::default(&Block::MOVING_PISTON); + props.facing = dir.to_facing(); + props.r#type = pistion_type; + moved_blocks_map.remove(&extended_pos); + world + .set_block_state( + &extended_pos, + props.to_state_id(&Block::MOVING_PISTON), + BlockFlags::MOVED, + ) + .await; + let mut props = PistonHeadLikeProperties::default(&Block::PISTON_HEAD); + props.facing = dir.to_facing(); + props.r#type = pistion_type; + world + .add_block_entity(Arc::new(PistonBlockEntity { + position: extended_pos, + facing: dir.to_facing().to_block_direction(), + pushed_block_state: get_state_by_state_id(props.to_state_id(&Block::PISTON_HEAD)) + .unwrap(), + current_progress: 0.0.into(), + last_progress: 0.0.into(), + extending: true, + source: true, + })) + .await; + } + + let air_state = Block::AIR.default_state_id; + for &pos in moved_blocks_map.keys() { + world + .set_block_state( + &pos, + air_state, + BlockFlags::NOTIFY_LISTENERS | BlockFlags::FORCE_STATE | BlockFlags::MOVED, + ) + .await; + } + + for (pos, state) in &moved_blocks_map { + // state.prepare(world, pos, BlockFlags::NOTIFY_LISTENERS); + world + .block_registry + .prepare( + world, + pos, + &get_block_by_state_id(state.id).unwrap(), + state.id, + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + world.update_neighbors(pos, None).await; + world + .block_registry + .prepare( + world, + pos, + &Block::AIR, + air_state, + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + } + + for (i, &broken_block_pos) in broken_blocks.iter().rev().enumerate() { + if let Some(block_state) = affected_block_states.get(i).cloned() { + world + .block_registry + .on_state_replaced( + world, + &get_block_by_state_id(block_state.id).unwrap(), + broken_block_pos, + block_state.id, // ? + false, + ) + .await; + world + .block_registry + .prepare( + world, + &broken_block_pos, + &get_block_by_state_id(block_state.id).unwrap(), + block_state.id, + BlockFlags::NOTIFY_LISTENERS, + ) + .await; + world.update_neighbors(&broken_block_pos, None).await; + } + } + + for &moved_block_pos in moved_blocks.iter().rev() { + world.update_neighbors(&moved_block_pos, None).await; + } + + // world.update_neighbors(&extended_pos, None).await; + + true +} diff --git a/pumpkin/src/block/blocks/piston/piston_extension.rs b/pumpkin/src/block/blocks/piston/piston_extension.rs new file mode 100644 index 000000000..dda63a6d7 --- /dev/null +++ b/pumpkin/src/block/blocks/piston/piston_extension.rs @@ -0,0 +1,46 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use pumpkin_data::block_properties::BlockProperties; +use pumpkin_data::{Block, BlockState}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::{block::FacingExt, world::BlockFlags}; + +use crate::entity::player::Player; +use crate::{ + block::pumpkin_block::{BlockMetadata, PumpkinBlock}, + world::World, +}; + +use super::piston::{PistonBlock, PistonProps}; + +pub(crate) type MovingPistonProps = pumpkin_data::block_properties::MovingPistonLikeProperties; + +use crate::server::Server; +#[pumpkin_block("minecraft:moving_piston")] +pub struct PistonExtensionBlock; + +#[async_trait] +impl PumpkinBlock for PistonExtensionBlock { + async fn broken( + &self, + _block: &Block, + _player: &Arc, + location: BlockPos, + _server: &Server, + world: Arc, + state: BlockState, + ) { + let props = MovingPistonProps::from_state_id(state.id, &Block::MOVING_PISTON); + let pos = location.offset(props.facing.opposite().to_block_direction().to_offset()); + let (new_block, new_state) = world.get_block_and_block_state(&pos).await.unwrap(); + if PistonBlock::ids(&PistonBlock).contains(&new_block.name) { + let props = PistonProps::from_state_id(new_state.id, &new_block); + if props.extended { + // TODO: use player + world.break_block(&pos, None, BlockFlags::SKIP_DROPS).await; + } + } + } +} diff --git a/pumpkin/src/block/blocks/piston/piston_head.rs b/pumpkin/src/block/blocks/piston/piston_head.rs new file mode 100644 index 000000000..c04515b94 --- /dev/null +++ b/pumpkin/src/block/blocks/piston/piston_head.rs @@ -0,0 +1,10 @@ +use async_trait::async_trait; +use pumpkin_macros::pumpkin_block; + +use crate::block::pumpkin_block::PumpkinBlock; + +#[pumpkin_block("minecraft:piston_head")] +pub struct PistonHeadBlock; + +#[async_trait] +impl PumpkinBlock for PistonHeadBlock {} diff --git a/pumpkin/src/block/blocks/pumpkin.rs b/pumpkin/src/block/blocks/pumpkin.rs index 3859c5667..41896abef 100644 --- a/pumpkin/src/block/blocks/pumpkin.rs +++ b/pumpkin/src/block/blocks/pumpkin.rs @@ -1,6 +1,6 @@ use crate::entity::item::ItemEntity; use crate::server::Server; -use crate::world::{BlockFlags, World}; +use crate::world::World; use crate::{block::registry::BlockActionResult, entity::player::Player}; use async_trait::async_trait; use pumpkin_data::Block; @@ -9,6 +9,7 @@ use pumpkin_data::item::Item; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::item::ItemStack; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; #[pumpkin_block("minecraft:pumpkin")] pub struct PumpkinBlock; diff --git a/pumpkin/src/block/blocks/redstone/buttons.rs b/pumpkin/src/block/blocks/redstone/buttons.rs index a05cffdb1..22ea20873 100644 --- a/pumpkin/src/block/blocks/redstone/buttons.rs +++ b/pumpkin/src/block/blocks/redstone/buttons.rs @@ -13,6 +13,7 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::chunk::TickPriority; +use pumpkin_world::world::BlockFlags; type ButtonLikeProperties = pumpkin_data::block_properties::LeverLikeProperties; @@ -22,7 +23,6 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; async fn click_button(world: &Arc, block_pos: &BlockPos) { diff --git a/pumpkin/src/block/blocks/redstone/lever.rs b/pumpkin/src/block/blocks/redstone/lever.rs index eb7d6d385..4f0554f23 100644 --- a/pumpkin/src/block/blocks/redstone/lever.rs +++ b/pumpkin/src/block/blocks/redstone/lever.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use crate::world::BlockFlags; use crate::{block::BlockIsReplacing, entity::player::Player}; use async_trait::async_trait; use pumpkin_data::{ @@ -11,8 +10,8 @@ use pumpkin_data::{ use pumpkin_macros::pumpkin_block; use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::BlockStateId; use pumpkin_world::block::{BlockDirection, HorizontalFacingExt}; +use pumpkin_world::{BlockStateId, world::BlockFlags}; use crate::{ block::{pumpkin_block::PumpkinBlock, registry::BlockActionResult}, diff --git a/pumpkin/src/block/blocks/redstone/mod.rs b/pumpkin/src/block/blocks/redstone/mod.rs index 17875e3ee..b60954889 100644 --- a/pumpkin/src/block/blocks/redstone/mod.rs +++ b/pumpkin/src/block/blocks/redstone/mod.rs @@ -43,20 +43,30 @@ pub async fn update_wire_neighbors(world: &Arc, pos: &BlockPos) { } } +pub async fn is_emitting_redstone_power( + block: &Block, + state: &BlockState, + world: &World, + pos: &BlockPos, + facing: BlockDirection, +) -> bool { + get_redstone_power(block, state, world, pos, facing).await > 0 +} + pub async fn get_redstone_power( block: &Block, state: &BlockState, world: &World, - pos: BlockPos, + pos: &BlockPos, facing: BlockDirection, ) -> u8 { if state.is_solid() { return std::cmp::max( - get_max_strong_power(world, &pos, true).await, - get_weak_power(block, state, world, &pos, facing, true).await, + get_max_strong_power(world, pos, true).await, + get_weak_power(block, state, world, pos, facing, true).await, ); } - get_weak_power(block, state, world, &pos, facing, true).await + get_weak_power(block, state, world, pos, facing, true).await } async fn get_redstone_power_no_dust( @@ -160,7 +170,7 @@ pub async fn block_receives_redstone_power(world: &World, pos: &BlockPos) -> boo .get_block_and_block_state(&neighbor_pos) .await .unwrap(); - if get_redstone_power(&block, &state, world, neighbor_pos, face).await > 0 { + if is_emitting_redstone_power(&block, &state, world, pos, face).await { return true; } } @@ -174,7 +184,7 @@ pub fn is_diode(block: &Block) -> bool { pub async fn diode_get_input_strength(world: &World, pos: &BlockPos, facing: BlockDirection) -> u8 { let input_pos = pos.offset(facing.to_offset()); let (input_block, input_state) = world.get_block_and_block_state(&input_pos).await.unwrap(); - let power: u8 = get_redstone_power(&input_block, &input_state, world, input_pos, facing).await; + let power: u8 = get_redstone_power(&input_block, &input_state, world, &input_pos, facing).await; if power == 0 && input_state.is_solid() { return get_max_weak_power(world, &input_pos, true).await; } diff --git a/pumpkin/src/block/blocks/redstone/observer.rs b/pumpkin/src/block/blocks/redstone/observer.rs index cb7e805f4..663723710 100644 --- a/pumpkin/src/block/blocks/redstone/observer.rs +++ b/pumpkin/src/block/blocks/redstone/observer.rs @@ -13,13 +13,10 @@ use pumpkin_world::{ BlockStateId, block::{BlockDirection, FacingExt}, chunk::TickPriority, + world::BlockFlags, }; -use crate::{ - block::pumpkin_block::PumpkinBlock, - server::Server, - world::{BlockFlags, World}, -}; +use crate::{block::pumpkin_block::PumpkinBlock, server::Server, world::World}; #[pumpkin_block("minecraft:observer")] pub struct ObserverBlock; diff --git a/pumpkin/src/block/blocks/redstone/piston.rs b/pumpkin/src/block/blocks/redstone/piston.rs index e5d7392f7..ef7600aa8 100644 --- a/pumpkin/src/block/blocks/redstone/piston.rs +++ b/pumpkin/src/block/blocks/redstone/piston.rs @@ -5,12 +5,12 @@ use async_trait::async_trait; use pumpkin_data::{Block, block_properties::BlockProperties}; use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::{BlockStateId, block::BlockDirection}; +use pumpkin_world::{BlockStateId, block::BlockDirection, world::BlockFlags}; use crate::{ block::pumpkin_block::{BlockMetadata, PumpkinBlock}, server::Server, - world::{BlockFlags, World}, + world::World, }; use super::block_receives_redstone_power; diff --git a/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs b/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs index 595d4023c..928300ad9 100644 --- a/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/activator_rail.rs @@ -5,13 +5,13 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use super::RailProperties; diff --git a/pumpkin/src/block/blocks/redstone/rails/common.rs b/pumpkin/src/block/blocks/redstone/rails/common.rs index 4ff0c12a6..2538a5e3b 100644 --- a/pumpkin/src/block/blocks/redstone/rails/common.rs +++ b/pumpkin/src/block/blocks/redstone/rails/common.rs @@ -5,9 +5,9 @@ use pumpkin_data::{ block_properties::{HorizontalFacing, RailShape, StraightRailShape}, }; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::BlockStateId; +use pumpkin_world::{BlockStateId, world::BlockFlags}; -use crate::world::{BlockFlags, World}; +use crate::world::World; use super::{HorizontalFacingRailExt, Rail, RailElevation, RailProperties, StraightRailShapeExt}; diff --git a/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs b/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs index 4e4d1d9ba..0fb51b831 100644 --- a/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/detector_rail.rs @@ -5,13 +5,13 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use super::RailProperties; diff --git a/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs b/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs index b6000a89d..f36fffcb9 100644 --- a/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/powered_rail.rs @@ -5,13 +5,13 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use super::RailProperties; diff --git a/pumpkin/src/block/blocks/redstone/rails/rail.rs b/pumpkin/src/block/blocks/redstone/rails/rail.rs index 08ae0fdbe..74af17a3d 100644 --- a/pumpkin/src/block/blocks/redstone/rails/rail.rs +++ b/pumpkin/src/block/blocks/redstone/rails/rail.rs @@ -7,13 +7,13 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use super::StraightRailShapeExt; diff --git a/pumpkin/src/block/blocks/redstone/redstone_lamp.rs b/pumpkin/src/block/blocks/redstone/redstone_lamp.rs index c029c74f8..dc49049f5 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_lamp.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_lamp.rs @@ -6,13 +6,9 @@ use pumpkin_data::{Block, block_properties::BlockProperties}; use pumpkin_macros::pumpkin_block; use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::{BlockStateId, block::BlockDirection, chunk::TickPriority}; +use pumpkin_world::{BlockStateId, block::BlockDirection, chunk::TickPriority, world::BlockFlags}; -use crate::{ - block::pumpkin_block::PumpkinBlock, - server::Server, - world::{BlockFlags, World}, -}; +use crate::{block::pumpkin_block::PumpkinBlock, server::Server, world::World}; use super::block_receives_redstone_power; diff --git a/pumpkin/src/block/blocks/redstone/redstone_torch.rs b/pumpkin/src/block/blocks/redstone/redstone_torch.rs index 3eebd530e..c391cf30d 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_torch.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_torch.rs @@ -12,13 +12,13 @@ use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::block::HorizontalFacingExt; use pumpkin_world::chunk::TickPriority; +use pumpkin_world::world::BlockFlags; type RWallTorchProps = pumpkin_data::block_properties::FurnaceLikeProperties; type RTorchProps = pumpkin_data::block_properties::RedstoneOreLikeProperties; use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; use super::get_redstone_power; @@ -209,7 +209,7 @@ impl PumpkinBlock for RedstoneTorchBlock { pub async fn should_be_lit(world: &World, pos: &BlockPos, face: BlockDirection) -> bool { let other_pos = pos.offset(face.to_offset()); let (block, state) = world.get_block_and_block_state(&other_pos).await.unwrap(); - get_redstone_power(&block, &state, world, other_pos, face).await == 0 + get_redstone_power(&block, &state, world, &other_pos, face).await == 0 } pub async fn update_neighbors(world: &Arc, pos: &BlockPos) { diff --git a/pumpkin/src/block/blocks/redstone/redstone_wire.rs b/pumpkin/src/block/blocks/redstone/redstone_wire.rs index b983ce5f0..19e631718 100644 --- a/pumpkin/src/block/blocks/redstone/redstone_wire.rs +++ b/pumpkin/src/block/blocks/redstone/redstone_wire.rs @@ -13,11 +13,11 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::{BlockDirection, HorizontalFacingExt}; +use pumpkin_world::world::BlockFlags; use crate::block::BlockIsReplacing; use crate::block::registry::BlockActionResult; use crate::entity::player::Player; -use crate::world::BlockFlags; use crate::{block::pumpkin_block::PumpkinBlock, server::Server, world::World}; use super::turbo::RedstoneWireTurbo; diff --git a/pumpkin/src/block/blocks/redstone/repeater.rs b/pumpkin/src/block/blocks/redstone/repeater.rs index a2d27a25d..87fbc530f 100644 --- a/pumpkin/src/block/blocks/redstone/repeater.rs +++ b/pumpkin/src/block/blocks/redstone/repeater.rs @@ -9,14 +9,17 @@ use pumpkin_data::{ use pumpkin_macros::pumpkin_block; use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::block::{BlockDirection, HorizontalFacingExt}; use pumpkin_world::{BlockStateId, chunk::TickPriority}; +use pumpkin_world::{ + block::{BlockDirection, HorizontalFacingExt}, + world::BlockFlags, +}; use crate::{ block::{BlockIsReplacing, pumpkin_block::PumpkinBlock, registry::BlockActionResult}, entity::player::Player, server::Server, - world::{BlockFlags, World}, + world::World, }; use super::{diode_get_input_strength, get_weak_power, is_diode}; diff --git a/pumpkin/src/block/blocks/redstone/turbo.rs b/pumpkin/src/block/blocks/redstone/turbo.rs index 81c2662bf..a3c4e36b4 100644 --- a/pumpkin/src/block/blocks/redstone/turbo.rs +++ b/pumpkin/src/block/blocks/redstone/turbo.rs @@ -7,11 +7,11 @@ use pumpkin_data::{ block_properties::{BlockProperties, EnumVariants, Integer0To15, RedstoneWireLikeProperties}, }; use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; -use pumpkin_world::block::BlockDirection; +use pumpkin_world::{block::BlockDirection, world::BlockFlags}; use rustc_hash::FxHashMap; use std::sync::Arc; -use crate::world::{BlockFlags, World}; +use crate::world::World; use super::get_redstone_power_no_dust; diff --git a/pumpkin/src/block/blocks/stairs.rs b/pumpkin/src/block/blocks/stairs.rs index 31e2ed6dd..8e76f3f27 100644 --- a/pumpkin/src/block/blocks/stairs.rs +++ b/pumpkin/src/block/blocks/stairs.rs @@ -11,11 +11,11 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use crate::block::BlockIsReplacing; use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; -use crate::world::BlockFlags; use crate::world::World; use crate::{entity::player::Player, server::Server}; diff --git a/pumpkin/src/block/blocks/sugar_cane.rs b/pumpkin/src/block/blocks/sugar_cane.rs index 28de7aa4a..e01806244 100644 --- a/pumpkin/src/block/blocks/sugar_cane.rs +++ b/pumpkin/src/block/blocks/sugar_cane.rs @@ -13,11 +13,11 @@ use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; use pumpkin_world::chunk::TickPriority; +use pumpkin_world::world::BlockFlags; use crate::block::pumpkin_block::PumpkinBlock; use crate::entity::player::Player; use crate::server::Server; -use crate::world::BlockFlags; use crate::world::World; #[pumpkin_block("minecraft:sugar_cane")] diff --git a/pumpkin/src/block/blocks/tnt.rs b/pumpkin/src/block/blocks/tnt.rs index f1ed69fce..fb001c596 100644 --- a/pumpkin/src/block/blocks/tnt.rs +++ b/pumpkin/src/block/blocks/tnt.rs @@ -5,7 +5,7 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::entity::tnt::TNTEntity; use crate::server::Server; -use crate::world::{BlockFlags, World}; +use crate::world::World; use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::entity::EntityType; @@ -15,6 +15,7 @@ use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::BlockStateId; +use pumpkin_world::world::BlockFlags; use rand::Rng; use super::redstone::block_receives_redstone_power; diff --git a/pumpkin/src/block/fluids/flowing_fluid.rs b/pumpkin/src/block/fluids/flowing_fluid.rs index 4061dc851..44c967d10 100644 --- a/pumpkin/src/block/fluids/flowing_fluid.rs +++ b/pumpkin/src/block/fluids/flowing_fluid.rs @@ -7,9 +7,9 @@ use pumpkin_data::{ fluid::{EnumVariants, Falling, Fluid, FluidProperties, Level}, }; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::{BlockId, BlockStateId, block::BlockDirection}; +use pumpkin_world::{BlockId, BlockStateId, block::BlockDirection, world::BlockFlags}; -use crate::world::{BlockFlags, World}; +use crate::world::World; type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; #[derive(Clone)] diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index 711485f6e..73c2d2d9f 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -13,6 +13,7 @@ use blocks::iron_bars::IronBarsBlock; use blocks::logs::LogBlock; use blocks::nether_portal::NetherPortalBlock; use blocks::note::NoteBlock; +use blocks::piston::piston_extension::PistonExtensionBlock; use blocks::pumpkin::PumpkinBlock; use blocks::redstone::buttons::ButtonBlock; use blocks::redstone::observer::ObserverBlock; @@ -101,7 +102,11 @@ pub fn default_registry() -> Arc { manager.register(ButtonBlock); manager.register(LeverBlock); manager.register(ObserverBlock); + + // Piston manager.register(PistonBlock); + manager.register(PistonExtensionBlock); + manager.register(RedstoneBlock); manager.register(RedstoneLamp); manager.register(RedstoneTorchBlock); diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 3de64c085..30818292d 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -1,7 +1,7 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::server::Server; -use crate::world::{BlockFlags, World}; +use crate::world::World; use async_trait::async_trait; use pumpkin_data::item::Item; use pumpkin_data::{Block, BlockState}; @@ -9,6 +9,7 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; use super::BlockIsReplacing; diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index 5451bda44..ab9076ca9 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -1,7 +1,7 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::entity::player::Player; use crate::server::Server; -use crate::world::{BlockFlags, World}; +use crate::world::World; use pumpkin_data::fluid::Fluid; use pumpkin_data::item::Item; use pumpkin_data::{Block, BlockState}; @@ -9,6 +9,7 @@ use pumpkin_protocol::server::play::SUseItemOn; use pumpkin_util::math::position::BlockPos; use pumpkin_world::BlockStateId; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use std::collections::HashMap; use std::sync::Arc; diff --git a/pumpkin/src/command/commands/fill.rs b/pumpkin/src/command/commands/fill.rs index cdedec38f..68ba7e89b 100644 --- a/pumpkin/src/command/commands/fill.rs +++ b/pumpkin/src/command/commands/fill.rs @@ -4,12 +4,12 @@ use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, literal}; use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::world::BlockFlags; use async_trait::async_trait; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; +use pumpkin_world::world::BlockFlags; const NAMES: [&str; 1] = ["fill"]; diff --git a/pumpkin/src/command/commands/setblock.rs b/pumpkin/src/command/commands/setblock.rs index 11df179ba..e3f45e21a 100644 --- a/pumpkin/src/command/commands/setblock.rs +++ b/pumpkin/src/command/commands/setblock.rs @@ -1,5 +1,6 @@ use async_trait::async_trait; use pumpkin_util::text::TextComponent; +use pumpkin_world::world::BlockFlags; use crate::command::args::block::BlockArgumentConsumer; use crate::command::args::position_block::BlockPosArgumentConsumer; @@ -7,7 +8,6 @@ use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, literal}; use crate::command::{CommandError, CommandExecutor, CommandSender}; -use crate::world::BlockFlags; const NAMES: [&str; 1] = ["setblock"]; diff --git a/pumpkin/src/item/items/axe.rs b/pumpkin/src/item/items/axe.rs index f31108c75..c17536ce8 100644 --- a/pumpkin/src/item/items/axe.rs +++ b/pumpkin/src/item/items/axe.rs @@ -1,7 +1,6 @@ use crate::entity::player::Player; use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem}; use crate::server::Server; -use crate::world::BlockFlags; use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::block_properties::BlockProperties; @@ -10,6 +9,7 @@ use pumpkin_data::item::Item; use pumpkin_data::tag::Tagable; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; pub struct AxeItem; diff --git a/pumpkin/src/item/items/bucket.rs b/pumpkin/src/item/items/bucket.rs index f21bfbb94..8091da414 100644 --- a/pumpkin/src/item/items/bucket.rs +++ b/pumpkin/src/item/items/bucket.rs @@ -8,9 +8,10 @@ use pumpkin_data::item::Item; use pumpkin_util::GameMode; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; +use pumpkin_world::world::BlockFlags; use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem}; -use crate::world::{BlockFlags, World}; +use crate::world::World; pub struct EmptyBucketItem; pub struct FilledBucketItem; diff --git a/pumpkin/src/item/items/flint_and_steel.rs b/pumpkin/src/item/items/flint_and_steel.rs index c411be85c..567f65a43 100644 --- a/pumpkin/src/item/items/flint_and_steel.rs +++ b/pumpkin/src/item/items/flint_and_steel.rs @@ -3,12 +3,12 @@ use pumpkin_data::Block; use pumpkin_data::item::Item; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; use crate::block::blocks::fire::FireBlockBase; use crate::entity::player::Player; use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem}; use crate::server::Server; -use crate::world::BlockFlags; pub struct FlintAndSteelItem; diff --git a/pumpkin/src/item/items/hoe.rs b/pumpkin/src/item/items/hoe.rs index cfbc4fa25..dab600c3d 100644 --- a/pumpkin/src/item/items/hoe.rs +++ b/pumpkin/src/item/items/hoe.rs @@ -2,7 +2,6 @@ use crate::entity::item::ItemEntity; use crate::entity::player::Player; use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem}; use crate::server::Server; -use crate::world::BlockFlags; use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::entity::EntityType; @@ -11,6 +10,7 @@ use pumpkin_data::tag::Tagable; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::BlockDirection; use pumpkin_world::item::ItemStack; +use pumpkin_world::world::BlockFlags; use std::sync::Arc; pub struct HoeItem; diff --git a/pumpkin/src/item/items/honeycomb.rs b/pumpkin/src/item/items/honeycomb.rs index b5c6e1b1a..4486cf8ac 100644 --- a/pumpkin/src/item/items/honeycomb.rs +++ b/pumpkin/src/item/items/honeycomb.rs @@ -1,7 +1,6 @@ use crate::entity::player::Player; use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem}; use crate::server::Server; -use crate::world::BlockFlags; use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::block_properties::BlockProperties; @@ -10,6 +9,7 @@ use pumpkin_data::item::Item; use pumpkin_data::tag::Tagable; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; pub struct HoneyCombItem; diff --git a/pumpkin/src/item/items/shovel.rs b/pumpkin/src/item/items/shovel.rs index d87728313..9df0e2dc4 100644 --- a/pumpkin/src/item/items/shovel.rs +++ b/pumpkin/src/item/items/shovel.rs @@ -1,13 +1,13 @@ use crate::entity::player::Player; use crate::item::pumpkin_item::{ItemMetadata, PumpkinItem}; use crate::server::Server; -use crate::world::BlockFlags; use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::item::Item; use pumpkin_data::tag::Tagable; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::BlockDirection; +use pumpkin_world::world::BlockFlags; pub struct ShovelItem; diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index b2edbc331..ce369683c 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -3,6 +3,7 @@ use pumpkin_data::item::Item; use pumpkin_inventory::InventoryError; use pumpkin_inventory::equipment_slot::EquipmentSlot; use pumpkin_inventory::screen_handler::ScreenHandler; +use pumpkin_world::world::BlockFlags; use rsa::pkcs1v15::{Signature as RsaPkcs1v15Signature, VerifyingKey}; use rsa::signature::Verifier; use sha1::Sha1; @@ -21,7 +22,7 @@ use crate::plugin::player::player_chat::PlayerChatEvent; use crate::plugin::player::player_command_send::PlayerCommandSendEvent; use crate::plugin::player::player_move::PlayerMoveEvent; use crate::server::seasonal_events; -use crate::world::{BlockFlags, World}; +use crate::world::World; use crate::{ command::CommandSender, entity::player::{ChatMode, Hand, Player}, diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 4f92ec5a1..67fb3b95b 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,4 +1,3 @@ -use std::hash::Hash; use std::{ collections::HashMap, sync::{Arc, atomic::Ordering}, @@ -23,7 +22,7 @@ use crate::{ }, server::Server, }; -use bitflags::bitflags; +use async_trait::async_trait; use border::Worldborder; use bytes::{BufMut, Bytes}; use explosion::Explosion; @@ -42,7 +41,7 @@ use pumpkin_data::{ world::{RAW, WorldEvent}, }; use pumpkin_macros::send_cancellable; -use pumpkin_nbt::to_bytes_unnamed; +use pumpkin_nbt::{compound::NbtCompound, to_bytes_unnamed}; use pumpkin_protocol::client::play::{ CBlockEvent, CRemoveMobEffect, CSetEntityMetadata, MetaDataType, Metadata, }; @@ -68,19 +67,20 @@ use pumpkin_registry::DimensionType; use pumpkin_util::math::{position::BlockPos, vector3::Vector3}; use pumpkin_util::math::{position::chunk_section_from_pos, vector2::Vector2}; use pumpkin_util::text::{TextComponent, color::NamedColor}; -use pumpkin_world::entity::entity_data_flags::{ - DATA_PLAYER_MAIN_HAND, DATA_PLAYER_MODE_CUSTOMISATION, -}; +use pumpkin_world::world::BlockFlags; use pumpkin_world::{ BlockStateId, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity, level::SyncChunk, }; use pumpkin_world::{block::BlockDirection, chunk::ChunkData}; use pumpkin_world::{chunk::TickPriority, level::Level}; +use pumpkin_world::{ + entity::entity_data_flags::{DATA_PLAYER_MAIN_HAND, DATA_PLAYER_MODE_CUSTOMISATION}, + world::GetBlockError, +}; use rand::{Rng, thread_rng}; use scoreboard::Scoreboard; use serde::Serialize; -use thiserror::Error; use time::LevelTime; use tokio::sync::{RwLock, mpsc}; use tokio::{ @@ -96,33 +96,6 @@ pub mod weather; use weather::Weather; -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct BlockFlags: u32 { - const NOTIFY_NEIGHBORS = 0b000_0000_0001; - const NOTIFY_LISTENERS = 0b000_0000_0010; - const NOTIFY_ALL = 0b000_0000_0011; - const FORCE_STATE = 0b000_0000_0100; - const SKIP_DROPS = 0b000_0000_1000; - const MOVED = 0b000_0001_0000; - const SKIP_REDSTONE_WIRE_STATE_REPLACEMENT = 0b000_0010_0000; - const SKIP_BLOCK_ENTITY_REPLACED_CALLBACK = 0b000_0100_0000; - const SKIP_BLOCK_ADDED_CALLBACK = 0b000_1000_0000; - } -} - -#[derive(Debug, Error)] -pub enum GetBlockError { - InvalidBlockId, - BlockOutOfWorldBounds, -} - -impl std::fmt::Display for GetBlockError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - impl PumpkinError for GetBlockError { fn is_kick(&self) -> bool { false @@ -226,10 +199,7 @@ impl World { async fn flush_synced_block_events(self: &Arc) { let mut queue = self.synced_block_event_queue.lock().await; - let events: Vec = queue.drain(..).collect(); - // lets drop it here, allows us to add new things - drop(queue); - for event in events { + for event in queue.drain(..) { let block = self.get_block(&event.pos).await.unwrap(); // TODO if !self .block_registry @@ -1731,7 +1701,10 @@ impl World { } } - pub async fn get_block_entity(&self, block_pos: &BlockPos) -> Option> { + pub async fn get_block_entity( + &self, + block_pos: &BlockPos, + ) -> Option<(NbtCompound, Arc)> { let chunk = self.get_chunk(block_pos).await; let chunk: tokio::sync::RwLockReadGuard = chunk.read().await; @@ -1744,9 +1717,9 @@ impl World { let mut chunk: tokio::sync::RwLockWriteGuard = chunk.write().await; let block_entity_nbt = block_entity.chunk_data_nbt(); - if let Some(nbt) = block_entity_nbt { + if let Some(nbt) = &block_entity_nbt { let mut bytes = Vec::new(); - to_bytes_unnamed(&nbt, &mut bytes).unwrap(); + to_bytes_unnamed(nbt, &mut bytes).unwrap(); self.broadcast_packet_all(&CBlockEntityData::new( block_entity.get_position(), VarInt(block_entity.get_id() as i32), @@ -1755,7 +1728,10 @@ impl World { .await; } - chunk.block_entities.insert(block_pos, block_entity); + chunk.block_entities.insert( + block_pos, + (block_entity_nbt.unwrap_or_default(), block_entity), + ); chunk.dirty = true; } @@ -1868,3 +1844,35 @@ impl World { (None, None) } } + +#[async_trait] +impl pumpkin_world::world::SimpleWorld for World { + async fn set_block_state( + self: Arc, + position: &BlockPos, + block_state_id: BlockStateId, + flags: BlockFlags, + ) -> BlockStateId { + Self::set_block_state(&self, position, block_state_id, flags).await + } + + async fn get_block(&self, position: &BlockPos) -> Result { + Self::get_block(self, position).await + } + + async fn update_neighbor(self: Arc, neighbor_block_pos: &BlockPos, source_block: &Block) { + Self::update_neighbor(&self, neighbor_block_pos, source_block).await; + } + + async fn update_neighbors( + self: Arc, + block_pos: &BlockPos, + except: Option, + ) { + Self::update_neighbors(&self, block_pos, except).await; + } + + async fn remove_block_entity(&self, block_pos: &BlockPos) { + self.remove_block_entity(block_pos).await; + } +}