diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 3daf37683..83d336d92 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -178,7 +178,7 @@ impl Default for BasicConfiguration { scrub_ips: true, use_favicon: true, favicon_path: "icon.png".to_string(), - default_level_name: "World".to_string(), + default_level_name: "world".to_string(), allow_chat_reports: false, white_list: false, enforce_whitelist: false, diff --git a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs b/pumpkin-world/src/block/entities/chiseled_bookshelf.rs index 884a7ac67..7f6183e8c 100644 --- a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs +++ b/pumpkin-world/src/block/entities/chiseled_bookshelf.rs @@ -66,6 +66,14 @@ impl BlockEntity for ChiseledBookshelfBlockEntity { ); } + fn get_inventory(self: Arc) -> Option> { + Some(self) + } + + fn is_dirty(&self) -> bool { + self.dirty.load(Ordering::Relaxed) + } + fn as_any(&self) -> &dyn std::any::Any { self } diff --git a/pumpkin-world/src/block/entities/furnace.rs b/pumpkin-world/src/block/entities/furnace.rs index 0e8c1714a..d9f92b679 100644 --- a/pumpkin-world/src/block/entities/furnace.rs +++ b/pumpkin-world/src/block/entities/furnace.rs @@ -160,7 +160,7 @@ impl FurnaceBlockEntity { #[async_trait] impl BlockEntity for FurnaceBlockEntity { - async fn tick(&self, world: &Arc) { + async fn tick(&self, world: Arc) { let is_burning = self.is_burning(); let mut is_dirty = false; if self.is_burning() { diff --git a/pumpkin-world/src/block/entities/hopper.rs b/pumpkin-world/src/block/entities/hopper.rs index 3682a223f..13da85499 100644 --- a/pumpkin-world/src/block/entities/hopper.rs +++ b/pumpkin-world/src/block/entities/hopper.rs @@ -70,7 +70,7 @@ impl BlockEntity for HopperBlockEntity { hopper } - async fn tick(&self, world: &Arc) { + async fn tick(&self, world: Arc) { self.ticked_game_time.store( world.get_world_age().await, std::sync::atomic::Ordering::Relaxed, @@ -86,7 +86,7 @@ impl BlockEntity for HopperBlockEntity { world.get_block_state(&self.position).await.id, &Block::HOPPER, ); - self.try_move_items(&state, world).await; + self.try_move_items(&state, &world).await; } } diff --git a/pumpkin-world/src/block/entities/mod.rs b/pumpkin-world/src/block/entities/mod.rs index 63a0c32c9..e0b2c450b 100644 --- a/pumpkin-world/src/block/entities/mod.rs +++ b/pumpkin-world/src/block/entities/mod.rs @@ -41,7 +41,7 @@ pub trait BlockEntity: Send + Sync { fn from_nbt(nbt: &NbtCompound, position: BlockPos) -> Self where Self: Sized; - async fn tick(&self, _world: &Arc) {} + async fn tick(&self, _world: Arc) {} fn resource_location(&self) -> &'static str; fn get_position(&self) -> BlockPos; async fn write_internal(&self, nbt: &mut NbtCompound) { @@ -67,6 +67,11 @@ pub trait BlockEntity: Send + Sync { None } fn set_block_state(&mut self, _block_state: BlockStateId) {} + async fn on_block_replaced(self: Arc, world: Arc, position: BlockPos) { + if let Some(inventory) = self.get_inventory() { + world.scatter_inventory(&position, &inventory).await; + } + } fn is_dirty(&self) -> bool { false } diff --git a/pumpkin-world/src/block/entities/piston.rs b/pumpkin-world/src/block/entities/piston.rs index 3695773de..c3494b261 100644 --- a/pumpkin-world/src/block/entities/piston.rs +++ b/pumpkin-world/src/block/entities/piston.rs @@ -60,7 +60,7 @@ impl BlockEntity for PistonBlockEntity { self.position } - async fn tick(&self, world: &Arc) { + async fn tick(&self, world: Arc) { let current_progress = self.current_progress.load(); self.last_progress.store(current_progress); if current_progress >= 1.0 { diff --git a/pumpkin-world/src/world.rs b/pumpkin-world/src/world.rs index 792fd3848..4b1399080 100644 --- a/pumpkin-world/src/world.rs +++ b/pumpkin-world/src/world.rs @@ -1,7 +1,7 @@ use std::sync::Arc; -use crate::BlockStateId; use crate::block::entities::BlockEntity; +use crate::{BlockStateId, inventory::Inventory}; use async_trait::async_trait; use bitflags::bitflags; use pumpkin_data::{Block, BlockDirection, BlockState}; @@ -59,6 +59,13 @@ pub trait SimpleWorld: BlockAccessor + Send + Sync { async fn remove_block_entity(&self, block_pos: &BlockPos); async fn get_block_entity(&self, block_pos: &BlockPos) -> Option>; async fn get_world_age(&self) -> i64; + + /* ItemScatterer */ + async fn scatter_inventory( + self: Arc, + position: &BlockPos, + inventory: &Arc, + ); } #[async_trait] diff --git a/pumpkin/src/block/blocks/barrel.rs b/pumpkin/src/block/blocks/barrel.rs index bd62401ab..c7ae6a590 100644 --- a/pumpkin/src/block/blocks/barrel.rs +++ b/pumpkin/src/block/blocks/barrel.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::block::pumpkin_block::{OnPlaceArgs, OnStateReplacedArgs, PlacedArgs}; +use crate::block::pumpkin_block::{OnPlaceArgs, PlacedArgs}; use crate::block::{ pumpkin_block::{NormalUseArgs, PumpkinBlock}, registry::BlockActionResult, @@ -68,8 +68,4 @@ impl PumpkinBlock for BarrelBlock { .add_block_entity(Arc::new(barrel_block_entity)) .await; } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } } diff --git a/pumpkin/src/block/blocks/chests.rs b/pumpkin/src/block/blocks/chests.rs index b2623a3cd..376be60dd 100644 --- a/pumpkin/src/block/blocks/chests.rs +++ b/pumpkin/src/block/blocks/chests.rs @@ -13,7 +13,7 @@ use pumpkin_world::block::entities::chest::ChestBlockEntity; use pumpkin_world::world::BlockFlags; use crate::block::pumpkin_block::{ - BlockMetadata, BrokenArgs, OnPlaceArgs, OnStateReplacedArgs, PlacedArgs, UseWithItemArgs, + BlockMetadata, BrokenArgs, OnPlaceArgs, PlacedArgs, UseWithItemArgs, }; use crate::entity::EntityBase; use crate::world::World; @@ -88,10 +88,6 @@ impl PumpkinBlock for ChestBlock { } } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } - async fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { BlockActionResult::Consume } diff --git a/pumpkin/src/block/blocks/chiseled_bookshelf.rs b/pumpkin/src/block/blocks/chiseled_bookshelf.rs index e0e5c599a..cc0acd632 100644 --- a/pumpkin/src/block/blocks/chiseled_bookshelf.rs +++ b/pumpkin/src/block/blocks/chiseled_bookshelf.rs @@ -3,8 +3,8 @@ use std::sync::{Arc, atomic::Ordering}; use crate::{ block::{ pumpkin_block::{ - BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, - OnStateReplacedArgs, PlacedArgs, PumpkinBlock, UseWithItemArgs, + BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs, + PumpkinBlock, UseWithItemArgs, }, registry::BlockActionResult, }, @@ -116,10 +116,6 @@ impl PumpkinBlock for ChiseledBookshelfBlock { args.world.add_block_entity(Arc::new(block_entity)).await; } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } - async fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { if let Some(block_entity) = args.world.get_block_entity(args.position).await { if let Some(block_entity) = block_entity diff --git a/pumpkin/src/block/blocks/end_portal.rs b/pumpkin/src/block/blocks/end_portal.rs index a304669b1..81c44fe3a 100644 --- a/pumpkin/src/block/blocks/end_portal.rs +++ b/pumpkin/src/block/blocks/end_portal.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use crate::block::pumpkin_block::OnEntityCollisionArgs; -use crate::block::pumpkin_block::OnStateReplacedArgs; use crate::block::pumpkin_block::PlacedArgs; use crate::block::pumpkin_block::PumpkinBlock; use async_trait::async_trait; @@ -35,8 +34,4 @@ impl PumpkinBlock for EndPortalBlock { .add_block_entity(Arc::new(EndPortalBlockEntity::new(*args.position))) .await; } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } } diff --git a/pumpkin/src/block/blocks/hopper.rs b/pumpkin/src/block/blocks/hopper.rs index b89d4b593..9348f35ad 100644 --- a/pumpkin/src/block/blocks/hopper.rs +++ b/pumpkin/src/block/blocks/hopper.rs @@ -1,9 +1,7 @@ use std::sync::Arc; use crate::block::blocks::redstone::block_receives_redstone_power; -use crate::block::pumpkin_block::{ - OnNeighborUpdateArgs, OnPlaceArgs, OnStateReplacedArgs, PlacedArgs, -}; +use crate::block::pumpkin_block::{OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs}; use crate::block::{ pumpkin_block::{NormalUseArgs, PumpkinBlock}, registry::BlockActionResult, @@ -98,10 +96,6 @@ impl PumpkinBlock for HopperBlock { ) .await; } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } } async fn check_powered_state( diff --git a/pumpkin/src/block/blocks/redstone/dropper.rs b/pumpkin/src/block/blocks/redstone/dropper.rs index 66e04d876..0d5a0ae8b 100644 --- a/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/pumpkin/src/block/blocks/redstone/dropper.rs @@ -1,7 +1,6 @@ use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::pumpkin_block::{ - NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs, - PlacedArgs, PumpkinBlock, + NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, PumpkinBlock, }; use crate::block::registry::BlockActionResult; use crate::entity::Entity; @@ -107,10 +106,6 @@ impl PumpkinBlock for DropperBlock { .await; } - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } - async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { let powered = block_receives_redstone_power(args.world, args.position).await || block_receives_redstone_power(args.world, &args.position.up()).await; diff --git a/pumpkin/src/block/blocks/shulker_box.rs b/pumpkin/src/block/blocks/shulker_box.rs index b14d1ad89..f589384ec 100644 --- a/pumpkin/src/block/blocks/shulker_box.rs +++ b/pumpkin/src/block/blocks/shulker_box.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use crate::block::pumpkin_block::{BlockMetadata, OnPlaceArgs, OnStateReplacedArgs, PlacedArgs}; +use crate::block::pumpkin_block::{BlockMetadata, OnPlaceArgs, PlacedArgs}; use crate::block::{ pumpkin_block::{NormalUseArgs, PumpkinBlock}, registry::BlockActionResult, @@ -79,8 +79,4 @@ impl PumpkinBlock for ShulkerBoxBlock { .add_block_entity(Arc::new(barrel_block_entity)) .await; } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } } diff --git a/pumpkin/src/block/blocks/signs.rs b/pumpkin/src/block/blocks/signs.rs index 42cf9cc82..32ac1bfb6 100644 --- a/pumpkin/src/block/blocks/signs.rs +++ b/pumpkin/src/block/blocks/signs.rs @@ -8,7 +8,6 @@ use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_world::block::entities::sign::SignBlockEntity; use crate::block::pumpkin_block::OnPlaceArgs; -use crate::block::pumpkin_block::OnStateReplacedArgs; use crate::block::pumpkin_block::PlacedArgs; use crate::block::pumpkin_block::PlayerPlacedArgs; use crate::block::pumpkin_block::PumpkinBlock; @@ -40,8 +39,4 @@ impl PumpkinBlock for SignBlock { crate::net::ClientPlatform::Bedrock(_bedrock) => todo!(), } } - - async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) { - args.world.remove_block_entity(args.position).await; - } } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 80b99b8d1..d49d8e935 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -93,16 +93,19 @@ use pumpkin_protocol::{ }, }; use pumpkin_registry::VanillaDimensionType; -use pumpkin_util::math::{position::chunk_section_from_pos, vector2::Vector2}; use pumpkin_util::resource_location::ResourceLocation; use pumpkin_util::text::{TextComponent, color::NamedColor}; use pumpkin_util::{ Difficulty, math::{boundingbox::BoundingBox, position::BlockPos, vector3::Vector3}, }; +use pumpkin_util::{ + math::{position::chunk_section_from_pos, vector2::Vector2}, + random::{RandomImpl, get_seed, xoroshiro128::Xoroshiro}, +}; use pumpkin_world::{ BlockStateId, GENERATION_SETTINGS, GeneratorSetting, biome, block::entities::BlockEntity, - chunk::io::Dirtiable, item::ItemStack, world::SimpleWorld, + chunk::io::Dirtiable, inventory::Inventory, item::ItemStack, world::SimpleWorld, }; use pumpkin_world::{chunk::ChunkData, world::BlockAccessor}; use pumpkin_world::{ @@ -676,7 +679,7 @@ impl World { for block_entity in tick_data.block_entities { let world: Arc = self.clone(); - block_entity.tick(&world).await; + block_entity.tick(world).await; } } @@ -1849,6 +1852,7 @@ impl World { } /// Sets a block and returns the old block id + #[allow(clippy::too_many_lines)] pub async fn set_block_state( self: &Arc, position: &BlockPos, @@ -1884,8 +1888,20 @@ impl World { let block_moved = flags.contains(BlockFlags::MOVED); - // WorldChunk.java line 310 - if old_block != new_block && (flags.contains(BlockFlags::NOTIFY_NEIGHBORS) || block_moved) { + let is_new_block = old_block != new_block; + + // WorldChunk.java line 305-314 + if is_new_block + && old_block.default_state.block_entity_type != u16::MAX + && let Some(entity) = self.get_block_entity(position).await + { + let world: Arc = self.clone(); + entity.on_block_replaced(world, *position).await; + self.remove_block_entity(position).await; + } + + // WorldChunk.java line 317 + if is_new_block && (flags.contains(BlockFlags::NOTIFY_NEIGHBORS) || block_moved) { self.block_registry .on_state_replaced( self, @@ -2073,6 +2089,60 @@ impl World { self.spawn_entity(item_entity).await; } + /* ItemScatterer.java */ + pub async fn scatter_inventory( + self: &Arc, + position: &BlockPos, + inventory: &Arc, + ) { + for i in 0..inventory.size() { + self.scatter_stack( + f64::from(position.0.x), + f64::from(position.0.y), + f64::from(position.0.z), + inventory.remove_stack(i).await, + ) + .await; + } + } + pub async fn scatter_stack(self: &Arc, x: f64, y: f64, z: f64, mut stack: ItemStack) { + const TRIANGULAR_DEVIATION: f64 = 0.114_850_001_711_398_36; + + const XZ_MODE: f64 = 0.0; + const Y_MODE: f64 = 0.2; + + let width = f64::from(EntityType::ITEM.dimension[0]); + let half_width = width / 2.0; + let spawn_area = 1.0 - width; + + let mut rng = Xoroshiro::from_seed(get_seed()); + + // TODO: Use world random here: world.random.nextDouble() + let x = x.floor() + rng.next_f64() * spawn_area + half_width; + let y = y.floor() + rng.next_f64() * spawn_area; + let z = z.floor() + rng.next_f64() * spawn_area + half_width; + + while !stack.is_empty() { + let item = stack.split((rng.next_bounded_i32(21) + 10) as u8); + let velocity = Vector3::new( + rng.next_triangular(XZ_MODE, TRIANGULAR_DEVIATION), + rng.next_triangular(Y_MODE, TRIANGULAR_DEVIATION), + rng.next_triangular(XZ_MODE, TRIANGULAR_DEVIATION), + ); + + let entity = Entity::new( + Uuid::new_v4(), + self.clone(), + Vector3::new(x, y, z), + EntityType::ITEM, + false, + ); + let entity = Arc::new(ItemEntity::new_with_velocity(entity, item, velocity, 10).await); + self.spawn_entity(entity).await; + } + } + /* End ItemScatterer.java */ + pub async fn sync_world_event(&self, world_event: WorldEvent, position: BlockPos, data: i32) { self.broadcast_packet_all(&CWorldEvent::new(world_event as i32, position, data, false)) .await; @@ -2557,6 +2627,14 @@ impl pumpkin_world::world::SimpleWorld for World { async fn get_world_age(&self) -> i64 { self.level_time.lock().await.world_age } + + async fn scatter_inventory( + self: Arc, + position: &BlockPos, + inventory: &Arc, + ) { + Self::scatter_inventory(&self, position, inventory).await; + } } #[async_trait]