mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
feat: Implement item scattering when destroying blocks with inventories (#1078)
* feat: Implement item scatterer * fix: Some issues * fix: Formatting * feat: Remove unused functions * feat: Optimize blockentity check * fix: Formatting * fix: Ups * fix: Remove log * fix: Make clippy happy * fix: Merge conflicts
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -66,6 +66,14 @@ impl BlockEntity for ChiseledBookshelfBlockEntity {
|
||||
);
|
||||
}
|
||||
|
||||
fn get_inventory(self: Arc<Self>) -> Option<Arc<dyn Inventory>> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn is_dirty(&self) -> bool {
|
||||
self.dirty.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ impl FurnaceBlockEntity {
|
||||
|
||||
#[async_trait]
|
||||
impl BlockEntity for FurnaceBlockEntity {
|
||||
async fn tick(&self, world: &Arc<dyn SimpleWorld>) {
|
||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
||||
let is_burning = self.is_burning();
|
||||
let mut is_dirty = false;
|
||||
if self.is_burning() {
|
||||
|
||||
@@ -70,7 +70,7 @@ impl BlockEntity for HopperBlockEntity {
|
||||
hopper
|
||||
}
|
||||
|
||||
async fn tick(&self, world: &Arc<dyn SimpleWorld>) {
|
||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<dyn SimpleWorld>) {}
|
||||
async fn tick(&self, _world: Arc<dyn SimpleWorld>) {}
|
||||
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<Self>, world: Arc<dyn SimpleWorld>, position: BlockPos) {
|
||||
if let Some(inventory) = self.get_inventory() {
|
||||
world.scatter_inventory(&position, &inventory).await;
|
||||
}
|
||||
}
|
||||
fn is_dirty(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ impl BlockEntity for PistonBlockEntity {
|
||||
self.position
|
||||
}
|
||||
|
||||
async fn tick(&self, world: &Arc<dyn SimpleWorld>) {
|
||||
async fn tick(&self, world: Arc<dyn SimpleWorld>) {
|
||||
let current_progress = self.current_progress.load();
|
||||
self.last_progress.store(current_progress);
|
||||
if current_progress >= 1.0 {
|
||||
|
||||
@@ -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<Arc<dyn BlockEntity>>;
|
||||
async fn get_world_age(&self) -> i64;
|
||||
|
||||
/* ItemScatterer */
|
||||
async fn scatter_inventory(
|
||||
self: Arc<Self>,
|
||||
position: &BlockPos,
|
||||
inventory: &Arc<dyn Inventory>,
|
||||
);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<u8> {
|
||||
if let Some(block_entity) = args.world.get_block_entity(args.position).await {
|
||||
if let Some(block_entity) = block_entity
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<dyn SimpleWorld> = 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<Self>,
|
||||
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<dyn SimpleWorld> = 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<Self>,
|
||||
position: &BlockPos,
|
||||
inventory: &Arc<dyn Inventory>,
|
||||
) {
|
||||
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<Self>, 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<Self>,
|
||||
position: &BlockPos,
|
||||
inventory: &Arc<dyn Inventory>,
|
||||
) {
|
||||
Self::scatter_inventory(&self, position, inventory).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user