feat: Sweet Berry Bushes (#1248)

* switch to btreemap to comply with lint

* amplifier i32 -> u8

* switch allow to expect lint

* remove many many unnecessary clippy lints

* dont use expect inside quotes

* beetroot chance 1/3

* prevent sugar cane from growing more than 3 high

* add sweet berry bush

* check for block above

* start nether warts

* nether warts with random ticks

* cargo fmt

* bamboo!

* pass when using bone meal on sweet berry bush with age < 3

* fix first two bamboo not being wide

* use early returns to flatten nesting

* switch damage and damage context to take references instead of arcs

* entity collision damage with sweet berry bush and campfire

* prevent panic and instead just reject placement if y < 0

* fix cactus growing and add cactus flowers

* cargo fmt
This commit is contained in:
Clicks
2026-01-03 13:44:20 +01:00
committed by GitHub
parent 70b3132396
commit 714e970292
28 changed files with 830 additions and 153 deletions

View File

@@ -336,7 +336,9 @@ impl ChunkSections {
block_state_id: BlockStateId,
) -> BlockStateId {
let y = y - self.min_y;
debug_assert!(y >= 0);
if y < 0 {
return Block::AIR.default_state.id;
}
let relative_y = y as usize;
self.set_relative_block(relative_x, relative_y, relative_z, block_state_id)

View File

@@ -529,19 +529,19 @@ impl Level {
chunk_z_base + z_offset,
);
let block_id = chunk
let block_state_id = chunk
.section
.get_block_absolute_y(x_offset as usize, random_pos.0.y, z_offset as usize)
.unwrap_or(Block::AIR.default_state.id);
section_block_data.push((random_pos, block_id));
section_block_data.push((random_pos, block_state_id));
}
section_blocks.push(section_block_data);
}
for section_data in section_blocks {
for (random_pos, block_id) in section_data {
if has_random_ticks(block_id) {
for (random_pos, block_state_id) in section_data {
if has_random_ticks(block_state_id) {
ticks.random_ticks.push(ScheduledTick {
position: random_pos,
delay: 0,

View File

@@ -1,17 +0,0 @@
use pumpkin_data::tag;
use pumpkin_data::tag::Taggable;
use pumpkin_macros::pumpkin_block;
use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs};
#[pumpkin_block("minecraft:bamboo")]
pub struct BambooBlock;
impl BlockBehaviour for BambooBlock {
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
let block_below = args.block_accessor.get_block(&args.position.down()).await;
block_below.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
})
}
}

View File

@@ -1,6 +1,7 @@
use pumpkin_data::{
Block, BlockDirection,
block_properties::{BlockProperties, CampfireLikeProperties},
damage::DamageType,
fluid::Fluid,
};
use pumpkin_world::{BlockStateId, tick::TickPriority};
@@ -32,8 +33,10 @@ impl BlockBehaviour for CampfireBlock {
if CampfireLikeProperties::from_state_id(args.state.id, args.block).lit
&& args.entity.get_living_entity().is_some()
{
// TODO
//args.entity.damage(args.entity, 1.0, DamageType::CAMPFIRE).await;
// FIXME: entity collision code is wrong
args.entity
.damage(args.entity, 1.0, DamageType::CAMPFIRE)
.await;
}
})
}

View File

@@ -1,9 +1,7 @@
pub mod anvil;
pub mod bamboo;
pub mod barrel;
pub mod barrier;
pub mod bed;
pub mod cactus;
pub mod cake;
pub mod campfire;
pub mod candle_cakes;
@@ -48,7 +46,6 @@ pub mod skull_block;
pub mod slabs;
pub mod spawner;
pub mod stairs;
pub mod sugar_cane;
pub mod tnt;
pub mod torches;
pub mod trapdoor;

View File

@@ -0,0 +1,286 @@
use std::sync::Arc;
use pumpkin_data::block_properties::{
BambooLeaves, BambooLikeProperties, BlockProperties, EnumVariants, Integer0To1,
};
use pumpkin_data::item::Item;
use pumpkin_data::tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON;
use pumpkin_data::tag::Taggable;
use pumpkin_data::tag::{self};
use pumpkin_data::{Block, BlockDirection};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::{BlockAccessor, BlockFlags};
use rand::Rng;
use crate::block::registry::BlockActionResult;
use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, blocks::plant::PlantBlockBase};
use crate::block::{
GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs,
UseWithItemArgs,
};
use crate::world::World;
#[pumpkin_block("minecraft:bamboo")]
pub struct BambooBlock;
impl BlockBehaviour for BambooBlock {
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let (block_below, state_id_below) = args
.world
.get_block_and_state_id(&args.position.down())
.await;
if block_below.has_tag(&MINECRAFT_BAMBOO_PLANTABLE_ON) {
let mut props = BambooLikeProperties::from_state_id(
Block::BAMBOO.default_state.id,
&Block::BAMBOO,
);
if block_below == &Block::BAMBOO_SAPLING {
return Block::BAMBOO.default_state.id;
} else if block_below == &Block::BAMBOO {
let props_below =
BambooLikeProperties::from_state_id(state_id_below, block_below);
if props_below.age.to_index() > 0 {
props.age = Integer0To1::L1;
}
} else {
let (block_above, state_id_above) =
args.world.get_block_and_state_id(&args.position.up()).await;
if block_above == &Block::BAMBOO {
let props_above =
BambooLikeProperties::from_state_id(state_id_above, block_above);
props.age = props_above.age;
} else {
return Block::BAMBOO_SAPLING.default_state.id;
}
}
return props.to_state_id(&Block::BAMBOO);
}
Block::AIR.default_state.id
})
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let lock = args.item_stack.lock().await;
if lock.get_item() == &Item::BONE_MEAL {
bone_meal(Arc::clone(args.world), args.position).await;
return BlockActionResult::Success;
}
BlockActionResult::Pass
})
}
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if !<Self as PlantBlockBase>::can_place_at(self, args.world.as_ref(), args.position)
.await
{
args.world
.break_block(args.position, None, BlockFlags::empty())
.await;
} else if args.world.get_block(&args.position.down()).await == &Block::BAMBOO_SAPLING {
args.world
.set_block_state(
&args.position.down(),
Block::BAMBOO.default_state.id,
BlockFlags::empty(),
)
.await;
}
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if !<Self as PlantBlockBase>::can_place_at(self, args.world, args.position).await {
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
let neighbor_block = args.world.get_block(args.neighbor_position).await;
if args.direction == BlockDirection::Up && neighbor_block == &Block::BAMBOO {
let neighbor_props =
BambooLikeProperties::from_state_id(args.neighbor_state_id, neighbor_block);
let mut props = BambooLikeProperties::from_state_id(args.state_id, args.block);
if neighbor_props.age.to_index() > props.age.to_index() {
props.age = match props.age {
Integer0To1::L0 => Integer0To1::L1,
Integer0To1::L1 => Integer0To1::L0,
};
return props.to_state_id(args.block);
}
}
args.state_id
})
}
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if rand::rng().random_range(0..=3) == 0 {
update_leaves_and_grow(args.world.clone(), args.position).await;
}
})
}
}
async fn update_leaves_and_grow(world: Arc<World>, position: &BlockPos) {
let above_pos = position.up();
let below_pos = position.down();
let two_below_pos = position.down_height(2);
let (block, state_id) = world.get_block_and_state_id(position).await;
let state_above = world.get_block_state(&above_pos).await;
if !state_above.is_air() {
return;
}
let mut props = BambooLikeProperties::from_state_id(state_id, block);
if props.stage != Integer0To1::L0 {
return;
}
let bamboo_count = count_bamboo_below(world.clone(), position).await;
if bamboo_count >= 16 {
return;
}
let (block_below, state_id_below) = world.get_block_and_state_id(&below_pos).await;
let (block_two_below, state_id_two_below) = world.get_block_and_state_id(&two_below_pos).await;
let mut props_below = BambooLikeProperties::from_state_id(state_id_below, block_below);
if bamboo_count >= 1 {
let below_is_bamboo = block_below == &Block::BAMBOO;
let below_has_leaves = props_below.leaves != BambooLeaves::None;
props.leaves = if !below_is_bamboo || !below_has_leaves {
BambooLeaves::Small
} else {
BambooLeaves::Large
};
if props.leaves == BambooLeaves::Large && block_two_below == &Block::BAMBOO {
props_below.leaves = BambooLeaves::Small;
let mut props_two_below =
BambooLikeProperties::from_state_id(state_id_two_below, block_two_below);
props_two_below.leaves = BambooLeaves::None;
world
.set_block_state(
&below_pos,
props_below.to_state_id(block_below),
BlockFlags::NOTIFY_ALL,
)
.await;
world
.set_block_state(
&two_below_pos,
props_two_below.to_state_id(block_two_below),
BlockFlags::NOTIFY_ALL,
)
.await;
}
}
props.age = if props.age != Integer0To1::L1 && block_two_below == &Block::BAMBOO {
Integer0To1::L0
} else {
Integer0To1::L1
};
props.stage =
if (bamboo_count < 11 || rand::rng().random::<f32>() >= 0.25) && bamboo_count != 15 {
Integer0To1::L0
} else {
Integer0To1::L1
};
world
.set_block_state(&above_pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
.await;
}
async fn count_bamboo_below(world: Arc<World>, pos: &BlockPos) -> usize {
let mut bamboo_count = 0;
let mut found_bamboo_below = true;
let mut current_position = pos.down();
while found_bamboo_below && bamboo_count < 16 {
found_bamboo_below = false;
if world.get_block(&current_position).await == &Block::BAMBOO {
current_position = current_position.down();
bamboo_count += 1;
found_bamboo_below = true;
}
}
bamboo_count
}
async fn count_bamboo_above(world: Arc<World>, pos: &BlockPos) -> usize {
let mut bamboo_count = 0;
let mut found_bamboo_below = true;
let mut current_position = pos.up();
while found_bamboo_below && bamboo_count < 16 {
found_bamboo_below = false;
if world.get_block(&current_position).await == &Block::BAMBOO {
current_position = current_position.up();
bamboo_count += 1;
found_bamboo_below = true;
}
}
bamboo_count
}
async fn bone_meal(world: Arc<World>, position: &BlockPos) {
let mut bamboo_above = count_bamboo_above(Arc::clone(&world), position).await;
let bamboo_below = count_bamboo_below(Arc::clone(&world), position).await;
let mut new_height = bamboo_above + bamboo_below + 1;
let l = rand::rng().random_range(0..=2) + 1; // what is this?
for _ in 0..l {
let next_pos = position.up_height(bamboo_above as i32);
let next_state = world.get_block_state(&next_pos).await;
if !next_state.is_air() || new_height >= 16 {
return;
}
let next_props = BambooLikeProperties::from_state_id(next_state.id, &Block::BAMBOO);
if next_props.stage == Integer0To1::L1 {
return;
}
update_leaves_and_grow(Arc::clone(&world), position).await;
new_height += 1;
bamboo_above += 1;
}
}
impl PlantBlockBase for BambooBlock {
async fn can_plant_on_top(
&self,
block_accessor: &dyn pumpkin_world::world::BlockAccessor,
pos: &pumpkin_util::math::position::BlockPos,
) -> bool {
let block = block_accessor.get_block(pos).await;
block.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
}
async fn can_place_at(&self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
<Self as PlantBlockBase>::can_plant_on_top(self, block_accessor, &block_pos.down()).await
}
}

View File

@@ -0,0 +1,119 @@
use pumpkin_data::{
Block, BlockDirection,
block_properties::{BambooLeaves, BambooLikeProperties, BlockProperties},
item::Item,
tag::Taggable,
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{
BlockStateId,
world::{BlockAccessor, BlockFlags},
};
use rand::Rng;
use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
OnNeighborUpdateArgs, UseWithItemArgs, blocks::plant::PlantBlockBase,
registry::BlockActionResult,
};
#[pumpkin_block("minecraft:bamboo_sapling")]
pub struct BambooSaplingBlock;
impl BlockBehaviour for BambooSaplingBlock {
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, &args.position.down())
.await
})
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let lock = args.item_stack.lock().await;
if lock.get_item() == &Item::BONE_MEAL {
let mut props_new = BambooLikeProperties::from_state_id(
Block::BAMBOO.default_state.id,
&Block::BAMBOO,
);
props_new.leaves = BambooLeaves::Small;
args.world
.set_block_state(
&args.position.up(),
props_new.to_state_id(&Block::BAMBOO),
BlockFlags::NOTIFY_ALL,
)
.await;
return BlockActionResult::Success;
}
BlockActionResult::Pass
})
}
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if args.block == &Block::BAMBOO_SAPLING
&& args.world.get_block(&args.position.up()).await == &Block::BAMBOO
{
args.world
.set_block_state(
args.position,
Block::BAMBOO.default_state.id,
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if !<Self as PlantBlockBase>::can_place_at(self, args.world, args.position).await {
return Block::AIR.default_state.id;
}
if args.direction == BlockDirection::Up
&& args.world.get_block(args.neighbor_position).await == &Block::BAMBOO
{
return Block::BAMBOO.default_state.id;
}
args.state_id
})
}
fn random_tick<'a>(&'a self, args: crate::block::RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let state_above = args.world.get_block_state(&args.position.up()).await;
if !state_above.is_air() || rand::rng().random_range(0..3) > 0 {
return;
}
let mut props_new =
BambooLikeProperties::from_state_id(Block::BAMBOO.default_state.id, &Block::BAMBOO);
props_new.leaves = BambooLeaves::Small;
args.world
.set_block_state(
&args.position.up(),
props_new.to_state_id(&Block::BAMBOO),
BlockFlags::NOTIFY_ALL,
)
.await;
})
}
}
impl PlantBlockBase for BambooSaplingBlock {
async fn can_place_at(&self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
<Self as PlantBlockBase>::can_plant_on_top(self, block_accessor, &block_pos.down()).await
}
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
let block = block_accessor.get_block(pos).await;
block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
}
}

View File

@@ -1,6 +1,7 @@
use pumpkin_data::block_properties::{
BlockProperties, CactusLikeProperties, EnumVariants, Integer0To15,
};
use pumpkin_data::damage::DamageType;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection, tag};
use pumpkin_macros::pumpkin_block;
@@ -8,10 +9,11 @@ use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::{BlockAccessor, BlockFlags};
use rand::Rng;
use crate::block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs,
OnScheduledTickArgs, RandomTickArgs,
OnEntityCollisionArgs, OnScheduledTickArgs, RandomTickArgs,
};
#[pumpkin_block("minecraft:cactus")]
@@ -30,38 +32,58 @@ impl BlockBehaviour for CactusBlock {
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if args
.world
.get_block_state(&args.position.up())
.await
.is_air()
{
let state_id = args.world.get_block_state(args.position).await.id;
let age = CactusLikeProperties::from_state_id(state_id, args.block).age;
if age == Integer0To15::L15 {
let block_up = args.position.up();
if args.world.get_block_state(&block_up).await.is_air() {
let state = args.world.get_block_state(args.position).await;
let mut props = CactusLikeProperties::from_state_id(state.id, args.block);
let age = props.age;
let mut i = 1;
while args.world.get_block(&args.position.down_height(i)).await == &Block::CACTUS {
i += 1;
if 1 == 3 && age == Integer0To15::L15 {
return;
}
}
if age == Integer0To15::L8 && can_place_at(args.world.as_ref(), &block_up).await {
let d = if i >= 3 { 0.25 } else { 0.1 };
if rand::rng().random_range(0.0..1.0) <= d {
args.world
.set_block_state(
&block_up,
Block::CACTUS_FLOWER.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
}
} else if age == Integer0To15::L15 && i < 3 {
args.world
.set_block_state(
&args.position.up(),
&block_up,
Block::CACTUS.default_state.id,
BlockFlags::empty(),
BlockFlags::NOTIFY_ALL,
)
.await;
let mut new_props = CactusLikeProperties::default(&Block::CACTUS);
new_props.age = Integer0To15::L0;
args.world
.set_block_state(
args.position,
Block::CACTUS.default_state.id,
BlockFlags::empty(),
new_props.to_state_id(&Block::CACTUS),
BlockFlags::SKIP_BLOCK_ENTITY_REPLACED_CALLBACK,
)
.await;
} else {
let props = CactusLikeProperties {
age: Integer0To15::from_index(age.to_index() + 1),
};
args.world
.update_neighbor(args.position, &Block::CACTUS)
.await;
}
if age.to_index() < 15 {
props.age = Integer0To15::from_index(age.to_index() + 1);
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::empty(),
props.to_state_id(&Block::CACTUS),
BlockFlags::SKIP_BLOCK_ENTITY_REPLACED_CALLBACK,
)
.await;
}
@@ -69,10 +91,14 @@ impl BlockBehaviour for CactusBlock {
})
}
// async fn on_entity_collision(&self, _args: OnEntityCollisionArgs<'_>) {
// // TODO
// //args.entity.damage(1.0, DamageType::CACTUS).await;
// }
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
log::warn!("CactusBlock::on_entity_collision");
Box::pin(async move {
args.entity
.damage(args.entity, 1.0, DamageType::CACTUS)
.await;
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
@@ -95,8 +121,6 @@ impl BlockBehaviour for CactusBlock {
}
async fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
// TODO: use tags
// Disallow to place any blocks nearby a cactus
for direction in BlockDirection::horizontal() {
let (block, state) = world
.get_block_and_state(&block_pos.offset(direction.to_offset()))
@@ -106,7 +130,6 @@ async fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
}
}
let block = world.get_block(&block_pos.down()).await;
// TODO: use tags
(block == &Block::CACTUS || block.has_tag(&tag::Block::MINECRAFT_SAND))
&& !world.get_block_state(&block_pos.up()).await.is_liquid()
}

View File

@@ -46,7 +46,7 @@ impl BlockBehaviour for BeetrootBlock {
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if rand::rng().random_range(0..2) != 0 {
if rand::rng().random_range(0..3) == 0 {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
}
})

View File

@@ -22,7 +22,9 @@ type FarmlandProperties = FarmlandLikeProperties;
pub mod beetroot;
pub mod carrot;
pub mod gourds;
pub mod nether_wart;
pub mod potatoes;
pub mod sweet_berry_bush;
pub mod torch_flower;
pub mod wheat;

View File

@@ -0,0 +1,99 @@
use std::sync::Arc;
use pumpkin_data::{
Block,
block_properties::{BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{
BlockStateId,
world::{BlockAccessor, BlockFlags},
};
use rand::Rng;
use crate::{
block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs,
blocks::plant::{PlantBlockBase, crop::CropBlockBase},
},
world::World,
};
#[pumpkin_block("minecraft:nether_wart")]
pub struct NetherWartBlock;
impl BlockBehaviour for NetherWartBlock {
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
})
}
}
impl PlantBlockBase for NetherWartBlock {
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
let block = block_accessor.get_block(pos).await;
block == &Block::SOUL_SAND
}
async fn can_place_at(&self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
<Self as PlantBlockBase>::can_plant_on_top(self, block_accessor, &block_pos.down()).await
}
}
impl CropBlockBase for NetherWartBlock {
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
<Self as PlantBlockBase>::can_plant_on_top(self, block_accessor, pos).await
}
fn max_age(&self) -> i32 {
3
}
fn get_age(&self, state: u16, block: &Block) -> i32 {
let props = NetherWartLikeProperties::from_state_id(state, block);
i32::from(props.age.to_index())
}
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
let mut props = NetherWartLikeProperties::from_state_id(state, block);
props.age = Integer0To3::from_index(age as u16);
props.to_state_id(block)
}
async fn random_tick(&self, world: &Arc<World>, pos: &BlockPos) {
let (block, state) = world.get_block_and_state_id(pos).await;
let age = self.get_age(state, block);
if age < self.max_age() && rand::rng().random_range(0..=10) == 0 {
world
.set_block_state(
pos,
self.state_with_age(block, state, age + 1),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
}
}

View File

@@ -0,0 +1,203 @@
use std::sync::Arc;
use pumpkin_data::{
Block,
block_properties::{BlockProperties, EnumVariants, Integer0To3, NetherWartLikeProperties},
damage::DamageType,
entity::EntityType,
item::Item,
tag::{self, Taggable},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{
BlockStateId,
item::ItemStack,
world::{BlockAccessor, BlockFlags},
};
use rand::Rng;
use crate::{
block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs,
OnEntityCollisionArgs, RandomTickArgs, UseWithItemArgs,
blocks::plant::{PlantBlockBase, crop::CropBlockBase},
registry::BlockActionResult,
},
world::World,
};
#[pumpkin_block("minecraft:sweet_berry_bush")]
pub struct SweetBerryBushBlock;
impl BlockBehaviour for SweetBerryBushBlock {
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let mut props = NetherWartLikeProperties::from_state_id(state_id, args.block);
match props.age {
Integer0To3::L2 | Integer0To3::L3 => {
let index = props.age.to_index() as u8;
props.age = Integer0To3::L1;
let count: u8 = rand::rng().random_range((index - 1)..=(index));
for _ in 0..count {
args.world
.drop_stack(
args.position,
ItemStack::new(1, &Item::SWEET_BERRIES), //
)
.await;
}
args.world
.set_block_state(
args.position,
props.to_state_id(&Block::SWEET_BERRY_BUSH),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::SuccessServer
}
_ => BlockActionResult::Pass,
}
})
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position).await;
let props = NetherWartLikeProperties::from_state_id(state_id, &Block::SWEET_BERRY_BUSH);
if props.age != Integer0To3::L3
&& args.item_stack.lock().await.get_item() == &Item::BONE_MEAL
{
BlockActionResult::Pass
} else {
BlockActionResult::PassToDefaultBlockAction
}
})
}
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
fn on_entity_collision<'a>(&'a self, args: OnEntityCollisionArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let entity = args.entity.get_entity();
if entity.entity_type == &EntityType::FOX || entity.entity_type == &EntityType::BEE {
return;
}
let state_id = args.world.get_block_state_id(args.position).await;
let props = NetherWartLikeProperties::from_state_id(state_id, args.block);
if props.age == Integer0To3::L0 {
return;
}
let velocity = entity.velocity.load(); // FIXME: velocity != momentum/movement
if velocity.horizontal_length_squared() <= 0.0
|| (velocity.x.abs() < 0.003 && velocity.z.abs() < 0.003)
{
return;
}
args.entity
.damage(args.entity, 1.0, DamageType::SWEET_BERRY_BUSH)
.await;
})
}
fn random_tick<'a>(&'a self, args: RandomTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if rand::rng().random_range(0..5) == 0 {
<Self as CropBlockBase>::random_tick(self, args.world, args.position).await;
}
})
}
}
impl PlantBlockBase for SweetBerryBushBlock {
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
let block = block_accessor.get_block(pos).await;
block.has_tag(&tag::Block::MINECRAFT_DIRT)
}
async fn get_state_for_neighbor_update(
&self,
block_accessor: &dyn BlockAccessor,
block_pos: &BlockPos,
block_state: BlockStateId,
) -> BlockStateId {
if !<Self as PlantBlockBase>::can_place_at(self, block_accessor, block_pos).await {
return Block::AIR.default_state.id;
}
block_state
}
async fn can_place_at(&self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
<Self as PlantBlockBase>::can_plant_on_top(self, block_accessor, &block_pos.down()).await
}
}
impl CropBlockBase for SweetBerryBushBlock {
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
<Self as PlantBlockBase>::can_plant_on_top(self, block_accessor, pos).await
}
fn max_age(&self) -> i32 {
3
}
fn get_age(&self, state: u16, block: &Block) -> i32 {
let props = NetherWartLikeProperties::from_state_id(state, block);
i32::from(props.age.to_index())
}
fn state_with_age(&self, block: &Block, state: u16, age: i32) -> BlockStateId {
let mut props = NetherWartLikeProperties::from_state_id(state, block);
props.age = Integer0To3::from_index(age as u16);
props.to_state_id(block)
}
async fn random_tick(&self, world: &Arc<World>, pos: &BlockPos) {
let (block, state) = world.get_block_and_state_id(pos).await;
let age = self.get_age(state, block);
if age < self.max_age() {
let state_above = world.get_block_state(&pos.up()).await;
if state_above.is_full_cube() || state_above.is_solid() {
return;
}
if rand::rng().random_range(0..=25) == 0 {
world
.set_block_state(
pos,
self.state_with_age(block, state, age + 1),
BlockFlags::NOTIFY_NEIGHBORS,
)
.await;
}
}
}
}

View File

@@ -2,7 +2,10 @@ use pumpkin_data::{Block, tag, tag::Taggable};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, world::BlockAccessor};
pub mod bamboo;
pub mod bamboo_sapling;
pub mod bush;
pub mod cactus;
pub mod crop;
pub mod dry_vegetation;
pub mod flower;
@@ -10,13 +13,13 @@ pub mod flowerbed;
pub mod leaf_litter;
pub mod lily_pad;
pub mod mushroom_plant;
pub mod nether_wart;
pub mod roots;
pub mod sapling;
pub mod sea_grass;
pub mod sea_pickles;
pub mod segmented;
pub mod short_plant;
pub mod sugar_cane;
pub mod tall_plant;
trait PlantBlockBase {

View File

@@ -1,41 +0,0 @@
use pumpkin_data::Block;
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::BlockStateId;
use pumpkin_world::world::BlockAccessor;
use crate::block::blocks::plant::PlantBlockBase;
use crate::block::{BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetStateForNeighborUpdateArgs};
#[pumpkin_block("minecraft:nether_wart")]
pub struct NetherWartBlock;
impl BlockBehaviour for NetherWartBlock {
fn can_place_at<'a>(&'a self, args: CanPlaceAtArgs<'a>) -> BlockFuture<'a, bool> {
Box::pin(async move {
<Self as PlantBlockBase>::can_place_at(self, args.block_accessor, args.position).await
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
<Self as PlantBlockBase>::get_state_for_neighbor_update(
self,
args.world,
args.position,
args.state_id,
)
.await
})
}
}
impl PlantBlockBase for NetherWartBlock {
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
let block = block_accessor.get_block(pos).await;
block == &Block::SOUL_SAND
}
}

View File

@@ -37,6 +37,9 @@ impl BlockBehaviour for SugarCaneBlock {
.get_block_state(&args.position.up())
.await
.is_air()
&& !(args.world.get_block(&args.position.down()).await == &Block::SUGAR_CANE
&& args.world.get_block(&args.position.down().down()).await
== &Block::SUGAR_CANE)
{
let state_id = args.world.get_block_state(args.position).await.id;
let age = CactusLikeProperties::from_state_id(state_id, args.block).age;

View File

@@ -1,9 +1,8 @@
use crate::block::blocks::anvil::AnvilBlock;
use crate::block::blocks::bamboo::BambooBlock;
use crate::block::blocks::barrel::BarrelBlock;
use crate::block::blocks::barrier::BarrierBlock;
use crate::block::blocks::bed::BedBlock;
use crate::block::blocks::cactus::CactusBlock;
use crate::block::blocks::carpet::{CarpetBlock, MossCarpetBlock, PaleMossCarpetBlock};
use crate::block::blocks::carved_pumpkin::CarvedPumpkinBlock;
use crate::block::blocks::chests::ChestBlock;
@@ -30,7 +29,12 @@ use crate::block::blocks::note::NoteBlock;
use crate::block::blocks::piston::piston::PistonBlock;
use crate::block::blocks::piston::piston_extension::PistonExtensionBlock;
use crate::block::blocks::piston::piston_head::PistonHeadBlock;
use crate::block::blocks::plant::bamboo::BambooBlock;
use crate::block::blocks::plant::bamboo_sapling::BambooSaplingBlock;
use crate::block::blocks::plant::bush::BushBlock;
use crate::block::blocks::plant::cactus::CactusBlock;
use crate::block::blocks::plant::crop::nether_wart::NetherWartBlock;
use crate::block::blocks::plant::crop::sweet_berry_bush::SweetBerryBushBlock;
use crate::block::blocks::plant::dry_vegetation::DryVegetationBlock;
use crate::block::blocks::plant::flower::FlowerBlock;
use crate::block::blocks::plant::flowerbed::FlowerbedBlock;
@@ -39,6 +43,7 @@ use crate::block::blocks::plant::lily_pad::LilyPadBlock;
use crate::block::blocks::plant::mushroom_plant::MushroomPlantBlock;
use crate::block::blocks::plant::sapling::SaplingBlock;
use crate::block::blocks::plant::short_plant::ShortPlantBlock;
use crate::block::blocks::plant::sugar_cane::SugarCaneBlock;
use crate::block::blocks::plant::tall_plant::TallPlantBlock;
use crate::block::blocks::pumpkin::PumpkinBlock;
use crate::block::blocks::redstone::buttons::ButtonBlock;
@@ -64,7 +69,6 @@ use crate::block::blocks::signs::SignBlock;
use crate::block::blocks::slabs::SlabBlock;
use crate::block::blocks::spawner::SpawnerBlock;
use crate::block::blocks::stairs::StairBlock;
use crate::block::blocks::sugar_cane::SugarCaneBlock;
use crate::block::blocks::tnt::TNTBlock;
use crate::block::blocks::torches::TorchBlock;
use crate::block::blocks::trapdoor::TrapDoorBlock;
@@ -103,7 +107,6 @@ use crate::block::blocks::plant::crop::carrot::CarrotBlock;
use crate::block::blocks::plant::crop::potatoes::PotatoBlock;
use crate::block::blocks::plant::crop::torch_flower::TorchFlowerBlock;
use crate::block::blocks::plant::crop::wheat::WheatBlock;
use crate::block::blocks::plant::nether_wart::NetherWartBlock;
use crate::block::blocks::plant::roots::RootsBlock;
use crate::block::blocks::plant::sea_grass::SeaGrassBlock;
use crate::block::blocks::plant::sea_pickles::SeaPickleBlock;
@@ -168,6 +171,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(JukeboxBlock);
manager.register(LogBlock);
manager.register(BambooBlock);
manager.register(BambooSaplingBlock);
manager.register(BannerBlock);
manager.register(SignBlock);
manager.register(SlabBlock);
@@ -184,6 +188,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(BeetrootBlock);
manager.register(TorchFlowerBlock);
manager.register(CarrotBlock);
manager.register(SweetBerryBushBlock);
manager.register(SeaGrassBlock);
manager.register(NetherWartBlock);
manager.register(WheatBlock);

View File

@@ -89,14 +89,7 @@ impl CommandExecutor for LocationExecutor {
let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?;
let success = target
.damage_with_context(
target.clone(),
amount,
damage_type,
Some(location),
None,
None,
)
.damage_with_context(&*target, amount, damage_type, Some(location), None, None)
.await;
send_damage_result(sender, success, amount, target.get_display_name().await).await;
@@ -143,7 +136,7 @@ impl CommandExecutor for EntityExecutor {
let success = target
.damage_with_context(
target.clone(),
&*target,
amount,
damage_type,
None,

View File

@@ -6,6 +6,7 @@ use crate::command::tree::CommandTree;
use crate::command::tree::builder::{argument, require};
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
use crate::entity::EntityBase;
use crate::server::Server;
use CommandError::InvalidConsumption;
const NAMES: [&str; 1] = ["kill"];
@@ -19,7 +20,7 @@ impl CommandExecutor for Executor {
fn execute<'a>(
&'a self,
sender: &'a CommandSender,
_server: &'a crate::server::Server,
_server: &'a Server,
args: &'a ConsumedArgs<'a>,
) -> CommandResult<'a> {
Box::pin(async move {
@@ -29,7 +30,7 @@ impl CommandExecutor for Executor {
let target_count = targets.len();
for target in targets {
target.kill(target.clone()).await;
target.kill(&**target).await;
}
let msg = if target_count == 1 {
@@ -57,12 +58,12 @@ impl CommandExecutor for SelfExecutor {
fn execute<'a>(
&'a self,
sender: &'a CommandSender,
_server: &'a crate::server::Server,
_server: &'a Server,
_args: &'a ConsumedArgs<'a>,
) -> CommandResult<'a> {
Box::pin(async move {
let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?;
target.kill(target.clone()).await;
target.kill(&*target).await;
sender
.send_message(TextComponent::translate(

View File

@@ -1,7 +1,4 @@
use std::sync::{
Arc,
atomic::{AtomicI32, AtomicI64, AtomicU8, Ordering},
};
use std::sync::atomic::{AtomicI32, AtomicI64, AtomicU8, Ordering};
use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, living::LivingEntity,
@@ -300,7 +297,7 @@ impl EntityBase for ArmorStandEntity {
fn damage_with_context<'a>(
&'a self,
caller: Arc<dyn EntityBase>,
caller: &'a dyn EntityBase,
_amount: f32,
damage_type: DamageType,
_position: Option<Vector3<f64>>,

View File

@@ -1,5 +1,4 @@
use core::f32;
use std::sync::Arc;
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage, living::LivingEntity};
use pumpkin_data::damage::DamageType;
@@ -37,7 +36,7 @@ impl EntityBase for EndCrystalEntity {
fn damage_with_context<'a>(
&'a self,
_caller: Arc<dyn EntityBase>,
_caller: &'a dyn EntityBase,
_amount: f32,
damage_type: DamageType,
_position: Option<Vector3<f64>>,

View File

@@ -1,5 +1,5 @@
use core::f32;
use std::sync::{Arc, atomic::Ordering};
use std::sync::atomic::Ordering;
use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, living::LivingEntity,
@@ -44,7 +44,7 @@ impl EntityBase for PaintingEntity {
fn damage_with_context<'a>(
&'a self,
_caller: Arc<dyn EntityBase>,
_caller: &'a dyn EntityBase,
_amount: f32,
_damage_type: DamageType,
_position: Option<Vector3<f64>>,

View File

@@ -71,7 +71,7 @@ impl HungerManager {
|| (difficulty == Difficulty::Hard)
|| (health > 1.0 && difficulty == Difficulty::Normal)
{
player.damage(player.clone(), 1.0, DamageType::STARVE).await;
player.damage(&**player, 1.0, DamageType::STARVE).await;
}
self.tick_timer.store(0);
}

View File

@@ -359,7 +359,7 @@ impl EntityBase for ItemEntity {
fn damage_with_context<'a>(
&'a self,
_caller: Arc<dyn EntityBase>,
_caller: &'a dyn EntityBase,
amount: f32,
_damage_type: DamageType,
_position: Option<Vector3<f64>>,
@@ -376,12 +376,12 @@ impl EntityBase for ItemEntity {
})
}
fn damage(
&self,
_caller: Arc<dyn EntityBase>,
fn damage<'a>(
&'a self,
_caller: &'a dyn EntityBase,
_amount: f32,
_damage_type: DamageType,
) -> EntityBaseFuture<'_, bool> {
) -> EntityBaseFuture<'a, bool> {
Box::pin(async { false })
}

View File

@@ -332,7 +332,7 @@ impl LivingEntity {
let suffocating = self.entity.tick_block_collisions(&caller, server).await;
if suffocating {
self.damage(caller, 1.0, DamageType::IN_WALL).await;
self.damage(&*caller, 1.0, DamageType::IN_WALL).await;
}
}
@@ -697,7 +697,7 @@ impl LivingEntity {
// TODO: Play block fall sound
if damage > 0.0 {
let check_damage = self.damage(caller, damage, DamageType::FALL).await; // Fall
let check_damage = self.damage(&*caller, damage, DamageType::FALL).await; // Fall
if check_damage {
self.entity
.play_sound(Self::get_fall_sound(fall_distance as i32))
@@ -840,7 +840,7 @@ impl LivingEntity {
}
}
async fn try_use_death_protector(&self, caller: &Arc<dyn EntityBase>) -> bool {
async fn try_use_death_protector(&self, caller: &dyn EntityBase) -> bool {
for hand in Hand::all() {
let stack = self.get_stack_in_hand(caller, hand).await;
let mut stack = stack.lock().await;
@@ -859,7 +859,7 @@ impl LivingEntity {
false
}
pub async fn held_item(&self, caller: &Arc<dyn EntityBase>) -> Arc<Mutex<ItemStack>> {
pub async fn held_item(&self, caller: &dyn EntityBase) -> Arc<Mutex<ItemStack>> {
if let Some(player) = caller.get_player() {
return player.inventory.held_item();
}
@@ -873,7 +873,7 @@ impl LivingEntity {
pub async fn get_stack_in_hand(
&self,
caller: &Arc<dyn EntityBase>,
caller: &dyn EntityBase,
hand: Hand,
) -> Arc<Mutex<ItemStack>> {
match hand {
@@ -962,7 +962,7 @@ impl NBTStorage for LivingEntity {
impl EntityBase for LivingEntity {
fn damage_with_context<'a>(
&'a self,
caller: Arc<dyn EntityBase>,
caller: &'a dyn EntityBase,
amount: f32,
damage_type: DamageType,
position: Option<Vector3<f64>>,
@@ -1046,7 +1046,7 @@ impl EntityBase for LivingEntity {
self.set_health(new_health).await;
}
if new_health <= 0.0 && !self.try_use_death_protector(&caller).await {
if new_health <= 0.0 && !self.try_use_death_protector(caller).await {
self.on_death(damage_type, source, cause).await;
}

View File

@@ -126,7 +126,7 @@ impl<T: Mob + Send + 'static> EntityBase for T {
fn damage_with_context<'a>(
&'a self,
caller: Arc<dyn EntityBase>,
caller: &'a dyn EntityBase,
amount: f32,
damage_type: DamageType,
position: Option<Vector3<f64>>,

View File

@@ -126,12 +126,12 @@ pub trait EntityBase: Send + Sync + NBTStorage {
}
/// Returns if damage was successful or not
fn damage(
&self,
caller: Arc<dyn EntityBase>,
fn damage<'a>(
&'a self,
caller: &'a dyn EntityBase,
amount: f32,
damage_type: DamageType,
) -> EntityBaseFuture<'_, bool> {
) -> EntityBaseFuture<'a, bool> {
Box::pin(async move {
self.damage_with_context(caller, amount, damage_type, None, None, None)
.await
@@ -156,7 +156,7 @@ pub trait EntityBase: Send + Sync + NBTStorage {
fn damage_with_context<'a>(
&'a self,
_caller: Arc<dyn EntityBase>,
_caller: &'a dyn EntityBase,
_amount: f32,
_damage_type: DamageType,
_position: Option<Vector3<f64>>,
@@ -220,7 +220,7 @@ pub trait EntityBase: Send + Sync + NBTStorage {
}
/// Kills the Entity.
fn kill(&self, caller: Arc<dyn EntityBase>) -> EntityBaseFuture<'_, ()> {
fn kill<'a>(&'a self, caller: &'a dyn EntityBase) -> EntityBaseFuture<'a, ()> {
Box::pin(async move {
if let Some(living) = self.get_living_entity() {
living
@@ -1761,11 +1761,11 @@ impl Entity {
vehicle.is_some()
}
pub async fn check_out_of_world(&self, dyn_self: Arc<dyn EntityBase>) {
pub async fn check_out_of_world(&self, dyn_self: &dyn EntityBase) {
if self.pos.load().y < f64::from(self.world.generation_settings().shape.min_y) - 64.0 {
// Tick out of world damage
dyn_self
.damage(dyn_self.clone(), 4.0, DamageType::OUT_OF_WORLD)
.damage(dyn_self, 4.0, DamageType::OUT_OF_WORLD)
.await;
}
}
@@ -1871,7 +1871,7 @@ impl EntityBase for Entity {
Box::pin(async move {
self.tick_portal(&caller).await;
self.update_fluid_state(&caller).await;
self.check_out_of_world(caller.clone()).await;
self.check_out_of_world(&*caller).await;
let fire_ticks = self.fire_ticks.load(Ordering::Relaxed);
if fire_ticks > 0 {
if self.entity_type.fire_immune {
@@ -1881,9 +1881,7 @@ impl EntityBase for Entity {
}
} else {
if fire_ticks % 20 == 0 {
caller
.damage(caller.clone(), 1.0, DamageType::ON_FIRE)
.await;
caller.damage(&*caller, 1.0, DamageType::ON_FIRE).await;
}
self.fire_ticks.store(fire_ticks - 1, Ordering::Relaxed);

View File

@@ -621,7 +621,7 @@ impl Player {
if !victim
.damage_with_context(
victim.clone(),
&*victim,
damage as f32,
DamageType::PLAYER_ATTACK,
None,
@@ -2313,7 +2313,7 @@ impl NBTStorageInit for EnderChestInventory {}
impl EntityBase for Player {
fn damage_with_context<'a>(
&'a self,
caller: Arc<dyn EntityBase>,
caller: &'a dyn EntityBase,
amount: f32,
damage_type: DamageType,
position: Option<Vector3<f64>>,

View File

@@ -14,6 +14,7 @@ pub mod loot;
pub mod portal;
pub mod time;
use crate::block::RandomTickArgs;
use crate::world::loot::LootContextParameters;
use crate::{
PLUGIN_MANAGER,
@@ -712,7 +713,8 @@ impl World {
}
}
/* TODO: Fix this deadlock
// TODO: Fix this deadlock
// TODO: ^ find this deadlock ^
for scheduled_tick in tick_data.random_ticks {
let block = self.get_block(&scheduled_tick.position).await;
if let Some(pumpkin_block) = self.block_registry.get_pumpkin_block(block) {
@@ -724,7 +726,7 @@ impl World {
})
.await;
}
} */
}
let spawn_entity_clock_start = tokio::time::Instant::now();